Flaga lokalna sed, która działa zarówno na komputerach Mac (BSD), jak i Linux


237

Czy istnieje wywołanie sededycji miejscowej todo bez kopii zapasowych, która działa zarówno w systemie Linux, jak i Mac? Podczas gdy BSD seddostarczany z OS X wydaje się być potrzebny sed -i '' …, seddystrybucje GNU Linux zwykle zawierają interpretację cytatów jako pustą nazwę pliku wejściowego (zamiast rozszerzenia kopii zapasowej) i sed -i …zamiast tego potrzebuje .

Czy jest jakaś składnia wiersza poleceń, która działa z oboma smakami, więc mogę używać tego samego skryptu w obu systemach?


Czy Perl nie jest opcją? Musisz użyć sed?
Noufal Ibrahim,

Może zainstaluj wersję GNU i skorzystaj z niej! topbug.net/blog/2013/04/14/… ? Najpierw spróbuję. Z drugiej strony to też jest do bani.
Dmitry Minkovsky,

2
@dimadima: Może być interesujące dla niektórych osób przeglądających to pytanie, które mają osobiste skrypty, które psują się na komputerze z systemem OS X. W moim przypadku jednak potrzebowałem go w systemie kompilacji projektu open source, w którym powiedzenie użytkownikowi, aby najpierw instalował GNU sed, zniszczyłoby pierwotny cel tego ćwiczenia (załatanie kilku plików w sposób „działa wszędzie”) .
dnadlinger

@klickverbot tak, ma sens. Najpierw dodałem komentarz jako odpowiedź, a następnie go usunąłem, zdając sobie sprawę, że to nie była odpowiedź na twoje pytanie :).
Dmitry Minkovsky,

1
Odsyłacz: Jak osiągnąć przenośność za pomocą sed -i (edycja na miejscu)? na giełdzie stosów Unix i Linux.
MvG,

Odpowiedzi:


212

Jeśli naprawdę chcesz po prostu użyć sed -i„łatwego” sposobu, poniższe DOES działają zarówno na GNU, jak i BSD / Mac sed:

sed -i.bak 's/foo/bar/' filename

Zwróć uwagę na brak miejsca i kropkę.

Dowód:

# GNU sed
% sed --version | head -1
GNU sed version 4.2.1
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

# BSD sed
% sed --version 2>&1 | head -1
sed: illegal option -- -
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

Oczywiście możesz po prostu usunąć .bakpliki.


2
To jest odpowiednie rozwiązanie. Dodaj kilka znaków, a będzie przenośny. Usunięcie pliku kopii zapasowej jest banalne (nazwa pliku jest już dostępna podczas wywoływania sed)
slezica

Działa to na Macu, ale w Ubuntu 16.04 nadpisuje oryginalny plik na 0 bajtów i tworzy plik .bak również z 0 bajtami?
house9

1
Warto zauważyć, że na MacOS Sierra (i ewentualnie wcześniej, nie mam pod ręką maszynę), sednie akceptuje pozycyjną commandargumentu przy wywołaniu z -i- to musi być dostarczony z inną flagą -e. Zatem polecenie staje się:sed -i.bak -e 's/foo/bar/' filename
ELLIOTTCABLE

5
Tworzy to kopie zapasowe, podczas gdy OP prosi o edycję w miejscu.
Marc-André Lafortune,

8
Tak więc pełne rozwiązanie to:sed -i.bak 's/foo/bar/' file && rm file.bak
joeytwiddle

112

Działa to z GNU sed, ale nie w OS X:

sed -i -e 's/foo/bar/' target.file
sed -i'' -e 's/foo/bar/' target.file

Działa to w OS X, ale nie w GNU sed:

sed -i '' -e 's/foo/bar/' target.file

W systemie OS X ty

  • nie można użyć, sed -i -eponieważ rozszerzenie pliku kopii zapasowej byłoby ustawione na-e
  • nie można używać sed -i'' -ez tych samych powodów - potrzebuje odstępu między -ii ''.

1
@AlexanderMills Informuje sed, że następny argument jest poleceniem - można go również pominąć.
Benjamin W.

4
Zauważ, że -ii -i''są identyczne w czasie analizy powłoki; sed nie może zachowywać się inaczej w przypadku tych dwóch pierwszych wywołań, ponieważ otrzymuje dokładnie te same argumenty.
wchargin

44

W systemie OSX zawsze instaluję wersję GNU sed przez Homebrew, aby uniknąć problemów ze skryptami, ponieważ większość skryptów została napisana dla wersji GNU sed.

