Regex 执行sed中由backreference定义的命令

Regex 执行sed中由backreference定义的命令,regex,bash,sed,Regex,Bash,Sed,我正在创建一个完全基于sed的原始实验模板引擎(只是为了我个人的享受)。几个小时以来,我一直在努力实现的一件事是用它们包含的命令的输出替换某些文本模式 要清除,如果输入行如下所示 Lorem {{echo ipsum}} 我将sed输出看起来如下所示: Lorem ipsum 最接近我的是: echo 'Lorem {{echo ipsum}}' | sed 's/{{\(.*\)}}/'"$(\\1)"'/g' 这是行不通的 但是, echo 'Lorem {{echo ipsum}}'

我正在创建一个完全基于sed的原始实验模板引擎(只是为了我个人的享受)。几个小时以来,我一直在努力实现的一件事是用它们包含的命令的输出替换某些文本模式

要清除,如果输入行如下所示

Lorem {{echo ipsum}}
我将sed输出看起来如下所示:

Lorem ipsum
最接近我的是:

echo 'Lorem {{echo ipsum}}' | sed 's/{{\(.*\)}}/'"$(\\1)"'/g'
这是行不通的

但是,

echo 'Lorem {{echo ipsum}}' | sed 's/{{\(.*\)}}/'"$(echo \\1)"'/g'
给我

Lorem echo ipsum
我不太明白这里发生了什么。为什么我可以将backreference提供给echo命令,但不能在$()中计算整个backreference?\\1何时得到评估?我试图用纯sed实现的目标是可能的吗

请记住,我完全清楚,我试图实现的目标很容易通过其他工具实现。然而,我对纯sed是否可以实现这一点非常感兴趣


谢谢

您的尝试不起作用的原因是,在调用
sed
之前,
$()
被shell扩展。由于这个原因,它不能使用backreferences
sed
最终将要捕获的内容

使用GNU sed(而不是POSIX sed)可以做这种事情。主要的技巧是GNU sed在
s
命令上有一个
e
标志,使它用作为shell命令执行的模式空间的结果替换模式空间(整个空间)。这意味着

echo 'echo foo' | sed 's/f/g/e'
打印
goo

这可用于您的用例,如下所示:

echo 'Lorem {{echo ipsum}}' | sed ':a /\(.*\){{\(.*\)}}\(.*\)/ { h; s//\1\n\3/; x; s//\2/e; G; s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/; ba }'
:a                                    # jump label for looping, in case there are
                                      # several {{}} expressions in a line
/\(.*\){{\(.*\)}}\(.*\)/ {            # if there is a {{}} expression,
  h                                   # make a copy of the line
  s//\1\n\3/                          # isolate the surrounding parts
  x                                   # swap the original back in
  s//\2/e                             # isolate the command, execute, get output
  G                                   # get the outer parts we put into the hold
                                      # buffer
  s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/    # rearrange the parts to put the command
                                      # output into the right place
  ba                                  # rinse, repeat until all {{}} are covered
}
sed
代码的工作原理如下:

echo 'Lorem {{echo ipsum}}' | sed ':a /\(.*\){{\(.*\)}}\(.*\)/ { h; s//\1\n\3/; x; s//\2/e; G; s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/; ba }'
:a                                    # jump label for looping, in case there are
                                      # several {{}} expressions in a line
