【问题标题】:Sed error: 'unescaped newline inside substitute pattern' and 'bad flag in substitute command'Sed 错误:“替代模式内未转义的换行符”和“替代命令中的错误标志”
【发布时间】:2012-12-18 09:40:32
【问题描述】:

问题:

我是sed新手,所以当我在shell中执行以下代码时:

sed -e "/^\s<key>CHANNEL_NAME<\/key>$/{N;s/\(^\s<string>\).+\(<\/string>$\)/\1test\2}" Info.plist > test.plist

Sed 给我一个错误:“sed: 1: "/^\sCHANNEL_NAME

我的问题:“替代模式内未转义的换行符”到底是什么意思?

Info.plist 文件是这样的:

...
<key>CHANNEL_NAME</key>
<string>App Store</string>
...

感谢大家回答问题,谢谢!


回答者:

感谢@potong @dogbane @Beta! :)

因为它是一个 Cocoa plist,所以这是我的最终解决方案:

sed '/<key>CHANNEL_NAME<\/key>/{N;s/\(<string>\).*\(<\/string>\)/\1test\2/;}' Info.plist > test.plist

提示:

  1. 我在解决问题的过程中遇到了两个错误。把它们放在这里:
    • sed: 1: "/^\sCHANNEL_NAME
    • sed: 1: "/CHANNEL_NAME...": 替换命令中的错误标志:'}'
  2. 我在第一个代码中犯了很多错误。
    • 没有逃脱'+'
    • 应该以 2/}" 结尾
    • 实际上应该以 2/;}" 结尾(我错过了一个 ';',所以我在提示 1 中遇到了第二个错误)
  3. 用户“n”或“N”都适用于我。
  4. 可能是因为在 Mac 上,'.+'(即使我逃脱了)不起作用,所以必须按照@potong 所说的那样更改它,'..*'

欢迎任何好的建议来批准代码,再次感谢以下所有人!

【问题讨论】:

  • 我无法重现此错误。但是您的 sed 表达式还有其他问题。您还没有逃脱 + 并且缺少一个斜杠来终止 s 命令,即它应该以 2/}" 结尾。
  • @dogbane 非常感谢。这是一个错误的输入,但问题不是表达。恩,你的操作系统是什么? Linux 还是 Mac?
  • 可以重现错误(在 Mac 上),当我更正 + 并使用 2/;} 终止命令时,替换工作。

标签: regex bash shell sed plist


【解决方案1】:

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

sed -e '/^\s*<key>CHANNEL_NAME<\/key>$/{n;s/^\(\s*<string>\).\+\(<\/string>\)$/\1test\2/}' Info.plist > test.plist

注意您应该在行的开头允许空格 (^\s*) 并在比较替换命令的下一行的开头之前打印匹配的行,即使用 n 而不是 N

或者:

sed -e '/^ *<key>CHANNEL_NAME<\/key>$/!b' -e 'n' -e 's/^\( *<string>\)..*\(<\/string>\)$/\1test\2/' Info.plist > test.plist

【讨论】:

  • 我尝试了您的建议,但仍然无法做到。我认为这可能是因为 Mac OS,但不确定。感谢所有的保存
  • @kimimaro 我添加了一个更通用的解决方案(删除了\s\+ 元字符)。但是,这不考虑标签。
  • 你的回答很有用!因为我的上下文是一个可可plist,所以可能最接近的答案是我自己的问题之后的答案,但你已经解决了我的问题。谢谢!
【解决方案2】:

既然您说您只是在学习 sed:sed 是一个出色的工具,可以在单行上进行简单替换,但对于其他任何事情,只需使用 awk。

这是一个 GNU awk 解决方案(如果你愿意,你可以把它塞进一行):

$ cat file
...
foo
<key>CHANNEL_NAME</key>
<string>App Store</string>
...
$
$ awk '
   found { $0=gensub(/(<string>).*(<\/string>)/,"\\1test\\2",""); found=0 }
   /<key>CHANNEL_NAME<\/key>/ { found=1 }
   { print $0 }
' file
...
foo
<key>CHANNEL_NAME</key>
<string>test</string>
...

它看起来与 sed 解决方案没有太大区别,但只需尝试修改 sed 解决方案来做 任何事情 额外的,例如在输出中添加行号:

$ awk '
   found { $0=gensub(/(<string>).*(<\/string>)/,"\\1test\\2",""); found=0 }
   /<key>CHANNEL_NAME<\/key>/ { found=1 }
   { print NR, $0 }
' file
1 ...
2 foo
3 <key>CHANNEL_NAME</key>
4 <string>test</string>
5 ...

或将“字符串”之间的文本替换为 CHANNEL_NAME 之前的行的内容,而不是硬编码的“测试”:

awk '
   found { $0=gensub(/(<string>).*(<\/string>)/,"\\1" rep "\\2",""); found=0 }
   /<key>CHANNEL_NAME<\/key>/ { found=1; rep=prev }
   { print $0; prev=$0 }
' file
...
foo
<key>CHANNEL_NAME</key>
<string>foo</string>
...

你会发现你需要一个完全不同的解决方案,可能涉及单个字母和标点符号的噩梦般的混合,而使用 awk 是一个简单的调整来增强你的初始解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-06
    • 1970-01-01
    • 2016-09-28
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 1970-01-01
    • 2018-08-19
    相关资源
    最近更新 更多