Znajdź i zamień w pliku, a plik zastępowania nie działa, opróżnia plik

604

Chciałbym uruchomić wyszukiwanie i zastąpić w pliku HTML za pomocą wiersza polecenia.

Moje polecenie wygląda mniej więcej tak:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html

Kiedy uruchamiam to i potem patrzę na plik, jest on pusty. Usunął zawartość mojego pliku.

Kiedy uruchomię to po przywróceniu pliku:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Jest stdoutto zawartość pliku, a wyszukiwanie i zamiana zostały wykonane.

Dlaczego to się dzieje?

BBales
źródło
13
Alternatywa dla Perla:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski
bardzo podobne sedpolecenie, aby znaleźć ciąg i zastąpić całą linię: stackoverflow.com/questions/11245144/…
cregox

Odpowiedzi:

917

Gdy powłoka widzi > index.htmlw wierszu poleceń, otwiera plik index.htmldo zapisu , usuwając całą swoją poprzednią zawartość.

Aby to naprawić, musisz przekazać -iopcję, aby sedwprowadzić zmiany bezpośrednio i utworzyć kopię zapasową oryginalnego pliku, zanim dokona on zmian w miejscu:

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Bez .bak polecenie nie powiedzie się na niektórych platformach, takich jak Mac OSX.

kodaddict
źródło
20
Mówienie truncates the filezamiast opens the fileprawdopodobnie czyni to jaśniejszym.
Mikel
12
Przynajmniej na moim komputerze Mac pierwsza sugestia nie działa ... jeśli zastępujesz plik w miejscu, musisz określić rozszerzenie. Możesz przynajmniej przekazać rozszerzenie zerowej długości: sed -i '' / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Tom Lianza
5
dla zmiennych sed -i.bak 's /' $ search '/' $ replace '/ g' index.html
Fatima Zohra
33
na osx, użyj pustego łańcucha '' jako parametru -i, na przykład:sed -i '' 's/blah/xx/g'
Pierre Houston
4
ale co do ciebie .bakpo sed -i?
Patrizio Bertoni,
210

Alternatywnym, użytecznym wzorem jest:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

Ma to ten sam efekt, bez użycia -iopcji, a dodatkowo oznacza, że ​​jeśli skrypt sed z jakiegoś powodu zawiedzie, plik wejściowy nie zostanie zablokowany. Ponadto, jeśli edycja się powiedzie, nie będzie leżącego pliku kopii zapasowej. Ten rodzaj idiomu może być przydatny w Makefiles.

Wiele seds ma taką -iopcję, ale nie wszystkie; posix sed to taki, który tego nie robi. Jeśli więc zależy Ci na przenośności, najlepiej tego unikać.

Norman Gray
źródło
9
+1 za brak pliku kopii zapasowej i nie blokowanie pliku wejściowego, jeśli edycja się nie powiedzie. Działa bezbłędnie na Macu.
Mike Grace
Pracował dla mnie idealnie. Dziękuję Ci! (na komputerze Mac)
zainteresowany
1
Działa to dla mnie idealnie, gdy na Ubuntu Server 14.04 sed -i ciągle zerował plik.
Chris Giddings,
2
Niezwykle niewielkie ulepszenie:... && mv index.html{.tmp,}
EdwardGarson
5
@EdwardGarson Rzeczywiście, prawdopodobnie tego bym użył, gdybym to pisał - zgadzam się, że jest fajniejszy - ale sh(jeśli dobrze pamiętam) nie ma tego {...}rozszerzenia. W Makefile możesz shraczej używać niż bash, więc jeśli dążysz do przenośności (lub posixness), musisz unikać takiej konstrukcji.
Norman Gray,
95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

Dokonuje to globalnego podstawienia lokalnego w pliku index.html. Cytując ciąg zapobiega problemom z białymi znakami w zapytaniu i zamianie.

Rich Apodaca
źródło
57

użyj opcji -i, np

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html
Kevin
źródło
Co to znaczy? sed: -i nie może być używany ze standardowym
arkuszowym
2
Pamiętaj, aby otaczać wzór cytatem, jeśli zawiera spacje -'s/STRING_TO_REPLACE/REPLACE_WITH/g'
Doug Thompson
@ sheheal: -iwykonuje edycję plików na miejscu , więc nie ma sensu łączyć ich z wejściem standardowym .
mklement0
To może działać na macOS, ale nie działa na Arch Linux.
xdevs23,
Bez -e zaakceptowana odpowiedź nie działa na MacOS, Catalina. Z -e działa.
cwhiii
18

Aby zmienić wiele plików (i zapisać kopię zapasową każdego pliku jako * .bak):

perl -p -i -e "s/\|/x/g" *  

weźmie wszystkie pliki w katalogu i wymienić |z x tego nazywany jest „Perl pie” (łatwe, jak ciasto)

Stenemo
źródło
1
Dobrze widzieć kogoś, kto chce przejrzeć opis problemu, a nie tylko tagi. OP nie został określony sedjako wymaganie, wykorzystał go tylko jako narzędzie, które już wypróbowano.
user7412956,
14

Powinieneś spróbować użyć opcji -iedycji w miejscu.

uloBasEI
źródło
6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

Jeśli masz link do dodania, spróbuj tego. Wyszukaj adres URL jak powyżej (zaczynając od https i kończąc na.com tutaj) i zastąp go ciągiem URL. Użyłem $pub_urltutaj zmiennej . stutaj oznacza wyszukiwanie i goznacza globalną wymianę.

