Sed将引用插入命令

时间:2015-07-08 13:11:35

标签: sed

我可以使用sed命令中的匹配组,用于生成替换的另一个命令。这样的事情:

sed -e 's/\(<regex>\)/$(<command using \1 reference and generating replacement>)/g'

我需要在第一个文件中替换它,根据另一个文件内容(替换不是常数并基于具体的替换行)。

2 个答案:

答案 0 :(得分:2)

正如@EtanReisner所提到的,这只有GNU sed才有可能 - 而且仍然有些棘手。此外,有潜在危险,,只有在输入来自可信赖的来源时才应使用它。

无论如何,e命令的s///修饰符在将替换作为shell命令,运行它之后处理模式空间的内容,并用该输出替换模式空间。命令,这意味着必须手动将输出分流到位。一般的模式是

sed '/regex/ { h; s//\n/; x; s//\n&\n/; s/.*\n\(.*\)\n.*/command \1/e; x; G; s/\([^\n]*\)\n\([^\n]*\)\n\(.*\)/\1\3\2/ }' filename

让我们从顶部开始讨论:

/regex/ {                                  # When we find what we seek:

  h                                        # Make a copy of the current line in
                                           # the hold buffer.

  s//\n/                                   # Put a newline where the match occurs
                                           # (// reattempts the last attempted
                                           # regex, which is the one from the
                                           # start). This serves as a marker
                                           # where the output of the command will
                                           # be inserted.

  x                                        # Swap the copy back in; the marked
                                           # line moves to the hold buffer

  s//\n&\n/                                # put markers around the match this
                                           # time,

  s/.*\n\(.*\)\n.*/command \1/e            # then use those markers to construct
                                           # the command and run it. The pattern
                                           # space contains the output of the
                                           # command now.

  x                                        # swap the marked line back in

  G                                        # append the output to it

  s/\([^\n]*\)\n\([^\n]*\)\n\(.*\)/\1\3\2/ # split, reassemble all that in
                                           # the right order, using the
                                           # newline marker we put there in
                                           # the beginning as a splitting
                                           # point.
}
显然,

regexcommand必须替换为你的正则表达式和命令。您可以尝试使用

echo 'foo /tmp/ bar' | sed '/\/\S*/ { h; s//\n/; x; s//\n&\n/; s/.*\n\(.*\)\n.*/ls \1/e; x; G; s/\([^\n]*\)\n\([^\n]*\)\n\(.*\)/\1\3\2/ }'

这将运行ls /tmp/并将列表放在foobar之间。

答案 1 :(得分:1)

您可能会发现使用awk更简单,更清晰。例如将输入中间的某个数字乘以3:

$ echo 'abc 12 def' |
    awk 'match($0,/[0-9]+/) {print substr($0,1,RSTART-1) substr($0,RSTART,RLENGTH)*3 substr($0,RSTART+RLENGTH)}'
abc 36 def

使用GNU awk,您可以使用第3个arg匹配()来保存正则表达式匹配段:

$ echo 'abc 12 def' |
    awk 'match($0,/(.* )([0-9]+)( .*)/,a){print a[1] a[2]*3 a[3]}'
abc 36 def

或将它传递给shell命令(可能不是一个好主意,但可以完成):

$ echo 'abc 12 def' |
    awk 'match($0,/(.* )([0-9]+)( .*)/,a){system("echo \"" a[2] "\"")}'
12