plik edycji sed na swoim miejscu

298

Próbuję dowiedzieć się, czy można edytować plik za pomocą pojedynczego polecenia sed bez ręcznego przesyłania strumieniowego edytowanej treści do nowego pliku, a następnie zmieniając nazwę nowego pliku na oryginalną nazwę. Wypróbowałem tę -iopcję, ale mój system Solaris stwierdził, że -ijest to opcja nielegalna. Czy jest inny sposób?

amfibia
źródło
7
-ijest opcją w gnu sed, ale nie ma standardowego sed. Jednak przesyła zawartość do nowego pliku, a następnie zmienia nazwę pliku, więc nie jest to, czego chcesz.
William Pursell
2
właściwie to jest to, czego chcę, po prostu chcę nie być narażony na konieczność wykonywania przyziemnego zadania zmiany nazwy nowego pliku na oryginalną nazwę
amfibia
3
Następnie musisz ponownie sformułować pytanie.
William Pursell
3
@amphibient: Czy mógłbyś w ogóle poprzedzić tytuł pytania słowem „Solaris”? Wartość twojego pytania jest tracona. Proszę zobaczyć komentarze poniżej mojej odpowiedzi. Dzięki.
Steve
1
@ Steve: Znów usunąłem przedrostek Solaris z tytułu, ponieważ nie jest to bynajmniej wyłączne dla Solaris.
tripleee

Odpowiedzi:

475

-iopcja strumieni na edytowanej zawartości do nowego pliku, a następnie zmienia nazwę go za kulisami, w każdym razie.

Przykład:

sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

i

sed -i '' 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

na macOS .

choroba
źródło
3
przynajmniej robi to dla mnie, więc nie muszę
amfibia
2
Możesz więc spróbować skompilować GNU sed w swoim systemie.
choroba
3
przykład:sed -i "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>
Thales Ceolin
2
To jest lepsze niż rozwiązanie Perla. Jakoś nie tworzy żadnego pliku .swap .... dzięki!
matematyka
3
@maths: Tak, ale może gdzieś indziej lub na krótszy czas?
choroba
72

W systemie, w którym sednie ma możliwości edycji plików, myślę, że lepszym rozwiązaniem byłoby użycie perl:

perl -pi -e 's/foo/bar/g' file.txt

Chociaż tworzy to plik tymczasowy, zastępuje oryginał, ponieważ dostarczono pusty przyrostek / rozszerzenie.

Steve
źródło
14
Nie tylko tworzy plik tymczasowy, ale także łamie twarde linki (np. Zamiast zmieniać zawartość pliku, zastępuje go nowym plikiem o tej samej nazwie). Czasami jest to pożądane zachowanie, czasem jest akceptowalne, ale gdy istnieje wiele linków do pliku, prawie zawsze jest to niewłaściwe zachowanie.
William Pursell
3
@DwightSpencer: Uważam, że wszystkie systemy bez Perla są zepsute. Jedno polecenie przebija łańcuch poleceń, gdy chce się zwiększyć uprawnienia i trzeba go użyć sudo. Moim zdaniem pytanie dotyczy tego, jak uniknąć ręcznego tworzenia i zmiany nazwy pliku tymczasowego bez konieczności instalowania najnowszego GNU lub BSD sed. Po prostu użyj Perla.
Steve
4
@PetrPeller: Naprawdę? Jeśli dokładnie przeczytasz pytanie, zrozumiesz, że PO próbuje uniknąć czegoś takiego, jak sed 's/foo/bar/g' file.txt > file.tmp && mv file.tmp file.txt. To, że edycja w miejscu zmienia nazwę przy użyciu pliku tymczasowego, nie oznacza, że musi on / ona ręcznie zmienić nazwę, gdy opcja nie jest dostępna. Istnieją inne narzędzia, które mogą robić to, czego chce, a Perl jest oczywistym wyborem. Jak to nie jest odpowiedź na pierwotne pytanie?
Steve
2
@a_arias: Masz rację; on zrobił. Ale nie może osiągnąć tego, czego chce, korzystając z Solaris sed. Pytanie było rzeczywiście bardzo dobry, ale jego wartość została zmniejszona przez ludzi, którzy albo nie potrafią czytać lub stron podręcznika nie może być jedno, aby rzeczywiście czytają pytania tutaj na SO.
Steve
4
@EdwardGarson: IIRC, Solaris jest dostarczany z Perlem, ale nie z Pythonem ani Ruby. I jak powiedziałem wcześniej, OP nie może osiągnąć pożądanego rezultatu za pomocą Solaris sed.
Steve
56

