Grep, aby znaleźć prawidłową linię, sed, aby zmienić zawartość, a następnie umieścić ją z powrotem w oryginalnym pliku?

9

Próbuję zmienić pojedyncze słowo w określonym wierszu w pliku, ale mam problemy z połączeniem wszystkich razem.

Zasadniczo w jednym wierszu w moim pliku znajduje się słowo kluczowe „wersja_oprogramowania”, a w tym wierszu (i tylko w tym wierszu) chcę zastąpić słowo „test” słowem „produkcja”.

Więc mogę to zrobić:

grep 'firmware_revision' myfile.py | sed 's/test/production'

Spowoduje to wybranie żądanej linii i wykonanie podstawienia, ale nie mogę wymyślić, jak przenieść tę nową linię do oryginalnego pliku, aby zastąpić starą. Oczywiście nie mogę po prostu przekierować go z powrotem do pliku, więc co powinienem zrobić?

Nawet jeśli używam grepplików tymczasowych, używając tylko potrzebnego wiersza, tracę wszystkie inne dane w pliku, więc nie mogę już przekierować ich wszystkich do pliku tymczasowego, a następnie zastąpić oryginał tempem.

Edytuj - ktoś poprosił o więcej informacji

Powiedzmy, że mam plik pełen takich linii

[
  ('key_name1', str, 'value1', 'Description'),
  ('key_name2', str, 'value2', 'Description'),
  ('key_name3', str, 'value3', 'Description'),
  ('firmware_revision', str, 'my-firmware-name-test', 'Firmware revision name')
]

teraz chcę napisać skrypt (najlepiej jednowierszowy), który znajdzie wiersz zawierający „wersja_sprawdzania oprogramowania” i zmieni wszystkie wystąpienia słowa „test” w tym wierszu na „produkcja”. Słowo „test” może znajdować się w innych miejscach tego pliku i nie chcę ich zmieniać. Żeby było jasne, chcę zmienić powyższą linię na

('firmware_revision', str, 'my-firmware-name-production', 'Firmware revision name')

Jak mam to zrobic?

John Allard
źródło
5
sedjest bardzo potężny, może wykonywać obie funkcje ( grepi podstawianie), ale potrzebujemy więcej informacji o tym, jak wygląda linia, aby ci pomóc.
grochmal
Dodam więcej informacji do oryginalnego postu
John Allard
7
Spróbuj:sed -i.bak '/firmware_revision/ s/test/production/' myfile.py
John1024,
1
Ach, działało idealnie! Czy potrafisz wyjaśnić składnię?
John Allard,
@JohnAllard Bardzo dobrze. Dodałem odpowiedź z wyjaśnieniem.
John1024,

Odpowiedzi:

22

Próbować:

sed -i.bak '/firmware_revision/ s/test/production/' myfile.py

Tutaj /firmware_revision/działa jako warunek. Dotyczy to wierszy pasujących do wyrażenia regularnego firmware_revisioni fałszem dla innych wierszy. Jeśli warunek jest spełniony, następuje wykonanie następującego polecenia. W tym przypadku, że rozkaz jest rozkazem substytut, który zastępuje pierwsze wystąpienie testz production.

Innymi słowy, polecenie s/test/production/jest wykonywane tylko na liniach, które pasują do wyrażenia regularnego firmware_revision. Wszystkie pozostałe linie przechodzą bez zmian.

Domyślnie sed wysyła dane wyjściowe do standardowego wyjścia. Chciałeś jednak zmienić plik na miejscu. Więc dodaliśmy -iopcję. W szczególności -i.bakpowoduje zmianę pliku w kopii zapasowej zapisanej z .bakrozszerzeniem.

Jeśli zdecydowałeś, że polecenie działa dla Ciebie i chcesz żyć niebezpiecznie i nie tworzyć kopii zapasowej, to w GNU sed (Linux) użyj:

sed -i '/firmware_revision/ s/test/production/' myfile.py

