Bash sed-用文件内容替换模式,而文件名是模式本身

Bash sed-用文件内容替换模式,而文件名是模式本身,bash,sed,Bash,Sed,从第一个开始,我有另一个。如果我成功了,我可以删除几行脚本:D 我想转换这一行: sed -i -r -e "/$(basename "$token_file")/{r $token_file" -e "d}" "$out_dir_rug"/rug.frag sed -i -r -e "/(##_[_a-zA-Z0-9]+_##)/{r $out_dir_frags_rug/\1" -e &qu

从第一个开始,我有另一个。如果我成功了,我可以删除几行脚本:D

我想转换这一行:

sed -i -r -e "/$(basename "$token_file")/{r $token_file" -e "d}" "$out_dir_rug"/rug.frag
sed -i -r -e "/(##_[_a-zA-Z0-9]+_##)/{r $out_dir_frags_rug/\1" -e "d}" "$out_dir_rug"/rug.frag
进入这一行:

sed -i -r -e "/$(basename "$token_file")/{r $token_file" -e "d}" "$out_dir_rug"/rug.frag
sed -i -r -e "/(##_[_a-zA-Z0-9]+_##)/{r $out_dir_frags_rug/\1" -e "d}" "$out_dir_rug"/rug.frag
想法如下。最初在第一行中,我搜索了一些模式,然后用它们的关联文件替换了这些模式。文件名就是模式本身

示例:

图案:(ufoo)_

文件名:_foo_

文件内容foo_2;

那么文本呢

bar
##_foo_##
bar
将成为

bar
first line of foo text
second line of foo text
bar
在第二次尝试中,我使用sed来定位模式,并进行实际替换

结果是找到了模式,但几乎什么都没有替换

sed应该能做我想要的替换吗?如果是,我应该如何更改命令

注意:一个文件通常有几个不同的模式,我称之为令牌,同一个模式可能出现多次

因此,输入文件可能如下所示:

bar
bar
##_foo_##
bar
##_haa_##
bar
##_foo_##
and so on
我已尝试将地址中的/替换为,但没有任何有用的结果。在\/的路径中转义/也没有帮助

我通过在sed之前添加下一行来验证替换文件的路径是否正确:

文件名就是模式本身

如果您需要任何动态的东西,那么sed是不够的。因为sed不能执行eval-不能将模式缓冲区的内容或hold buffer重新解释为令人惊讶的命令不能将该行用作命令的一部分*

您可以使用bash,未经测试,在此处编写:

while IFS= read -r line; do
    if [[ "$line" =~ ^##_([_a-zA-Z0-9]+)_## ]]; then
       cat "${BASH_REMATCH[1]}"
    else
       printf "%s\n" "$line"
    fi
done < inputfile
也就是说,对于您的特定问题,与其重新发明轮子,编写另一个模板和预处理工具,不如集中精力研究现有的解决方案。具有以下内容的简单cpp文件可以使用C预处理器进行预处理:

bar
bar
#include "foo"
bar
#include "haa"
bar
#include "foo"
and so on
任何人都清楚它的含义,它有一个非常标准化的格式,你还可以得到所有你可以使用的ifdef条件表达式、宏和宏函数——但是你不能以它们开头,不知道这是否重要。对于无止境的终极模板功能,我建议使用标准unix命令中的m4

*但是,您可以使用GNU sed在shell中使用e标志执行s命令中替换字符串的内容。我在写这个答案的时候确实忘记了这一点,因为它很少被使用,我强烈建议不要使用e标志-找到子shell的正确引用是不可能的?而且很容易滥用它。无论如何,以下方法可能有效:

sed -n '/^##_\(.*\)_##$/!{p;n;}; s//cat \1/ep'
但使用以下输入可能会对您的系统造成危害:

some input file
##_$(rm /)_##
   ^^^^^^^ - will be executed in subshell and remove all your files
我认为适当的引用是未经测试的:

awk '/^##_[_a-zA-Z0-9]+_##$/{
        gsub(/^##_/, "", $0);
        gsub(/_##$/, "", $0);
        file = $0
        while (getline tmp < (file)) print tmp;
        next
     }
     {print}
' inputfile
sed -n '/^##_\(.*\)_##$/!{p;n;}; s//\1/; '"s/'/'\\\\''/g; p; s/.*/cat '&'/ep"
但我还是会使用现有的工具,如cpp或m4。

使用sed 是的,这在GNU sed中是可能的

使用此输入文件input.txt:

还有你在问题中给出的文件,命令

sed -E '
    /^##_[_a-zA-Z0-9]+_##$/ {
        s|^|cat ./|
        e
    }
' input.txt
。。。将产生:

= bar =
first line of foo text
second line of foo text
= bar2 =
first line of foo text
second line of foo text
= bar3 =
此命令也可以缩短为一行:

sed -E '/^##_[_a-zA-Z0-9]+_##$/ s|^|cat ./|e' input.txt
awk '! /^##_[_a-zA-Z0-9]+_##$/ || system("cat ./" $0)' input.txt
解释 gnused有一个特殊的命令e,它执行在模式空间中找到的命令,然后用命令的输出替换模式空间的内容

当上面的程序遇到与您的模式文件匹配的行时,它将cat./前置到模式空间,并用e执行它

s/../../../e命令是一个缩短版本,其作用完全相同,只有在成功进行替换时才执行该命令

与KamilCuk在回答中所说的相反,上面的两个sed命令都是完全安全的,不需要任何转义/引用,因为它们是按照已知的无害模式执行的,不能被欺骗来执行预期的cat以外的任何操作

当然,这是为了与您在问题中给出的_文件u模式配合使用而设计的。在模式中允许空格或其他奇特的字符可能会破坏一些东西,因为它们可能会被shell解释

带着awk 以下是awk的等效值:

此命令也可以缩短为一行:

sed -E '/^##_[_a-zA-Z0-9]+_##$/ s|^|cat ./|e' input.txt
awk '! /^##_[_a-zA-Z0-9]+_##$/ || system("cat ./" $0)' input.txt
解释 这与上面的sed命令非常相似:当awk遇到模式_文件u时,它构建相应的cat命令并使用系统执行它,然后使用next跳转到下一个输入行。与图案不匹配的行将按1行打印


当然,对于由shell解释的命令,同样的警告也适用于此:两个awk命令都是完全安全的,只要模式保持如此简单,就不需要任何转义/引用。

文件名就是模式本身。那么就不要使用sed。使用awk。或者python。或任何其他模板解决方案。感谢您的回答。塞德对我剧本的这一小改动感到窒息,这让我很不高兴。在开始学习awk之前,我将尝试遵循您的建议并使用grep+sed,因为我已经开始学习了
熟悉这些工具,尽管我还需要学习很多。Neee,learnawk,它非常简单,功能强大。你可以在awk中做你在grep和sed以及Looooot中可以做的事情。也许在下一集中,在我有工作之后。不过,谢谢你的建议;«sed无法执行评估-无法将模式缓冲区或hold buffer的内容重新解释为令人惊讶的命令!»... 好吧,准备好惊讶吧,因为GNU sed可以做到这一点:-当然,你可以做sed的/*/sed&/e,但这是不可靠的,引用几乎不可能处理。而sed并不是为了这个。陛下让我们把这一点添加到答案中。