Греп, щоб знайти правильний рядок, 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.