Grep, чтобы найти правильную строку, sed, чтобы изменить содержимое, а затем положить его обратно в исходный файл?

9

Я пытаюсь изменить одно слово в определенной строке в файле, но у меня возникают проблемы с соединением всех вместе.

По сути, в одной строке моего файла есть ключевое слово «firmware_revision», а в этой строке (и только в этой строке) я хочу заменить слово «тест» словом «производство».

Так что я могу сделать это:

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

Это выберет нужную мне строку и выполнит подстановку, но я не могу понять, как вставить эту новую строку в исходный файл, чтобы заменить старую строку. Я, очевидно, не могу просто перенаправить его обратно в файл, так что мне делать?

Даже если я использую временные grepфайлы, используя только нужную мне строку, я теряю все остальные данные в файле, поэтому я больше не могу просто перенаправить все это во временный файл, а затем заменить оригинал временным.

Изменить - Кто-то попросил дополнительную информацию

Допустим, у меня есть файл, полный строк, как это

[
  ('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')
]

Теперь я хочу написать скрипт (в идеале, однострочный), который найдет строку, содержащую «firmware_revision», и изменит все экземпляры слова «test» в этой строке на «production». Слово «тест» может быть в других местах в этом файле, и я не хочу, чтобы они были изменены. Чтобы было ясно, я хочу изменить приведенную выше строку на

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

Как мне это сделать?

Джон Аллард
источник
5
sedОн очень мощный, он может выполнять обе функции ( grepи замену), но нам потребуется больше информации о том, как выглядит линия, чтобы помочь вам.
Grochmal
Я добавлю больше информации к оригинальному сообщению
Джон Аллард
7
Попробуйте:sed -i.bak '/firmware_revision/ s/test/production/' myfile.py
John1024
1
Ах, это сработало отлично! Можете ли вы объяснить синтаксис?
Джон Аллард
@JohnAllard Очень хорошо. Я добавил ответ с объяснением.
John1024

Ответы:

22

Пытаться:

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

Здесь /firmware_revision/действует как условие. Это верно для строк, которые соответствуют регулярному выражению, firmware_revisionи false для других строк. Если условие истинно, выполняется следующая команда. В этом случае, эта команда является командой заменителя , который заменяет первое вхождение testс production.

Другими словами, команда s/test/production/выполняется только в строках, которые соответствуют регулярному выражению firmware_revision. Все остальные линии проходят без изменений.

По умолчанию sed отправляет свой вывод стандартному выводу. Вы, однако, хотели изменить файл на месте. Итак, мы добавили -iопцию. В частности, -i.bakвызывает изменение файла на место с сохранением резервной копии с .bakрасширением.

Если вы решили, что команда работает для вас, и вы хотите жить опасно, а не создавать резервную копию, то с GNU sed (Linux) используйте:

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

В BSD (OSX), напротив, -iпараметр должен иметь аргумент. Если вы не хотите сохранять резервную копию, укажите для нее пустой аргумент. Таким образом, используйте:

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

редактировать

При редактировании вопроса OP запрашивает каждое вхождение testстроки, подлежащей замене production. В этом случае мы добавляем gопцию к команде замены для глобальной (для этой строки) замены:

sed -i.bak '/firmware_revision/ s/test/production/g' myfile.py
John1024
источник
5
Для полноты вы можете также объяснить эту -i.bakчасть.
Стивен Харрис
5
@ StefhenHarris Хороший вопрос. Объяснение -iдобавлено.
John1024
Я чувствую, что вы, вероятно, могли бы достичь звезд, просто стоя на книге, объясняя все, что sedможно сделать ...
KlaymenDK
@ John1024 Не для BSD, не могли бы вы добавить причину первого ''после -i?
Хастур
3
ОП хотел изменить все вхождения слова «тест» в этой строке на «производство» . В этом случае важно добавить / g к команде sed: в sed -i '/firmware_revision/ s/test/production/g' myfile.pyпротивном случае изменяется только первый экземпляр.
Кн
4

На старых машинах со старой школой, sedкоторые не поддерживают опцию -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
Сату Кацура
источник
1
На этих старых машинах вы можете просто использовать edи сделать это в одной строке:printf %s\\n g/firmware_revision/s/test/production/g w q | ed -s myfile.py
don_crissti
1
@don_crissti Очень верно, но ed(1)не предлагает никаких предлогов, чтобы показать использование mktemp(1).
Сат Кацура
Если вы используете временный файл, вы можете также сохранить индекс и привилегии исходного файла так же, как это edделается. Вместо того mv -f "$TF" myfile.pl, чтобы использовать cat "$TF" > myfile.pl && rm -f "$TF". Кстати, хорошей практикой является использование имен переменных в нижнем регистре ( $tfвместо $TF), они гарантированно не конфликтуют с какими-либо встроенными переменными bash (возможно, и с другими оболочками bourne).
Cas
@cas Re:: cat "$TF" > myfile.plпопробуйте это: touch a b; chmod 0444 a; cat b >a(Кстати, sed -iв этом случае тоже не будет работать). Лучше пусть пользователь справится с этим. Re:: rm -f "$TF"не нужно, видите trap ... EXIT. Re: $tfвместо $TF: возможно; это вопрос стиля.
Satō Katsura
Это нормальное и ожидаемое поведение прав доступа к файлам Unix. Моя точка зрения заключалась в том, что 1. создание нового файла и его перетаскивание поверх оригинала приведет к созданию нового inode для этого имени файла (сломав любые жесткие ссылки). 2. возможно, изменить перми, если ток umask отличается от оригинала. Что касается переменных в верхнем регистре, то это не просто вопрос стиля - многие люди, которые допустили ошибку, используя верхний регистр PATH, RANDOM или SHELL или что-то еще для своих собственных переменных, нашли трудный путь. (и да, я часто использую переменные верхнего регистра самостоятельно. Я знаю, что это неправильно, я пытаюсь избавиться от этой привычки).
Cas