Pamiętaj, że w systemie OS X mogą wystąpić dziwne błędy, takie jak „nieprawidłowy kod polecenia” lub inne dziwne błędy podczas uruchamiania tego polecenia. Aby rozwiązać ten problem, spróbuj

sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>

To dlatego, że od wersji OSX z sedThe -iopcja oczekuje extensionargumentu więc komenda jest analizowany jakoextension argument i ścieżka do pliku jest interpretowana jako kodu poleceń. Źródło: https://stackoverflow.com/a/19457213

diadyne
źródło
1
To kolejna dziwna cecha OS X. Na szczęście brew install gnu-sedwraz z a PATHpozwoli ci korzystać z wersji GNU sed. Dzięki za wzmiankę; zdecydowanie drapak w głowie.
Doug
23

Poniższe działa dobrze na moim komputerze Mac

sed -i.bak 's/foo/bar/g' sample

Zastępujemy foo paskiem w przykładowym pliku. Kopia zapasowa oryginalnego pliku zostanie zapisana w sample.bak

Do edycji wbudowanej bez kopii zapasowej użyj następującego polecenia

sed -i'' 's/foo/bar/g' sample
minhas23
źródło
W jaki sposób można zapobiec przechowywaniu plików kopii zapasowych?
Petr Peller
@PetrPeller, możesz użyć wspomnianego polecenia do tej samej ... próbki -i '' s / foo / bar / g '
minhas23
18

Należy zauważyć, sedże nie można samodzielnie zapisywać plików, ponieważ jedynym celem sed jest działanie jako edytor w „strumieniu” (tj. Potokach stdin, stdout, stderr i innych >&nbuforach, gniazdach i tym podobnych). Mając to na uwadze, możesz użyć innego polecenia, teeaby zapisać dane wyjściowe z powrotem do pliku. Inną opcją jest utworzenie łatki z pipingu zawartości diff.

Metoda tee

sed '/regex/' <file> | tee <file>

Metoda łatki

sed '/regex/' <file> | diff -p <file> /dev/stdin | patch

AKTUALIZACJA:

Zauważ też, że łatka sprawi, że plik zmieni się z pierwszego wiersza wyjścia różnicowego:

Łatka nie musi wiedzieć, do którego pliku uzyskać dostęp, ponieważ znajduje się on w pierwszym wierszu wyniku z diff:

$ echo foobar | tee fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin
*** fubar   2014-03-15 18:06:09.000000000 -0500
--- /dev/stdin  2014-03-15 18:06:41.000000000 -0500
***************
*** 1 ****
! foobar
--- 1 ----
! fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin | patch
patching file fubar
Dwight Spencer
źródło
2
/dev/stdin 2014-03-15, odpowiedział 7 stycznia - czy jesteś podróżnikiem w czasie?
Adrian Frühwirth
W systemie Windows użycie msysgit /dev/stdinnie istnieje, więc musisz zastąpić /dev/stdin„-” pojedynczym łącznikiem bez cudzysłowów, więc następujące polecenia powinny działać: $ sed 's/oo/u/' fubar | diff -p fubar -i$ sed 's/oo/u/' fubar | diff -p fubar - | patch
Jim Raden
@ AdrianFrühwirth Mam gdzieś w pobliżu niebieską skrzynkę policyjną iz jakiegoś powodu nazywają mnie Doktorem w pracy. Ale szczerze mówiąc, wygląda na to, że w tym czasie w systemie było naprawdę złe przesunięcie zegara.
Dwight Spencer
Rozwiązania te wydają mi się polegać na określonym zachowaniu buforowania. Podejrzewam, że w przypadku większych plików mogą się one zepsuć, ponieważ podczas czytania sed plik może zacząć się zmieniać pod komendą patch, a nawet gorzej przez komendę tee, która skróci plik. Właściwie nie jestem pewien, czy też patchnie zostanie obcięty. Myślę, że zapisywanie danych wyjściowych w innym pliku, a następnie catprzejście do oryginału byłoby bezpieczniejsze.
akostadinov
prawdziwa odpowiedź na miejscu od @ william-pursell
akostadinov
11

