Греп, щоб знайти правильний рядок, sed щоб змінити вміст, а потім повернути його в початковий файл?


9

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

В основному, в одному рядку в моєму файлі є ключове слово 'firmware_revision', і в цьому рядку (і лише в цьому рядку) я хочу замінити слово 'test' на слово 'production'.

Тож я можу це зробити:

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' на цьому рядку на 'виробництво'. Слово "тест" може бути в інших місцях цього файлу, і я не хочу, щоб вони змінювалися. Щоб було зрозуміло, я хочу змінити вищевказаний рядок на

('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і хибних для інших рядків. Якщо умова істинна, виконується наступна команда. В цьому випадку, ця команда є командою замінника , який замінює перше входження 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

Редагувати

У редагуванні на питання ОП просить кожне виникнення testна лінії замінити на production. У цьому випадку ми додаємо gпараметр до команди-замінника для глобальної заміни (для цього рядка):

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

5
Для повноти ви можете також пояснити -i.bakдеталь.
Стівен Харріс

5
@StephenHarris Добре. Пояснення -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).
Satō Katsura

Якщо ви використовуєте тимчасовий файл, ви можете також зберігати inode та perms вихідного файлу, як edі. Замість mv -f "$TF" myfile.plвикористання cat "$TF" > myfile.pl && rm -f "$TF". До речі, добре використовувати імена змінних малих регістрів ( $tfзамість них $TF), вони гарантовано не конфліктують з будь-якими вбудованими варами (можливо, і з іншими оболонками Bourne).
cas

@cas Re:: cat "$TF" > myfile.plспробуйте це: touch a b; chmod 0444 a; cat b >a(BTW, теж sed -iне буде працювати в цьому випадку). Краще дозвольте користувачеві займатися цим. Re:: rm -f "$TF"не потрібно, див trap ... EXIT. Re: $tfзамість $TF: можливо; це питання стилю.
Satō Katsura

Це нормальна та очікувана поведінка дозволів файлів Unix. Моя думка полягала в тому, що створення нового файлу та переміщення його над оригіналом 1. призведе до нового inode для цього імені файлу (порушення будь-яких жорстких посилань). 2. можливо, змінити візові речовини, якщо струм umask відрізняється від вихідних. Що стосується великих регістрів, то це не лише питання стилю - багато людей, які допустили помилку, використовуючи великі регістри PATH або RANDOM або SHELL або що-небудь для власних змінних, знайшли важкий шлях. (і так, я часто використовую великі літери сам. Я знаю, що це неправильно, я намагаюся порушити звичку).
cas
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.