brew install gnu-sed --with-default-names

Wtedy twój BSD sed zostanie zastąpiony przez GNU sed.

Alternatywnie możesz zainstalować bez domyślnych nazw, ale następnie:

  • Zmień PATHzgodnie z instrukcją po instalacjignu-sed
  • Sprawdź swoje skrypty, aby wybrać pomiędzy gsedlub w sedzależności od systemu

4
--with-default-nameszostał usunięty z homebrew-core , więcej informacji w tej odpowiedzi. podczas instalacji gnu-sedteraz, instrukcje instalacji określają, że musisz dodać gnubindo PATH:PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
pgericson

18

Jak pyta Noufal Ibrahim , dlaczego nie możesz używać Perla? Każdy komputer Mac będzie miał Perla i istnieje bardzo niewiele dystrybucji Linuksa lub BSD, które nie zawierają niektórych wersji Perla w systemie podstawowym. Jednym z niewielu środowisk, w których może brakować Perla, byłby BusyBox (który działa jak GNU / Linux -i, z wyjątkiem tego, że nie można określić rozszerzenia kopii zapasowej).

Jak zaleca ismail ,

Ponieważ perl jest dostępny wszędzie, po prostu to robię perl -pi -e s,foo,bar,g target.file

i wydaje się to lepszym rozwiązaniem w prawie każdym przypadku niż skrypty, aliasy lub inne obejścia, aby poradzić sobie z podstawową niezgodnością sed -imiędzy GNU / Linux a BSD / Mac.


Uwielbiam to rozwiązanie. perljest częścią specyfikacji Linux Standard Base od 1 maja 2009 r. refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/...
OregonTrail

1
Jest to z
pewnością

17

Nie ma sposobu, aby działało.

Jednym ze sposobów jest użycie pliku tymczasowego, takiego jak:

TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file

Działa to w obu przypadkach


Czy istnieje powód, dla którego SOME_FILE = "$ (sed -s" s / abc / def / "some / file)"; echo „$ SOME_FILE”> some / file; zamiast tego nie zadziała
Jason Gross,

Wygląda na to, że byłbyś ograniczony przez maksymalny rozmiar zmiennej bash, nie? Nie jestem pewien, czy będzie działać z plikami GB.
analog

3
Jeśli tworzysz plik tymczasowy, dlaczego nie dać rozszerzenia, aby utworzyć plik kopii zapasowej (który możesz następnie usunąć), jak sugeruje poniżej @kine?
Alex Dupuy,

13

Odpowiedź: Nie.

Oryginalnie zaakceptowana odpowiedź tak naprawdę nie spełnia wymagań (jak zaznaczono w komentarzach). (Znalazłem tę odpowiedź, gdy szukałem powodu, dla którego a file-epojawiał się „losowo” w moich katalogach).

Najwyraźniej nie ma sposobu sed -ina konsekwentną pracę na MacOS i Linuces.

Zalecam, aby nie warto aktualizować w miejscu sed(które ma złożone tryby awarii), ale generować nowe pliki i zmieniać ich nazwy później. Innymi słowy: unikaj -i.


9

Ta -iopcja nie jest częścią POSIX Sed . Bardziej przenośną metodą byłoby użycie Vima w trybie Ex:

ex -sc '%s/alfa/bravo/|x' file
  1. % wybierz wszystkie linie

  2. s zastąpić

  3. x Zapisz i zamknij


To jest poprawna odpowiedź. Kluczową cechą POSIX jest przenośność.
Kajukenbo,

9

Oto kolejna wersja, która działa w systemie Linux i macOS bez użycia evali bez konieczności usuwania plików kopii zapasowej. Używa tablic Bash do przechowywania sedparametrów, co jest czystsze niż przy użyciu eval:

# Default case for Linux sed, just use "-i"
sedi=(-i)
case "$(uname)" in
  # For macOS, use two parameters
  Darwin*) sedi=(-i "")
esac

# Expand the parameters in the actual call to "sed"
sed "${sedi[@]}" -e 's/foo/bar/' target.file

Nie tworzy to pliku kopii zapasowej ani pliku z dołączonymi cytatami.


2
to jest poprawna odpowiedź. Uprościłbym to trochę, ale pomysł działa idealnie sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '') sed "${sedi[@]}" -e 's/foo/bar/' target.file
Alex Skrypnyk

Dobry towar! Wolę jednak dłuższą wersję ze względu na czytelność.
nwinkler

1
To dla mnie najlepszy sposób na kompatybilność z innym systemem.
Victor Choy,

6

