如何在sed替换组捕获中使用命令?

时间:2018-07-21 18:48:08

标签: bash sed

我在内容中有一个像这样的文件: #INCLUDE<~/boulou/billy.txt>

从bash / sed脚本/命令中,我想用文件~/boulou/billy.txt

的内容替换这一行。

这是我当前可以找到文件路径的命令:

sed -E "s/#INCLUDE\<(.*)\>/\1/g" test.sh

这向我显示了文件路径,但是当我想用这个来获取内容时:

sed -E "s/#INCLUDE\<(.*)\>/$(cat \1)/g" test.sh

我收到错误消息“ cat:1:没有这样的文件或目录”

让我知道是否需要更多信息。

5 个答案:

答案 0 :(得分:3)

您的代码不起作用,因为它首先运行cat \1(与cat 1相同),将输出插入sed命令,然后运行sed -E "s/...//g" test.sh(空替换,因为cat 1在stdout上不输出任何内容。

这是因为shell首先处理$( ... ),然后运行生成的命令行。

对于您要执行的操作,您必须从搜索/替换命令内部读取文件,而不是从前读取。

我不知道如何用sed做到这一点,所以我只使用Perl:

perl -pe 's{#INCLUDE<([^<>]*)>}{ open my $fh, "<", $1 or die "$1: $!"; local $/; readline $fh }eg' test.sh

但是,这不适用于您的示例,因为~/boulou/billy.txt不存在(您很可能在当前工作目录中没有一个名为~的目录)。要解决此问题(并大大简化代码),我将使用:

perl -MPath::Tiny -pe 's{#INCLUDE<([^<>]*)>}{ path($1)->slurp }eg' test.sh

但是,这需要Path::Tiny模块,它不是核心perl发行版的一部分。

可以手动扩展~,但这会使代码更加混乱(这是我考虑将其放在单独的脚本文件中的地方):

perl -pe 's{#INCLUDE<([^<>]*)>}{ my $p = $1; $p =~ s{^~/}{$ENV{HOME}/}; open my $fh, "<", $p or die "$p: $!"; local $/; readline $fh }eg' test.sh

(免责声明:有些hacky,不处理~user表示法,仅用~/环境变量的内容替换前导HOME。)

答案 1 :(得分:2)

您可以尝试类似的方法:

$ sed -r 's/#INCLUDE<(.*)>/printf "%b" "$(cat \1)"/e' test.sh

它应该工作得很流畅:)

答案 2 :(得分:1)

这可能对您有用(GNU sed):

sed 's/^#INCLUDE<\(.*\)>$/cat \1/e' file

这将计算替换命令右侧的表达式。在这种情况下,它将用<>之间命名的文件内容替换匹配的行。

答案 3 :(得分:0)

这里的问题是外壳程序正在尽早替换$(cat \ 1)。也就是说,即使在执行sed命令并传递该参数之前,shell也会进行插值。

如果仅执行#INCLUDE替换,则可以考虑使用m4。如果您需要自己编写Awk,可能是解决此问题的一种合理方法。

答案 4 :(得分:0)

这将与在任何UNIX系统上的任何shell中运行任何awk的任何输入文件内容牢固地协同工作:

输入:

$ cat file1
before
foo [#INCLUDE<file2>] bar
after

$ cat file2
stuff
&       nonsense
    here

脚本:

$ cat tst.awk
match($0,/#INCLUDE<[^<>]*>/) {
    file = substr($0,RSTART+9,RLENGTH-10)
    rep = sep = ""
    while ( (getline line < file) > 0 ) {
        rep = rep sep line
        sep = ORS
    }
    $0 = substr($0,1,RSTART-1) rep substr($0,RSTART+RLENGTH)
}
{ print }

输出:

$ awk -f tst.awk file1
before
foo [stuff
&       nonsense
    here] bar
after

the currently accepted answer将在相同的输入下执行以下操作:

$ sed -r 's/#INCLUDE<(.*)>/printf "%s" $(cat \1)/e' file1
before
/bin/sh: foo: command not found

after

这是如果INCLUDE自己一行的话会做的事情:

$ cat file1
before
#INCLUDE<file2>
after

$ sed -r 's/#INCLUDE<(.*)>/printf "%s" $(cat \1)/e' file1
before
stuff&nonsensehere
after

如果INCLUDE位于其自己的一行上,则此更简单的脚本将更接近于纠正:

$ sed -r 's/#INCLUDE<(.*)>/cat \1/e' file1
before
stuff
&       nonsense
    here
after

但是如果行上还有其他任何文本也会失败,因为它仍然会尝试执行它!