Эд awk solution, очевидно, путь здесь.
Для удовольствия я попытался придумать решение sed, и вот (свернутый GNU sed), который заставляет шаблон и сценарий запускаться как параметры; вход считывается из стандартного ввода (т. е. вы можете подключиться к нему) или из файла, предоставленного в качестве третьего аргумента.
Для примера, мы должны были бы infile
с содержанием
siedi87sik65owk55dkd
siedi11sik22owk33dkd
(две строки, чтобы продемонстрировать, как это работает для нескольких линий), затем script
с содержанием
#!/bin/bash
echo "!!!${1}!!!"
и, наконец, решение сам скрипт, so
. Использование является
./so patternscript [input]
где pattern
является расширенное регулярное выражение в понимании GNU СЭД (с опцией -r
), script
это имя команды, которую вы хотите запустить для каждого матча, а по желанию input
- это имя входного файла, если вход не является стандартным входом.
Для примера, это будет
./so '[[:digit:]]{2}' script infile
или, как фильтр,
cat infile | ./so '[[:digit:]]{2}' script
с выходом
siedi!!!87!!!sik!!!65!!!owk!!!55!!!dkd
siedi!!!11!!!sik!!!22!!!owk!!!33!!!dkd
Это то, что so
выглядит следующим образом:
#!/bin/bash
pat=$1 # The pattern to match
script=$2 # The command to run for each pattern
infile=${3:-/dev/stdin} # Read from standard input if not supplied
# Use sed and have $pattern and $script expand to the supplied parameters
sed -r "
:build_loop # Label to loop back to
h # Copy pattern space to hold space
s/.*($pat).*/.\/\"$script\" \1/ # (1) Extract last match and prepare command
# Replace pattern space with output of command
e
G # (2) Append hold space to pattern space
s/(.*)$pat(.*)/\1~~~\2/ # (3) Replace last match of pattern with ~~~
/\n[^\n]*$pat[^\n]*$/b build_loop # Loop if string contains match
:fill_loop # Label for second loop
s/(.*\n)(.*)\n([^\n]*)~~~([^\n]*)$/\1\3\2\4/ # (4) Replace last ~~~
t fill_loop # Loop if there was a replacement
s/(.*)\n(.*)~~~(.*)$/\2\1\3/ # (5) Final ~~~ replacement
" < "$infile"
Команда sed работает с двумя контурами. Первая копирует пространство шаблонов в пространство удержания, а затем удаляет все, кроме последнего совпадения, из пространства шаблонов и подготавливает команду для запуска. После подстановки с (1) в своем комментарии, картина пространство выглядит следующим образом:
./script 55
Команда e
(расширение GNU), а затем заменяет шаблон пространства с выходом этой команды. После этого G
присоединяет пространство удержания к пространству рисунка (2). Узор пространство теперь выглядит следующим образом:
!!!55!!!
siedi87sik65owk55dkd
замена в (3) заменяет последний матч со строкой, надеюсь, не равный образцу, и мы получаем
!!!55!!!
siedi87sik65owk~~~dkd
повторов цикла, если последние строка пространства шаблонов по-прежнему соответствует шаблону. После трех циклов, картина пространство выглядит следующим образом:
!!!87!!!
!!!65!!!
!!!55!!!
siedi~~~sik~~~owk~~~dkd
Второй цикл заменяет теперь последний ~~~
со вторым по последней строке шаблона с заменой (4). Команда использует множество «не новой строки» ([^\n]
), чтобы убедиться, что мы не вытаскиваем неправильную замену для ~~~
.
Из команды пути (4) написано, цикл заканчивается последним замещением идти, так что перед командой (5), мы имеем этот шаблон пространство:
!!!87!!!
siedi~~~sik!!!65!!!owk!!!55!!!dkd
Command (5) является более простая версия команды (4), а после нее выход будет таким же, как требуется.
Это, кажется, довольно надежным и может иметь дело с пробелами в имени сценария будет работать до тех пор, как он правильно процитировал при вызове:
./so '[[:digit:]]{2}' 'my script' infile
Это потерпит неудачу, если
- Входной файл содержит
~~~
(разрешается путем замены всех вхождений в начале, поместив их обратно в конец)
- Выходной сигнал
script
содержит ~~~
- Шаблон содержит
~~~
т.е., решение очень сильно зависит от ~~~
, являющегося уникальным.
Потому что никто не спросил: so
как однострочника.
#!/bin/bash
sed -re ":b;h;s/.*($1).*/.\/\"$2\" \1/;e" -e "G;s/(.*)$1(.*)/\1~~~\2/;/\n[^\n]*$1[^\n]*$/bb;:f;s/(.*\n)(.*)\n([^\n]*)~~~([^\n]*)$/\1\3\2\4/;tf;s/(.*)\n(.*)~~~(.*)$/\2\1\3/" < "${3:-/dev/stdin}"
еще работы!
Что такое линия после getline? Почему вы устанавливаете tgt = line. Также почему вы должны закрыть (cmd) – Jacob
Я заменяю исходное значение 'tgt' на результат' cmd | getline', только если 'cmd | getline' преуспел, иначе я оставляю 'tgt' с его исходным значением. См. Https://www.gnu.org/software/gawk/manual/gawk.html#Getline_002fPipe и http://awk.freeshell.org/AllAboutGetline для получения подробной информации о том, как и когда использовать getline (из канала). –