Natomiast w BSD (OSX) -iopcja musi mieć argument. Jeśli nie chcesz przechowywać kopii zapasowej, podaj pusty argument. Dlatego użyj:

sed -i '' '/firmware_revision/ s/test/production/' myfile.py

Edytować

W edycji pytania PO żąda zastąpienia każdego wystąpienia testwiersza production. W takim przypadku dodajemy gopcję do polecenia zastępowania w celu zastąpienia globalnego (dla tej linii):

sed -i.bak '/firmware_revision/ s/test/production/g' myfile.py
John1024
źródło
5
Dla kompletności możesz również wyjaśnić tę -i.bakczęść.
Stephen Harris
5
@StephenHarris Dobra uwaga. Wyjaśnienie -idodanego.
John1024,
Myślę, że prawdopodobnie dotrzesz do gwiazd, po prostu stojąc na książce i wyjaśniając wszystkie rzeczy, które sedmogą zrobić ...
KlaymenDK
@ John1024 Czy dla osób spoza BSD możesz podać przyczyny pierwszego ''po -i?
Hastur
3
OP chciał zmienić wszystkie wystąpienia słowa „test” w tym wierszu na „produkcja” . W tym przypadku ważne jest dołączenie / g do polecenia sed: sed -i '/firmware_revision/ s/test/production/g' myfile.pyw przeciwnym razie zmieni się tylko pierwsza instancja.
knub
4

Na starszych komputerach ze starą szkołą, sedktóre nie obsługują opcji -i:

TF=$( mktemp -t "${0##*/}"_$$_XXXXXXXX ) && \
trap 'rm -f "$TF"' EXIT HUP INT QUIT TERM && \
sed '/firmware_revision/ s/test/production/' myfile.py >"$TF" && \
mv -f "$TF" myfile.py
Satō Katsura
źródło
1
Na tych starszych komputerach możesz po prostu użyć edi zrobić to w jednym wierszu:printf %s\\n g/firmware_revision/s/test/production/g w q | ed -s myfile.py
don_crissti
1
@don_crissti To prawda, ale ed(1)nie oferuje żadnego pretekstu do pokazania użycia mktemp(1).
Satō Katsura
Jeśli używasz pliku tymczasowego, możesz zachować i-węzeł i perms oryginalnego pliku, tak samo jak edrobi. Zamiast tego mv -f "$TF" myfile.plużyj cat "$TF" > myfile.pl && rm -f "$TF". BTW, dobrą praktyką jest używanie małych nazw zmiennych ( $tfzamiast $TF), gwarantuje się, że nie będą kolidować z żadnymi wbudowanymi zmiennymi bash (prawdopodobnie także z innymi powłokami Bourne'a).
cas
@cas Re:: cat "$TF" > myfile.plspróbuj tego: touch a b; chmod 0444 a; cat b >a(BTW, sed -iw takim przypadku też nie będzie działać). Lepiej pozwól użytkownikowi sobie z tym poradzić. Odp: rm -f "$TF"nie potrzebne, patrz trap ... EXIT. Re: $tfzamiast $TF: może; to kwestia stylu.
Satō Katsura,
To normalne i oczekiwane zachowanie uprawnień do plików unix. Chodzi mi o to, że utworzenie nowego pliku i przeniesienie go na oryginał spowoduje 1. utworzenie nowego i-węzła dla tej nazwy pliku (zerwanie wszelkich twardych linków). 2. ewentualnie zmienić perms, jeśli prąd umask różni się od pierwotnych perms. Jeśli chodzi o wielkie litery, nie jest to tylko kwestia stylu - wiele osób, które popełniły błąd przy użyciu wielkich liter PATH lub RANDOM lub SHELL, czy cokolwiek innego dla własnych zmiennych, znalazło trudną drogę. (i tak, często sam używam wielkich liter. Wiem, że to źle, próbuję zerwać z nałogiem).
cas