Ed的awk solution显然是走在这里的方式。
为了好玩,我试着想出一个sed解决方案,这里是(一个错综复杂的GNU sed),它将模式和脚本作为参数运行;输入可以从标准输入读取(也就是说,您可以管道输入)或从作为第三个参数提供的文件中读取。
对于你的榜样,我们不得不infile
与内容
siedi87sik65owk55dkd
siedi11sik22owk33dkd
(两行说明它如何工作的多线),然后用script
内容
#!/bin/bash
echo "!!!${1}!!!"
,最终解决方案脚本本身,so
。用法是
./so patternscript [input]
其中pattern
是一个扩展的正则表达式的GNU的sed(与-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
命令(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
我用'cmd |的结果替换'tgt'的原始值。 getline'只有在'cmd | getline'成功了,否则我会离开'tgt'它的原始值。有关如何/何时使用getline(来自管道)的详细信息,请参阅https://www.gnu.org/software/gawk/manual/gawk.html#Getline_002fPipe和http://awk.freeshell.org/AllAboutGetline。 –