Вставте рядок після першого матчу за допомогою sed


245

Я чомусь не можу знайти прямої відповіді на це, і на даний момент я переживаю трохи кризи. Як би я пішов про вставку рядка тексту вибору після першого рядка, що відповідає певній рядку за допомогою sedкоманди. У мене є ...

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

І я хочу вставити рядок після CLIENTSCRIPT=рядка, що призводить до ...

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Відповіді:


379

Спробуйте зробити це за допомогою GNU sed:

sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

якщо ви хочете замінити місце , скористайтеся

sed -i '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

Вихідні дані

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Док

  • див. sed doc та пошук \a(додавати)

Дякую! І щоб змусити його записувати назад у файл без друку вмісту файлу?
користувач2150250

11
Зауважте, що обидва припускають GNU sed. Це не стандартний sedсинтаксис і не працюватиме з будь-якою іншою sedреалізацією.
Стефан Хазелас

У мене виникли проблеми з тим, щоб це працювало для мене, оскільки я використовував інший роздільник. Після RTFM'ing я зрозумів, що повинен запустити регулярний вираз з \, а потім з роздільника.
Крісті

10
Як це застосовується ТОЛЬКО до першого матчу? зрозуміло, що він додає текст після матчу, але як це знати лише до першого матчу?
Мухаммед Нурелдін

7
Це додає текст після кожного матчу для мене.
schoppenhauer

49

Зверніть увагу на стандартний sedсинтаксис (як у POSIX, який підтримується всіма відповідними sedреалізаціями навколо (GNU, OS / X, BSD, Solaris ...)):

sed '/CLIENTSCRIPT=/a\
CLIENTSCRIPT2="hello"' file

Або в одному рядку:

sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file

