【问题标题】:Sed variable expansion and \n match when trying to append some text尝试附加一些文本时 Sed 变量扩展和 \n 匹配
【发布时间】:2020-04-26 04:47:40
【问题描述】:

我有以下模板文件

# 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
#

【问题讨论】:

  • 你能分享file test.txt和/或cat -vTE test.txt的输出吗
  • 您在 1) 中的第一个命令有效(至少在 GNU sed 中);我不知道你是如何得到你报告的错误信息的。问题 2) 比较棘手,因为 sed 逐行处理文件,并且一行(根据定义)不会包含换行符后跟其他字符。
  • @umusuarioqualquer:运行 dos2unix file.txt 并尝试运行您的 sed 命令
  • unknown command: 'B' 好吧,我猜这个变量有多行,而不是一行,比如REPLACE=$'something\nB something',对吧?
  • 大家好,我对问题进行了更新,问题1是通过使用真正的linux机器而不是git-bash解决的,现在唯一剩下的问题是第2点,谢谢大家帮助到现在:)

标签: linux bash shell sed


【解决方案1】:

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

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' - 换行符。

* 只是 &lt;(echo "$replace") 也可以,但不知何故我觉得 cat &lt;&lt;&lt;"$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}'

outputs on 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 一次读取一行。由于这非常相似,我只想指出 this answer 我昨天刚刚处理了同样的问题。在这种情况下,sed 中的脚本需要使用N 命令一次缓冲两行:

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
'

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

【讨论】:

  • 太棒了,你做到了,谢谢你的帮助,我永远无法想象如何做这项工作。还在学习 sed,但即使阅读 sed 文档也不是很清楚这类东西。
  • 也许sed '/# INSERT YOUR OWN RULE/!b;n;r'&lt;(cat &lt;&lt;&lt;"$replace") file 也可以?
  • @potong 是的,它应该可以工作。这很好,因为它最后不需要$'\n'
【解决方案2】:
sed "/INSERT YOUR OWN RULE/a '\n'TEXTTOAPPEND" file-for-change |
sed "/INSERT YOUR OWN RULE/{n;s/'/#/;n;s/^'//;n;d"}

第二个 sed 用于更改并删除下两行开头的“'”前导符号

或者,更简单的变体:

sed -r '/INSERT YOUR/,+1{s/(#$)/\1\nTEXTTOAPPEND/}'

它适用于 GNU sed。

【讨论】:

    【解决方案3】:

    有时最简单的方法是最好的,也许就是这样?

    sed '8aTEXTTOAPPEND' -i test.txt
    

    这将在第 8 个字符串之后添加“TEXTTOAPPEND”。

    并自动执行此操作

    N=$(grep -n 'INSERT YOUR OWN RULE' test.txt) # get line number
    N=${N%%:*}                                   # remove all except line number
    ((N++))                                      # inc line number coz we got this line with #
    sed "${N}aTEXTTOAPPEND" -i test.txt          # add text after $Nth line
    

    单线

    N=$(grep -n 'INSERT YOUR OWN RULE' test.txt); N=${N%%:*}; ((N++)); sed "${N}aTEXTTOAPPEND" -i test.txt
    

    【讨论】:

      【解决方案4】:

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

      cat <<! | sed 'N;/^# INSERT YOUR OWN RULE.*\n#/!{P;D};r /dev/stdin' file
      Text to be appended or a variable
      $var
      or both $var
      !
      

      构造一个要附加的 here 文档并将其通过管道传递给 sed 调用。 sed 调用使​​用NP;D 命令在文件的整个长度中打开一个两行窗口,但在匹配所需的两行时调用r 命令,该命令将前一个here 文档附加在此处。

      另一种选择:

      sed '1{x;s/^/'"$var"'/;x};N;/^# INSERT YOUR OWN RULE.*\n#/!{P;D};G' file
      

      但是,这也应该有效:

      sed 'N;/^# INSERT YOUR OWN RULE.*\n#/!{P;D};a\'"$var" file
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-07-02
        • 1970-01-01
        • 2013-03-30
        • 2019-12-29
        • 2020-07-04
        • 1970-01-01
        • 2021-02-17
        相关资源
        最近更新 更多