Nie można robić tego, co chcesz sed. Nawet wersje sedtego typu obsługują -iopcję edycji pliku w miejscu, robią dokładnie to, czego wyraźnie nie chcesz: zapisują w pliku tymczasowym, a następnie zmieniają jego nazwę. Ale może możesz po prostu użyć ed. Na przykład, aby zmienić wszystkie wystąpienia foona barw pliku file.txt, możesz:

echo ',s/foo/bar/g; w' | tr \; '\012' | ed -s file.txt

Składnia jest podobna do sed , ale z pewnością nie dokładnie taka sama.

Ale może powinieneś rozważyć, dlaczego nie chcesz używać pliku tymczasowego o zmienionej nazwie. Nawet jeśli nie masz -iwsparcia sed, możesz łatwo napisać skrypt, który wykona pracę za Ciebie. Zamiast tego sed -i 's/foo/bar/g' filemożesz zrobić inline file sed 's/foo/bar/g'. Taki skrypt jest trywialny do napisania. Na przykład:

#!/bin/sh -e
IN=$1
shift
trap 'rm -f $tmp' 0
tmp=$( mktemp )
<$IN "$@" >$tmp && cat $tmp > $IN  # preserve hard links

powinien być odpowiedni do większości zastosowań.

William Pursell
źródło
1
na przykład, jak nazwałbyś ten skrypt i jak byś go nazwał?
amfibia
2
Nazwałbym to inlinei przywołałem, jak opisano powyżej:inline inputfile sed 's/foo/bar/g'
William Pursell
1
wow, edtylko odpowiedź, która naprawdę działa w miejscu. Pierwszy raz, kiedy potrzebuję ed. Dziękuję Ci. Niewielką optymalizacją jest użycie echo -e ",s/foo/bar/g\012 w"lub echo $',s/foo/bar/g\012 w'tam, gdzie powłoka pozwala na uniknięcie dodatkowego trpołączenia.
akostadinov
2
edtak naprawdę nie wprowadza modyfikacji na miejscu. Na podstawie stracedanych wyjściowych GNU edtworzy plik tymczasowy, zapisuje zmiany w pliku tymczasowym, a następnie przepisuje cały oryginalny plik, zachowując twarde łącza. Z trussdanych wyjściowych Solaris 11 edużywa również pliku tymczasowego, ale zmienia nazwę pliku tymczasowego na pierwotną nazwę pliku po zapisaniu za pomocą wpolecenia, niszcząc wszelkie twarde łącza.
Andrew Henle,
@AndrewHenle To zniechęcające. To, edco jest dostarczane z obecnymi makami (Mojave, cokolwiek to oznacza marketingowy żargon), nadal zachowuje twarde linki. YMMV
William Pursell
10

Możesz użyć vi

vi -c '%s/foo/bar/g' my.txt -c 'wq'
różowe
źródło
9

sed obsługuje edycję w miejscu. Od man sed:

-i[SUFFIX], --in-place[=SUFFIX]

    edit files in place (makes backup if extension supplied)

Przykład :

Załóżmy, że masz plik hello.txtz tekstem:

hello world!

Jeśli chcesz zachować kopię zapasową starego pliku, użyj:

sed -i.bak 's/hello/bonjour' hello.txt

Otrzymasz dwa pliki: hello.txtz zawartością:

bonjour world!

i hello.txt.bakze starą treścią.

Jeśli nie chcesz zachować kopii, po prostu nie przekazuj parametru rozszerzenia.

Sergio A.
źródło
3
OP wyraźnie wspomina, że ​​jego platforma nie obsługuje tej (popularnej, ale) niestandardowej opcji.
tripleee
3

Nie określiłeś, jakiej powłoki używasz, ale dzięki zsh możesz użyć =( )konstrukcji, aby to osiągnąć. Coś w stylu:

cp =(sed ... file; sync) file

=( )jest podobny, >( )ale tworzy plik tymczasowy, który jest automatycznie usuwany po cpzakończeniu.

