每次出现模式时(在本例中为2位数字的情况)我想将该模式传递给脚本并用脚本输出替换该模式。
我正在使用sed一个它应该是什么样子的例子
echo 'siedi87sik65owk55dkd' | sed 's/[0-9][0-9]/.\/script.sh/g'
现在返回
siedi./script.shsik./script.showk./script.shdkd
但我希望它返回
siedi!!!87!!!sik!!!65!!!owk!!!55!!!dkd
这就是./script.sh
#!/bin/bash
echo "!!!$1!!!"
必须用输出替换它。在这个例子中,我知道我可以使用正常的sed替换,但我不希望这作为答案。
答案 0 :(得分:3)
sed用于单个行上的简单替换,即全部。任何其他东西,即使它可以完成,也需要神秘的语言结构,这些结构在20世纪70年代中期被发明时已经过时,当时awk被发明并且今天纯粹用于心理锻炼。你的问题不是简单的替代,所以你不应该尝试使用sed来解决它。
你想要的东西如下:
awk '{
head = ""
tail = $0
while ( match(tail,/[0-9]{2}/) ) {
tgt = substr(tail,RSTART,RLENGTH)
cmd = "./script.sh " tgt
if ( (cmd | getline line) > 0) {
tgt = line
}
close(cmd)
head = head substr(tail,1,RSTART-1) tgt
tail = substr(tail,RSTART+RLENGTH)
}
print head tail
}'
e.g。使用echo
代替script.sh
命令:
$ echo 'siedi87sik65owk55dkd' |
awk '{
head = ""
tail = $0
while ( match(tail,/[0-9]{2}/) ) {
tgt = substr(tail,RSTART,RLENGTH)
cmd = "echo !!!" tgt "!!!"
if ( (cmd | getline line) > 0) {
tgt = line
}
close(cmd)
head = head substr(tail,1,RSTART-1) tgt
tail = substr(tail,RSTART+RLENGTH)
}
print head tail
}'
siedi!!!87!!!sik!!!65!!!owk!!!55!!!dkd
答案 1 :(得分:2)
Ed awk solution显然是去这里的方式。
为了好玩,我尝试提出一个sed解决方案,这里是一个(一个复杂的GNU sed),它采用模式和脚本作为参数运行;输入可以从标准输入读取(即,您可以管道输入)或从作为第三个参数提供的文件中读取。
对于您的示例,我们的内容为infile
siedi87sik65owk55dkd
siedi11sik22owk33dkd
(两行演示多行的工作方式),然后是script
内容
#!/bin/bash
echo "!!!${1}!!!"
最后解决方案脚本本身so
。用法是
./so pattern script [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
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}"
仍然有效!
答案 2 :(得分:0)
概念上更简单的多用途解决方案:
使用 GNU 实用程序:
sed
使用 BSD 实用程序(也适用于GNU实用程序):
"
我们的想法是使用$
将词典上感兴趣的标记转换为包含shell命令替换的字符串,该字符串使用标记调用目标脚本,然后将结果传递给shell进行评估。
注意:
输入中的所有嵌入式\
和xargs -d'\n'
字符必须为tr '\n' '\0'
- 转义。
xargs -0
(GNU)和echo 'siedi87sik65owk55dkd' |
sed 's|[0-9]\{2\}|$(./script.sh &)|g' | tr '\n' '\0' |
xargs -I% sh -c 'printf "%s\n" '\"%\"
/ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.yardbird.justice.yardbird.Fragments.DrinksFragment">
(BSD)只需要在输入中正确保留空格 - 如果是不需要,以下符合POSIX标准的解决方案将:
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="47dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>