polecenie sed z opcją -i nie działa na Macu, ale działa na Linuksie

303

Z powodzeniem użyłem następującego sedpolecenia do wyszukiwania / zamiany tekstu w systemie Linux:

sed -i 's/old_link/new_link/g' *

Jednak gdy wypróbuję to w systemie Mac OS X, otrzymuję:

„polecenie c oczekuje \ po którym następuje tekst”

Myślałem, że mój Mac działa z normalną powłoką BASH. Co tam?

EDYTOWAĆ:

Według @High Performance wynika to z tego, że Mac sedma inny smak (BSD), więc moje pytanie brzmi: w jaki sposób mam replikować to polecenie w BSD sed?

EDYTOWAĆ:

Oto faktyczny przykład, który powoduje:

sed -i 's/hello/gbye/g' *
Yarin
źródło
1
Oznacza to, że sedwidzi „c” w danych jako polecenie. Czy używasz zmiennej? Proszę zamieścić coś, co bardziej odzwierciedla rzeczywiste polecenie i niektóre przetwarzane dane. W ten sposób można uzyskać prostą demonstrację tego błędu echo x | sed c.
Wstrzymano do odwołania.
@Dennis, powoduje to proste polecenie powyżej, chociaż przetwarzane przez niego dane to cała strona internetowa (konwertuję wszystkie linki do obrazów), w tym pliki HTML i CSS ...
Yarin

Odpowiedzi:

397

Jeśli skorzystasz z tej -iopcji, musisz podać rozszerzenie dla swoich kopii zapasowych.

Jeśli masz:

File1.txt
File2.cfg

Polecenie (zwróć uwagę na brak miejsca między -ii ''a, -eaby działało na nowych wersjach Maca i GNU):

sed -i'.original' -e 's/old_link/new_link/g' *

Utwórz 2 pliki kopii zapasowych, takie jak:

File1.txt.original
File2.cfg.original

Nie ma przenośnego sposobu na uniknięcie tworzenia kopii zapasowych plików, ponieważ nie można znaleźć kombinacji komend sed, która działa we wszystkich przypadkach:

  • sed -i -e ...- nie działa w systemie OS X, ponieważ tworzy -ekopie zapasowe
  • sed -i'' -e ... - nie działa w systemie OS X 10.6, ale działa w wersji 10.9+
  • sed -i '' -e ... - nie działa na GNU

Uwaga: Ponieważ na wszystkich platformach nie działa polecenie sed, możesz spróbować użyć innego polecenia, aby uzyskać ten sam wynik.

Na przykład, perl -i -pe's/old_link/new_link/g' *

Sinetris
źródło
4
Miałem ten sam problem. Dzięki za to rozwiązanie. Ale kiedy próbowałem z „man sed” znaleźć opis „-i”, nie ma nic o używaniu -i do ignorowania kopii zapasowych. To moja pierwsza wina. Po drugie, gdy pojawia się błąd „polecenie oczekuje \ a po nim tekst”, dlaczego nie mówi nam bezpośrednio, że oczekuje nazwy kopii zapasowej dla opcji „-i” !! ?? Taka sytuacja zdarza się wszędzie: pojawia się błąd, ale nie powód błędu, a następnie wyszukujesz instrukcję, która nic na ten temat nie wyjaśnia. Potem google, aby znaleźć kogoś innego, również ma ten sam problem. Mam na myśli, dlaczego nie podać przykładu w instrukcji?
lukmac
lub powiedz nam, dlaczego wystąpił błąd zamiast tylko informacyjnego komunikatu o błędzie. Jest to sugestia dla wszystkich twórców narzędzi, jeśli kiedykolwiek mogą przeczytać ten komentarz.
lukmac
5
@lukmac Na tyle, na ile to sedmożliwe, NIE dostarczyłeś przyrostka. Sufiks kopii zapasowej to s/old_link/new_link/g. Następnym argumentem po tym są polecenia edycji. Ponieważ interpretował polecenia jako nazwę kopii zapasowej, następnie wziął pierwszą nazwę pliku jako polecenie edycji, ale nie były one prawidłowe.
Barmar
2
Czy to zawsze będzie problem? Czy jest jakiś sposób, aby Apple mógł stworzyć obejście / pakiet GNU sed z OSX? A może GNU sed nie obsługuje sed -i '' -e ...?
blong
5
sed -i'' -ewydaje się nie działać zgodnie z oczekiwaniami na Mac 10.14
MoOx
64

Uważam, że w systemie OS X, gdy używasz -i, wymagane jest rozszerzenie plików kopii zapasowej . Próbować:

sed -i .bak 's/hello/gbye/g' *

Korzystanie z GNU sedrozszerzenie jest opcjonalne .

Wstrzymano do odwołania.
źródło
55

Działa to zarówno z wersjami sed: GNU, jak i BSD:

sed -i'' -e 's/old_link/new_link/g' *

lub z kopią zapasową:

sed -i'.bak' -e 's/old_link/new_link/g' *

Zwróć uwagę na brak miejsca po -iopcji! (Niezbędne dla GNU sed)

chipiik
źródło
7
Pierwszy z nich nie działa na OSX (Właśnie testowałem go na 10.6.8)
Marcin
4
Dla mnie w systemie OS X (10.10.3) pierwszy utworzył pliki kopii zapasowych z rozszerzeniem -e. Nie dobrze. Drugi był jedyną rzeczą, która działała dla mnie konsekwentnie między Ubuntu i OS X. Jednak nie chciałem tworzyć kopii zapasowych plików, więc musiałem uruchomić rmpolecenie, aby je usunąć.
Brendan
1
Pierwszy wiersz powinien zostać przepisany spacją do pracy 10.10: sed -i'' ...=>sed -i '' ...
Daniel Jomphe
3
@DanielJomphe Ale dodanie tego miejsca nie działa na GNU sed
Brice
1
@marcin pierwszy działa również dla mnie na OSX 10.11.2. Jedyną rzeczą jest to, że tworzy plik kopii zapasowej, który należy później usunąć. Drugi wygląda lepiej, ponieważ nie musimy później wymyślać nazwy pliku.
vikas027
41