/\(.*\){{\(.*\)}}\(.*\)/ {            # if there is a {{}} expression,
  h                                   # make a copy of the line
  s//\1\n\3/                          # isolate the surrounding parts
  x                                   # swap the original back in
  s//\2/e                             # isolate the command, execute, get output
  G                                   # get the outer parts we put into the hold
                                      # buffer
  s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/    # rearrange the parts to put the command
                                      # output into the right place
  ba                                  # rinse, repeat until all {{}} are covered
}
这使得在正则表达式中使用
sed
的贪婪匹配始终捕获行中最后一个
{{}
表达式。请注意,如果一行中有多个命令,而后面的一个命令具有多行输出,则会有困难。处理这种情况需要定义一个标记,该标记不允许嵌入在数据中的命令作为其输出的一部分,也不允许模板包含该标记。我会建议像
{{}}
这样的东西,这会导致

sed ':a /\(.*\){{\(.*\)}}\(.*\)/ { h; s//{{{}}}\1{{{}}}\3/; x; s//\2/e; G; s/\(.*\)\n{{{}}}\(.*\){{{}}}\(.*\)/\2\1\3/; ba }'
这背后的原因是,如果嵌入的命令进一步打印
{{}
术语,模板引擎无论如何都会遇到问题。这个约定是不可能强制执行的,但是无论如何,传递到这个模板引擎中的任何代码最好来自可信的源代码

请注意,我不确定这整件事是不是一个理智的想法。您不打算在任何生产代码中使用它,是吗


1但是,我很确定这是否是一个明智的想法。

您的尝试不起作用的原因是,在调用
sed
之前,shell会扩展
$()
。由于这个原因,它不能使用backreferences
sed
最终将要捕获的内容

使用GNU sed(而不是POSIX sed)可以做这种事情。主要的技巧是GNU sed在
s
命令上有一个
e
标志,使它用作为shell命令执行的模式空间的结果替换模式空间(整个空间)。这意味着

echo 'echo foo' | sed 's/f/g/e'
打印
goo

这可用于您的用例,如下所示:

echo 'Lorem {{echo ipsum}}' | sed ':a /\(.*\){{\(.*\)}}\(.*\)/ { h; s//\1\n\3/; x; s//\2/e; G; s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/; ba }'
:a                                    # jump label for looping, in case there are
                                      # several {{}} expressions in a line
/\(.*\){{\(.*\)}}\(.*\)/ {            # if there is a {{}} expression,
  h                                   # make a copy of the line
  s//\1\n\3/                          # isolate the surrounding parts
  x                                   # swap the original back in
  s//\2/e                             # isolate the command, execute, get output
  G                                   # get the outer parts we put into the hold
                                      # buffer
  s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/    # rearrange the parts to put the command
                                      # output into the right place
  ba                                  # rinse, repeat until all {{}} are covered
}
sed
代码的工作原理如下:

echo 'Lorem {{echo ipsum}}' | sed ':a /\(.*\){{\(.*\)}}\(.*\)/ { h; s//\1\n\3/; x; s//\2/e; G; s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/; ba }'
:a                                    # jump label for looping, in case there are
                                      # several {{}} expressions in a line
/\(.*\){{\(.*\)}}\(.*\)/ {            # if there is a {{}} expression,
  h                                   # make a copy of the line
  s//\1\n\3/                          # isolate the surrounding parts
  x                                   # swap the original back in
  s//\2/e                             # isolate the command, execute, get output
  G                                   # get the outer parts we put into the hold
                                      # buffer
  s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/    # rearrange the parts to put the command
                                      # output into the right place
  ba                                  # rinse, repeat until all {{}} are covered
}
这使得在正则表达式中使用
sed
的贪婪匹配始终捕获行中最后一个
{{}
表达式。请注意,如果一行中有多个命令,而后面的一个命令具有多行输出,则会有困难。处理这种情况需要定义一个标记,该标记不允许嵌入在数据中的命令作为其输出的一部分,也不允许模板包含该标记。我会建议像
{{}}
这样的东西,这会导致

sed ':a /\(.*\){{\(.*\)}}\(.*\)/ { h; s//{{{}}}\1{{{}}}\3/; x; s//\2/e; G; s/\(.*\)\n{{{}}}\(.*\){{{}}}\(.*\)/\2\1\3/; ba }'
这背后的原因是,如果嵌入的命令进一步打印
{{}
术语,模板引擎无论如何都会遇到问题。这个约定是不可能强制执行的,但是无论如何,传递到这个模板引擎中的任何代码最好来自可信的源代码

请注意,我不确定这整件事是不是一个理智的想法。您不打算在任何生产代码中使用它,是吗


1但是,我很确定这是否是一个明智的想法。

您的尝试不起作用的原因是,在调用
sed
之前,shell会扩展
$()
。由于这个原因,它不能使用backreferences
sed
最终将要捕获的内容

使用GNU sed(而不是POSIX sed)可以做这种事情。主要的技巧是GNU sed在
s
命令上有一个
e
标志,使它用作为shell命令执行的模式空间的结果替换模式空间(整个空间)。这意味着

echo 'echo foo' | sed 's/f/g/e'
打印
goo

这可用于您的用例,如下所示:

echo 'Lorem {{echo ipsum}}' | sed ':a /\(.*\){{\(.*\)}}\(.*\)/ { h; s//\1\n\3/; x; s//\2/e; G; s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/; ba }'
:a                                    # jump label for looping, in case there are
                                      # several {{}} expressions in a line
/\(.*\){{\(.*\)}}\(.*\)/ {            # if there is a {{}} expression,
  h                                   # make a copy of the line
  s//\1\n\3/                          # isolate the surrounding parts
  x                                   # swap the original back in
  s//\2/e                             # isolate the command, execute, get output
  G                                   # get the outer parts we put into the hold
                                      # buffer
  s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/    # rearrange the parts to put the command
                                      # output into the right place
  ba                                  # rinse, repeat until all {{}} are covered
}
sed
代码的工作原理如下:

echo 'Lorem {{echo ipsum}}' | sed ':a /\(.*\){{\(.*\)}}\(.*\)/ { h; s//\1\n\3/; x; s//\2/e; G; s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/; ba }'
:a                                    # jump label for looping, in case there are
                                      # several {{}} expressions in a line
/\(.*\){{\(.*\)}}\(.*\)/ {            # if there is a {{}} expression,
  h                                   # make a copy of the line
  s//\1\n\3/                          # isolate the surrounding parts
  x                                   # swap the original back in
  s//\2/e                             # isolate the command, execute, get output
  G                                   # get the outer parts we put into the hold
                                      # buffer
  s/\(.*\)\n\(.*\)\n\(.*\)/\2\1\3/    # rearrange the parts to put the command
                                      # output into the right place
  ba                                  # rinse, repeat until all {{}} are covered
}
这使得在正则表达式中使用
sed
的贪婪匹配始终捕获行中最后一个
{{}
表达式。请注意,如果一行中有多个命令,而后面的一个命令具有多行输出,则会有困难。处理这种情况需要定义一个标记,该标记不允许嵌入在数据中的命令作为其输出的一部分,也不允许模板包含该标记。我会建议像
{{}}
这样的东西,这会导致

sed ':a /\(.*\){{\(.*\)}}\(.*\)/ { h; s//{{{}}}\1{{{}}}\3/; x; s//\2/e; G; s/\(.*\)\n{{{}}}\(.*\){{{}}}\(.*\)/\2\1\3/; ba }'
这背后的原因是,如果嵌入的命令进一步打印
{{}
术语,模板引擎无论如何都会遇到问题。这个约定是不可能强制执行的,但是无论如何,传递到这个模板引擎中的任何代码最好来自可信的源代码

请注意,我不确定这整件事