Odpowiedź Steve'a Powella jest całkiem poprawna, zapoznanie się ze stroną MAN dla sed w OSX i Linux (Ubuntu 12.04) podkreśla niezgodność w zakresie użycia sed w „miejscu” w obu systemach operacyjnych.

JFYI, nie powinno być spacji między -i i dowolnymi cudzysłowami (które oznaczają puste rozszerzenie pliku) przy użyciu sed wersji Linux, więc

sed Linux Man Page

#Linux
sed -i"" 

i

sed Strona podręcznika OSX

#OSX (notice the space after the '-i' argument)
sed -i "" 

Obejrzałem to w skrypcie, używając komendy alias'd i wyjścia nazwy systemu operacyjnego „ uname ” w ciągu bash „if”. Próba przechowywania ciągów poleceń zależnych od systemu operacyjnego w zmiennych była trafiona i pominięta podczas interpretacji cudzysłowów. Użycie „ shopt -s expand_aliases ” jest konieczne, aby rozwinąć / użyć aliasów zdefiniowanych w skrypcie. wykorzystanie shopt jest omówione tutaj .


1

Jeśli trzeba zrobić sedna miejscu w bashskrypcie, a ty nie chcesz, aby w miejscu spowoduje z plikami .bkp i masz sposób na wykrycie OS (powiedzmy, korzystając ostype.sh ) - wtedy następujący hack z bashwbudowaną powłoką evalpowinien działać:

OSTYPE="$(bash ostype.sh)"

cat > myfile.txt <<"EOF"
1111
2222
EOF

if [ "$OSTYPE" == "osx" ]; then
  ISED='-i ""'
else # $OSTYPE == linux64
  ISED='-i""'
fi

eval sed $ISED 's/2222/bbbb/g' myfile.txt
ls 
# GNU and OSX: still only myfile.txt there

cat myfile.txt
# GNU and OSX: both print:
# 1111
# bbbb

# NOTE: 
# if you just use `sed $ISED 's/2222/bbbb/g' myfile.txt` without `eval`,
# then you will get a backup file with quotations in the file name, 
# - that is, `myfile.txt""`

0

Możesz użyć gąbki. Sponge to stary program uniksowy, znajdujący się w pakiecie moreutils (zarówno w Ubuntu i prawdopodobnie Debianie, jak i homebrew w Macu).

Buforuje całą zawartość z potoku, poczekaj, aż potok się zamknie (prawdopodobnie oznacza to, że plik wejściowy jest już zamknięty), a następnie zastąpi:

Od strony man :

Streszczenie

plik sed ... grep „...” | pilnik gąbkowy


3
Ładne podejście - niestety tak naprawdę nie pomaga w pierwotnej sytuacji, ponieważ powodem ucieczki się do sed było posiadanie czegoś do użycia w wieloplatformowym skrypcie pomocniczym używanym na komputerach klienckich, gdzie nie można polegać na niczym innym niż na systemie instalowane narzędzia. Może jednak być pomocny dla kogoś innego.
dnadlinger

0

Poniższe działa dla mnie w systemach Linux i OS X:

sed -i' ' <expr> <file>

np. dla pliku fzawierającegoaaabbaaba

sed -i' ' 's/b/c/g' f

daje aaaccaacazarówno Linux, jak i Mac. Zauważ, że istnieje cytowany ciąg zawierający spację , bez spacji między nim -ia ciągiem. Oba pojedyncze lub podwójne cudzysłowy działają.

W systemie Linux używam bashwersji 4.3.11 pod Ubuntu 14.04.4, a Mac wersji 3.2.57 pod OS X 10.11.4 El Capitan (Darwin 15.4.0).


1
Wow, cieszę się, że nie słuchałem wszystkich powyżej, którzy mówili, że to niemożliwe i czytałem dalej! To naprawdę działa. Dzięki!
Personman

4
Nie działa dla mnie w systemie Mac OS ... tworzony jest nowy plik ze spacją dołączoną na końcu nazwy pliku
Frédéric

Na Macu też nie działa (/ usr / bin / sed) - Mam teraz dodatkowy plik kopii zapasowej z miejscem dołączonym do nazwy pliku :-(
paul_h

Spróbuj usunąć przestrzeń z pojedynczych cudzysłowów, to działa dla mnie.
dps

0

Wpadłem na ten problem. Jedynym szybkim rozwiązaniem było zastąpienie sed w mac do wersji GNU:

brew install gnu-sed

To powiela istniejącą odpowiedź ze znacznie bardziej szczegółowymi
danymi
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.