Miałem ten sam problem na Macu i rozwiązałem go za pomocą brew:

brew install gnu-sed

i użyj jako

gsed SED_COMMAND

możesz ustawić równie dobrze sedjako alias na gsed(jeśli chcesz):

alias sed=gsed
Sruthi Poddutur
źródło
3
Dlaczego udzieliłeś dokładnie takiej samej odpowiedzi jak Ohad Kravchick ?
gniourf_gniourf
1
ustawienie takiego aliasu nie jest świetnym pomysłem
SantaXL
1
Zamiast aliasu, zgodnie z zaleceniami na odpowiedniej stronie parzenia, dodaj go do ścieżki: PATH = "$ (brew --prefix) / opt / gnu-sed / libexec / gnubin: $ PATH"
y.luis
24

Lub możesz zainstalować wersję GNU sed na swoim Macu, zwaną gsed, i używać go przy użyciu standardowej składni Linuksa.

W tym celu zainstaluj gsedza pomocą portów (jeśli go nie masz, pobierz go na stronie http://www.macports.org/ ), uruchamiając sudo port install gsed. Następnie możesz biegaćsed -i 's/old_link/new_link/g' *

Ohad Kravchick
źródło
24
.. lub jeśli używasz homebrew, zainstalujgnu-sed
Sudar
1
Dzięki @Sudar, kciuki w górę!
Andrew Swan,
9

Twój komputer Mac rzeczywiście obsługuje powłokę BASH, ale jest to raczej kwestia tego, z którą implementacją sed masz do czynienia. Na Macu sed pochodzi z BSD i subtelnie różni się od sed, który można znaleźć na typowym Linux-ie. Proponuję ci man sed.

Znak wysokiej wydajności
źródło
3
Dzięki, że zwróciłeś uwagę na problem BSD. Ale jestem dość niepiśmienny i potrzebuję szybkiej korekty mojego polecenia - szybkie spojrzenie na mężczyznę nic mi nie mówi
Yarin
5
@Yarin - nie, a jeśli rzuciłem okiem na człowieka, to też nic ci nie mówi. Spróbuj dłużej.
High Performance Mark
21
Większość SO odpowiedzi jest pochowanych gdzieś u mężczyzny, ale właśnie tak SO są zajęci ludzie, którzy potrzebują odpowiedzi od inteligentnych ludzi
Yarin
9

Odpowiedź Sinetrisa jest słuszna, ale używam tego z findpoleceniem, aby dokładniej określić, jakie pliki chcę zmienić. Ogólnie powinno to działać (testowane na systemie OSX /bin/bash):

find . -name "*.smth" -exec sed -i '' 's/text1/text2/g' {} \;

Ogólnie rzecz biorąc, stosowanie sedbez findw złożonych projektach jest mniej wydajne.

Lucas
źródło
-execjest bardzo miła! Zastanawiam się tylko, czy cięcie w końcu naprawdę jest potrzebne
Ye Liu
2
@YeLiu: bez \The ;got interpretowane przez powłokę kiedy próbowałem takiego
Serv-inc
2

Stworzyłem funkcję do obsługi sedróżnicy między MacOS (testowanym na MacOS 10.12) a innym systemem operacyjnym:

OS=`uname`
# $(replace_in_file pattern file)
function replace_in_file() {
    if [ "$OS" = 'Darwin' ]; then
        # for MacOS
        sed -i '' -e "$1" "$2"
    else
        # for Linux and Windows
        sed -i'' -e "$1" "$2"
    fi
}

Stosowanie:

$(replace_in_file 's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' "./mysql/.env")

Gdzie:

, jest ogranicznikiem

's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' jest wzorem

"./mysql/.env" to ścieżka do pliku

v.babak
źródło
1

Oto jak zastosować zmienne środowiskowe do pliku szablonu (bez potrzeby tworzenia kopii zapasowej).

1. Utwórz szablon za pomocą {{FOO}} do późniejszego zastąpienia.

echo "Hello {{FOO}}" > foo.conf.tmpl

2. Zastąp {{FOO}} zmienną FOO i wyślij do nowego pliku foo.conf

FOO="world" && sed -e "s/{{FOO}}/$FOO/g" foo.conf.tmpl > foo.conf

Działa zarówno z macOS 10.12.4, jak i Ubuntu 14.04.5

katopz
źródło
0

Jak wskazują inne odpowiedzi, nie ma sposobu, aby używać sed w przenośny sposób w OS X i Linux bez tworzenia kopii zapasowych. Więc zamiast tego użyłem tego Rubinowego jedno-liniowego:

ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml

W moim przypadku musiałem wywołać go z rakezadania (tj. W skrypcie Ruby), więc użyłem tego dodatkowego poziomu cytowania:

sh %q{ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml}
Dan Kohn
źródło
-1
sed -ie 's/old_link/new_link/g' *

Działa zarówno na BSD, jak i Linuksie

ashokrajar
źródło
3
To tworzy na Linuksie kolejną nazwę pliku z edołączonym
Brice,
1
Zepsuty, lepiej go usunąć.
sorin
Działa na komputerach Mac i Linux. Na czym polega problem z tym rozwiązaniem?
Islam Azab,
Nie, nie działa na Mac BSD Sed - utworzyłby plik kopii zapasowej z dodanym „e” do nazwy pliku.
Jesper Grann Laursen