( -expressions (і вміст -files) з'єднуються з новими рядками, щоб скласти sedінтерпретації сценарію sed ).

Можливість -iредагування на місці - це також розширення GNU, підтримка цього в деяких інших реалізаціях (наприклад, FreeBSD) -i ''.

Крім того, для портативності ви можете використовувати perlзамість цього:

perl -pi -e '$_ .= qq(CLIENTSCRIPT2="hello"\n) if /CLIENTSCRIPT=/' file

Або ви можете використовувати edабо ex:

printf '%s\n' /CLIENTSCRIPT=/a 'CLIENTSCRIPT2="hello"' . w q | ex -s file

2
Я можу помилятися, але течія sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' fileуникає цитати в кінці першого параметра і порушує команду.
Покинутий кошик

1
@AbaintedCart, в оболонках Борна cshчи rcсім'ї, '...'є сильними цитатами, всередині яких зворотний кут не особливий. Єдиний виняток, який я знаю, - fishоболонка.
Стефан Хазелас

1
Термін на MacOS (а згодом і сценарій, запущений у терміналі), мабуть, також є винятком. Я знайшов альтернативний синтаксис, але все одно дякую.
Покинутий кошик

1
@AbaintedCart, це щось інше. Це macOS тут sedне сумісний з POSIX. Це робить моє твердження про те, що воно переносно некоректно (для варіанту з одним рядком). Я попрошу відкрити групу для підтвердження, чи справді це невідповідність або неправильне тлумачення стандарту з мого боку.
Стефан Хазелас

19

Сумісний з POSIX за допомогою sкоманди:

sed '/CLIENTSCRIPT="foo"/s/.*/&\
CLIENTSCRIPT2="hello"/' file

1
... що навіть підтримує вставлення нових рядків. Я це люблю!
Стефан Геннінгсен

1
Дивіться також, sed -e '/.../s/$/\' -e 'CLI.../'що б уникнути проблем із рядками, які містять байтові послідовності, не утворюючи дійсних символів.
Стефан Хазелас

14

Команда Sed, яка працює на MacOS (принаймні, ОС 10) та Unix (тобто. Не потребує gnu sed як Gilles '(наразі прийнято))

sed -e '/CLIENTSCRIPT="foo"/a\'$'\n''CLIENTSCRIPT2="hello"' file

Це працює в bash і, можливо, і в інших оболонках, які знають стиль цитування $ '\ n' оцінки. Все може бути в одному рядку та працювати в старих командах / POSIX sed. Якщо може бути кілька рядків, що відповідають CLIENTSCRIPT = "foo" (або вашому еквіваленту), і ви хочете додати додатковий рядок лише перший раз, ви можете переробити його наступним чином:

sed -e '/^ *CLIENTSCRIPT="foo"/b ins' -e b -e ':ins' -e 'a\'$'\n''CLIENTSCRIPT2="hello"' -e ': done' -e 'n;b done' file

(це створює цикл після коду вставки рядка, який просто переходить через решту файлу, ніколи не повертаючись до першої команди sed).

Ви можете помітити, що я додав '^ *' до відповідного шаблону у випадку, якщо рядок відображається в коментарі, скажімо, або з відступом. Це не на 100% досконало, але охоплює деякі інші ситуації, які можуть бути загальними. Налаштуйте за потребою ...

Ці два рішення також вирішують проблему (для загального рішення для додавання рядка), що якщо ваш новий вставлений рядок містить немальовані зворотні риски або розширення, вони будуть інтерпретовані sed і, ймовірно, не вийдуть однаковими, як і \n є - наприклад. \0буде відповідним першим рядком. Особливо зручно, якщо ви додаєте рядок, що походить від змінної, де вам інакше доведеться уникати всього спочатку, використовуючи $ {var //} раніше, або інший оператор sed тощо.

Це рішення трохи менш заплутане у сценаріях (цитування цього та \ n непросте для читання), коли ви не хочете ставити текст заміни для команди на початку рядка, якщо, скажімо, у функції з відступними лініями. Я скористався тим, що $ '\ n' обчислюється оболонкою до нового рядка, а не в регулярних значеннях \ \ n 'з одним котируванням.

Його отримання стає досить довгим, хоча я думаю, що perl / even awk може виграти через те, що він читає більше.


1
Дякую за відповідь! Перший працює як шарм і на AIX OS.
абхишек

як це зробити, окрім багаторядкової вставки?
тацу

1
POSIX sed підтримує буквальні нові рядки в рядку заміни, якщо ви "уникнете" їх зворотною косою рисою ( джерело ). Отже: s/CLIENTSCRIPT="foo"/&\ [Enter] CLIENTSCRIPT2="hello"/ . Зворотна косої риски повинна бути самим останнім символом на лінії (після неї немає пробілу), всупереч тому, як це виглядає в цьому коментарі.
TheDudeAbides

1
@tatsu Нові рядки в рядку заміни s///, а також ті, що знаходяться в командах aта iкомандах, можна "уникнути", використовуючи той самий метод, який я описав у своєму коментарі вище.
TheDudeAbides

4

Можливо, трохи пізно, щоб опублікувати відповідь на це, але я знайшов деякі з перерахованих вище рішень трохи громіздкими.

Я спробував просту заміну рядка в sed і це спрацювало:

sed 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

& знак відображає відповідні рядки, а потім ви додаєте \ n та новий рядок.

Як згадувалося, якщо ви хочете зробити це на місці:

sed -i 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

Ще одна річ. Можна зіставити, використовуючи вираз:

sed -i 's/CLIENTSCRIPT=.*/&\nCLIENTSCRIPT2="hello"/' file

Сподіваюся, що це комусь допоможе


Це чудово працює в Linux, де GNU sed за замовчуванням, оскільки він розуміє \nв рядку заміни. Plain-ваніль (POSIX) sedнічого НЕ розумію \nв рядку заміни, проте, так що це не буде працювати на BSD або MacOS-якщо ви не встановлені GNU СЕД яким - то чином. За допомогою sed vanilla sed ви можете вставляти нові рядки, "уникаючи" їх, тобто закінчуючи рядок із зворотним нахилом, потім натискаючи [Enter] ( посилання , під заголовком для [2addr]s/BRE/replacement/flags). Це означає, що ваша команда sed повинна охоплювати кілька рядків у терміналі.
TheDudeAbides

Іншим варіантом отримання буквального нового рядка в рядку заміни є використання функції цитування ANSI-C у Bash, про що йдеться в одній з інших відповідей .
TheDudeAbides


1

У мене було подібне завдання, і не вдалося змусити вищезазначене рішення Perl працювати.

Ось моє рішення:

perl -i -pe "BEGIN{undef $/;} s/^\[mysqld\]$/[mysqld]\n\ncollation-server = utf8_unicode_ci\n/sgm" /etc/mysql/my.cnf

Пояснення:

Використовує регулярний вираз для пошуку рядка у моєму файлі /etc/mysql/my.cnf, який містив лише, [mysqld]і замінив його на

[mysqld] collation-server = utf8_unicode_ci

ефективно додавати collation-server = utf8_unicode_ciрядок після рядка, що містить [mysqld].


0

Мені довелося це робити недавно і для Mac, і для ОС ОС, і після перегляду багатьох постів і спроб багатьох речей, на мою думку, я ніколи не потрапляв туди, куди хотів, а це: досить просте, щоб зрозуміти рішення, використовуючи добре відоме і стандартні команди з простими шаблонами, один вкладиш, портативний, розширюваний, щоб додати більше обмежень. Тоді я спробував поглянути на це з іншої точки зору, саме тоді я зрозумів, що можу обійтися без параметра "один вкладиш", якщо "2-лайнер" відповідає решті моїх критеріїв. Врешті-решт я придумав це рішення, яке мені подобається, що воно працює і в Ubuntu, і в Mac, яким я хотів поділитися з усіма:

insertLine=$(( $(grep -n "foo" sample.txt | cut -f1 -d: | head -1) + 1 ))
sed -i -e "$insertLine"' i\'$'\n''bar'$'\n' sample.txt

У першій команді grep шукає номери рядків, що містять "foo", "cut / head" вибирає перше виникнення, а арифметичні збільшення кроків, що перше число рядка появи на 1, оскільки я хочу вставити після виникнення. У другій команді - це місцеве редагування файлів, "i" для вставки: ansi-c, цитуючи новий рядок, "bar", потім ще один новий рядок. Результатом є додавання нового рядка, що містить "бар" після рядка "foo". Кожну з цих 2 команд можна розширити на більш складні операції та узгодження.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.