首页 > 解决方案 > 尝试附加一些文本时 Sed 变量扩展和 \n 匹配

问题描述

我有以下模板文件

# We strongly recommend the following be uncommented to protect innocent
# web applications running on the proxy server who think the only
# one who can access services on "localhost" is a local user
#http_access deny to_localhost

#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#



# Example rule allowing access from your local networks.
# Adapt localnet in the ACL section to list your (internal) IP networks
# from where browsing should be allowed
#http_access allow localnet
#http_access allow localhost

我想使用 sed 在下面的行后面附加一些文本(这是一个要求,我不能使用其他工具)

# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#

我尝试使用以下命令:

sed "/# INSERT YOUR OWN RULE/aTEXTTOAPPEND" test.txt

结果:

#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
TEXTTOAPPEND
#

旧代码使用 perl 来完成这项工作

perl -i -0pe 's/(#( |\t)*\n# INSERT YOUR OWN RULE.*\n#( |\t)*\n)/\1\n'"$REPLACE"'\n/g' /etc/squid/squid.conf

我面临两个问题:

1 - 无法在此附加命令中使用变量

到目前为止我尝试过:

$ echo $REPLACE
TEXTTOAPPEND

$ sed "/# INSERT YOUR OWN RULE/a${REPLACE}" test.txt
sed: -e expression #1, char 53: unknown command: `B'

sed "/# INSERT YOUR OWN RULE/a\${REPLACE}" test.txt
#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
${REPLACE}
#

2 - 在我想附加文本的行之后无法匹配哈希字符(当我添加 \n 命令停止匹配时,因此在这种情况下不添加任何内容)

sed "/# INSERT YOUR OWN RULE.*\n#/aTEXTTOAPPEND" test.txt
#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#

预期输出:

#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#
TEXTTOAPPEND

有人可以指出我在这里缺少什么吗?对于变量扩展,我认为使用双引号和 ${var} 就可以了。


更新:

我正在使用 git-bash 尝试一切,在真正的 linux 机器上尝试时,下面的命令起作用了:

echo $REPLACE
ABC

sed "/# INSERT YOUR OWN RULE.*/a${REPLACE}" test.txt
#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
ABC
#

现在唯一的问题是 2. 在这种情况下如何使用 \n ?

sed "/# INSERT YOUR OWN RULE.*\n#.*/a${REPLACE}" test.txt
#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#

标签: linuxbashshellsed

解决方案


有人可以指出我在这里缺少什么吗?

中的命令sed由换行符分隔。Sed 看到一个换行符 - 它假定命令在这里结束。我可以sed: -e expression #1, char 53: unknown command: 'B'使用具有换行符的简单变量重现您的内容,下一行以B

replace="something
B something"
sed "/# INSERT YOUR OWN RULE/a${replace}"

sed看到asomething并附something加到输出和换行符终止a命令。然后它看到B something并尝试将其解析为命令,但B无效。

使用 sed 附加变量内容的最安全方法是使用带有r命令的临时文件。请注意,您需要r在文件名之后的命令后加上换行符,因为任何;内容都将被解析为文件名的一部分!在 bash 中,您可以很聪明地将进程替换与此处的字符串结合起来创建一个临时文件描述符*。在 sed 使用n命令中输出当前模式空间并将下一行读入模式空间。像这样:

sed '/# INSERT YOUR OWN RULE/{n;r'<(cat <<<"$replace")$'\n}'

它将replace在正则表达式之后的下一行之后附加内容。请注意,之后r$'\n'- 换行符。

* 只是 a<(echo "$replace")也可以,但不知何故我觉得cat <<<"$replace"大字符串的内存消耗会更好,我没有以任何方式检查。

以下脚本:

replace="anything
can
be here!"

cat <<EOF |
#http_access deny to_localhost

#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#

# Example rule allowing access from your local networks.
EOF
sed '/# INSERT YOUR OWN RULE/{n;r'<(cat <<<"$replace")$'\n}'

repl 上的输出

#http_access deny to_localhost

#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#
anything
can
be here!

# Example rule allowing access from your local networks.

在这种情况下如何使用 \n ?

Sed 一次读取一行。由于这非常相似,我只想指出我昨天刚做的这个答案,它处理了同样的问题。N在这种情况下,sed 中的脚本需要使用命令一次缓冲两行:

sed '
  : restart
  N # buffer two lines
  : loop
      # match two lines
      /# INSERT YOUR OWN RULE.*\n#/{
           r'<(cat <<<"$replace")'
           # print and start over
           n ; b restart
       }
       # hold, print leading line, change, remove leading line
       h ; s/\n.*// ; p ; x ; s/[^\n]*\n//
       # append next line and loop
       N
  b loop
'

但是你不能做rsomethingor asomethingwithsed -z因为这样记录会被零分隔,所以 sed 读取整个文件,所以asomething会显示在整个文件之后。好吧,您可以对其进行测试sed -z '/# INSERT YOUR OWN RULE.*\n#.*/r'<(cat <<<$replace),它只会$replace在文件末尾打印 的内容。


推荐阅读