To działa !

Kaey
źródło
6

Ostrzeżenie: to niebezpieczna metoda! Nadużywa buforów we / wy w systemie Linux i dzięki konkretnym opcjom buforowania zarządza pracą na małych plikach. To ciekawa ciekawość. Ale nie używaj go do prawdziwej sytuacji!

Poza -iopcją sed można użyć teenarzędzia .

Od man:

tee - odczyt ze standardowego wejścia i zapis na standardowe wyjście i pliki

Tak więc rozwiązaniem byłoby:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

- tutaj teepowtarza się, aby upewnić się, że rurociąg jest buforowany. Następnie wszystkie polecenia w potoku są blokowane, dopóki nie uzyskają danych wejściowych do pracy. Każde polecenie w potoku rozpoczyna się, gdy poprzedzające polecenia zapisują 1 bufor bajtów ( gdzieś rozmiar jest zdefiniowany ) na wejściu polecenia. Tak więc ostatnie polecenie tee index.html, które otwiera plik do zapisu i dlatego go opróżnia, jest uruchamiane po zakończeniu potoku wyjściowego i wyjściu znajduje się w buforze w potoku.

Najprawdopodobniej następujące elementy nie będą działać:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

- uruchomi oba polecenia potoku w tym samym czasie, bez blokowania. (Bez blokowania gazociąg powinien przechodzić linię bajty po linii zamiast buforu przez bufor. Tak samo jak podczas uruchamiania cat | sed s/bar/GGG/. Bez blokowania jest bardziej interaktywny i zazwyczaj rurociągi zaledwie 2 poleceń uruchomić bez buforowania i blokowania. Dłuższe rurociągi są buforowane.) The tee index.htmlwoli otwórz plik do zapisu i zostanie on opróżniony. Jeśli jednak zawsze włączasz buforowanie, druga wersja również będzie działać.

xealits
źródło
3
Plik wyjściowy tee jest również natychmiast otwierany, co skutkuje pustym plikiem index.html dla całego polecenia.
sjngm
3
Spowoduje to uszkodzenie dowolnego pliku wejściowego, który jest większy niż bufor potoku (zwykle jest to 64 KB) . (@sjngm: plik nie jest obcinany natychmiast jak w przypadku >, ale chodzi o to, że jest to zepsute rozwiązanie, które może spowodować utratę danych).
mklement0
4

Problem z poleceniem

sed 'code' file > file

jest to, że filejest obcinane przez powłokę, zanim sed faktycznie ją przetworzy. W rezultacie otrzymujesz pusty plik.

Sed to sposób na -iedycję w miejscu, jak sugerowały inne odpowiedzi. Nie zawsze jest to jednak to, czego chcesz. -iutworzy plik tymczasowy, który zostanie następnie użyty do zastąpienia oryginalnego pliku. Jest to problematyczne, jeśli oryginalny plik był linkiem (link zostanie zastąpiony zwykłym plikiem). Jeśli chcesz zachować łącza, możesz użyć zmiennej tymczasowej do przechowywania danych wyjściowych sed przed zapisaniem ich z powrotem do pliku, w następujący sposób:

tmp=$(sed 'code' file); echo -n "$tmp" > file

Jeszcze lepiej, użycie printfzamiast echoponieważ echojest prawdopodobne, aby proces \\jak \w niektórych muszli (np kreska):

tmp=$(sed 'code' file); printf "%s" "$tmp" > file
Andrzej Pronobis
źródło
1
+1 za zachowanie linków. Działa również z plikiem tymczasowym:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
dashohoxha
3

I edodpowiedź:

printf "%s\n" '1,$s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' w q | ed index.html

Aby powtórzyć odpowiedź na kodaddict , powłoka najpierw obsługuje przekierowanie , usuwając plik „input.html”, a następnie powłoka wywołuje polecenie „sed”, przekazując mu teraz pusty plik.

Glenn Jackman
źródło
2
szybkie pytanie, dlaczego ludzie wciąż udzielają „ edwersji” sedodpowiedzi? czy działa szybciej?
cregox
6
Niektóre seds nie implementują -ido edycji w miejscu. edjest wszechobecny i pozwala zapisywać zmiany w oryginalnym pliku. Ponadto zawsze dobrze jest mieć wiele narzędzi w zestawie.
glenn jackman
ok fajnie. więc pod względem wydajności przypuszczam, że są takie same. dzięki!
cregox
2

Możesz używać Vima w trybie Ex:

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % wybierz wszystkie linie

  2. x Zapisz i zamknij

Steven Penny
źródło
0

Szukałem opcji, w której mogę zdefiniować zakres linii i znalazłem odpowiedź. Na przykład chcę zmienić host1 na host2 z linii 36-57.

sed '36,57 s/host1/host2/g' myfile.txt > myfile1.txt

Możesz również użyć opcji gi, aby zignorować wielkość liter.

sed '30,40 s/version/story/gi' myfile.txt > myfile1.txt

źródło
0

Z całym szacunkiem dla powyższych poprawnych odpowiedzi, zawsze dobrym pomysłem jest uruchamianie takich skryptów „na sucho”, aby nie uszkodzić pliku i zacząć od nowa.

Wystarczy, że skrypt rozleje dane wyjściowe do wiersza poleceń zamiast zapisywać je w pliku, na przykład w ten sposób:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

LUB

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

W ten sposób możesz zobaczyć i sprawdzić dane wyjściowe polecenia bez obcinania pliku.

Nestor Milyaev
źródło