Thor
źródło
3
mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt

Powinny zachować wszystkie dowiązania twarde, ponieważ dane wyjściowe są kierowane z powrotem w celu zastąpienia zawartości oryginalnego pliku i nie wymagają żadnej specjalnej wersji sed.

JJM
źródło
3

Jeśli zastępujesz tę samą liczbę znaków i po uważnym przeczytaniu edycji „w miejscu” plików ...

Możesz również użyć operatora przekierowania, <>aby otworzyć plik do odczytu i zapisu:

sed 's/foo/bar/g' file 1<> file

Zobacz na żywo:

$ cat file
hello
i am here                           # see "here"
$ sed 's/here/away/' file 1<> file  # Run the `sed` command
$ cat file
hello
i am away                           # this line is changed now

Z podręcznika użytkownika Bash → 3.6.10 Otwieranie deskryptorów plików do odczytu i zapisu :

Operator przekierowania

[n]<>word

powoduje, że plik, którego nazwa jest rozszerzeniem słowa, jest otwierany zarówno do odczytu, jak i zapisu na deskryptorze pliku n lub na deskryptorze pliku 0, jeśli n nie jest określone. Jeśli plik nie istnieje, jest tworzony.

fedorqui „SO przestań szkodzić”
źródło
To jest uszkodzone przez plik. Nie jestem pewien przyczyny tego. paste.fedoraproject.org/462017
Nehal J Wani
Patrz wiersze [43–44], [88–89], [135–136]
Nehal J Wani
2
W tym blogu wyjaśniono, dlaczego nie należy używać tej odpowiedzi. backreference.org/2011/01/29/in-place-editing-of-files
Nehal J Wani
@NehalJWani Będę długo czytać artykuł, dzięki za heads-up! W odpowiedzi nie wspomniałem, że działa to, jeśli zmienia tę samą liczbę znaków.
fedorqui „SO przestań szkodzić”
@NehalJWani powiedziawszy to, przepraszam, jeśli moja odpowiedź spowodowała uszkodzenie twoich plików. Zaktualizuję go z poprawkami (lub w razie potrzeby usunę) po przeczytaniu podanych linków.
fedorqui „SO przestań szkodzić”
2

Jak powiedział Moneypenny w Skyfall: „Czasami najlepsze są stare sposoby”. Kincade powiedział później coś podobnego.

$ printf ',s/false/true/g\nw\n' | ed {YourFileHere}

Udana edycja w miejscu. Dodano „\ nw \ n”, aby zapisać plik. Przepraszamy za opóźnienie w odpowiedzi na prośbę.

jlettvin
źródło
Czy możesz wyjaśnić, co to robi?
Matt Montag
1
Wywołuje ed (1), edytor tekstu z niektórymi znakami zapisanymi na stdin. Jako osoba poniżej 30 roku życia uważam, że mogę powiedzieć, że ed (1) dotyczy dinozaurów, tak jak dinozaury. W każdym razie, zamiast otwierać ed i wpisywać te rzeczy, jlettvin używa potworów |. @MattMontag
Ari Sweedler
Jeśli pytasz, co robią polecenia, są dwa, zakończone znakiem \n. [range] s / <old> / <new> / <flag> jest formą polecenia zastępczego - paradygmatu wciąż występującego w wielu innych edytorach tekstu (ok, vim, bezpośredni potomek ed) W każdym razie gflaga oznacza „globalny” i oznacza „Każda instancja w każdej linii, na którą patrzysz”. Zakres 3,5wygląda na liniach 3-5. ,5patrzy na StartOfFile-5, 3,patrzy na linie 3-EndOfFile i ,patrzy na StartOfFile-EndOfFile. Globalne polecenie podstawiania w każdym wierszu, które zastępuje „fałsz” słowem „prawda”. Następnie wwprowadzane jest polecenie zapisu .
Ari Sweedler,
1

Bardzo dobre przykłady. Miałem problem z edycją wielu plików, a opcja -i wydaje się być jedynym rozsądnym rozwiązaniem używającym go w poleceniu find. Tutaj skrypt, aby dodać „wersję:” przed pierwszym wierszem każdego pliku:

find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;
Robert
źródło