为什么sed不能取代重叠模式

时间:2011-09-14 18:23:32

标签: shell unix sed

我有一个数据库卸载文件,其字段用< TAB>分隔。字符。我通过sed运行此文件来替换< TAB>< TAB>的任何出现。 < TAB> \ N< TAB>。这样,当文件加载到MySQL时,\ N被解释为NULL。

sed命令的/ \ t \ t / \ t \ N \ t / g;'几乎可以工作,只是它只替换第一个例子,例如“...< TAB>< TAB>< TAB> ...”变为“...< TAB> \ N< TAB>< TAB> ...”。

如果我使用's / \ t \ t / \ t \ N \ t / g; s / \ t \ t \ \ t \ n \ t \ N \ t / g;'它取代了更多的实例。

我有一个想法,尽管/ g修饰符,这与一个匹配的结束是另一个匹配的开始有关。

任何人都可以解释发生了什么,并建议一个可以工作的sed命令,或者我需要循环。

我知道我可能会切换到awk,perl,python,但我想知道sed中发生了什么。

5 个答案:

答案 0 :(得分:2)

我知道你想要sed,但是sed完全不喜欢这个,它似乎特别(见here)不会做你想要的。但是,perl会这样做(AFAIK):

perl -pe 'while (s#\t\t#\t\n\t#) {}' <filename>

答案 1 :(得分:2)

作为解决方法,使用tab + \ N替换每个选项卡;然后删除所有出现的\ N,它们不会紧跟一个标签。

sed -e 's/\t/\t\\N/g' -e 's/\\N\([^\t]\)/\1/g'

...如果你的sed在分组括号之前使用反斜杠(有sed方言不需要反斜杠;如果这对你没用,请尝试不使用它们。)

答案 2 :(得分:1)

是的,即使使用/g,sed也会与它再次替换的文本不匹配。因此,它读取<TAB><TAB>并输出<TAB>\N<TAB>,然后从输入流中读取下一个内容。见http://www.grymoire.com/Unix/Sed.html#uh-7

在支持前瞻的正则表达式语言中,您可以通过前瞻来解决这个问题。

答案 3 :(得分:1)

嗯,sed只是按设计工作。输入行扫描一次,而不是多次。如果sed使用重新扫描输入行来默认处理重叠模式,可能会有所帮助:在这种情况下,即使是简单的替换也会有不同的作用 - 有些人可能会反直觉地说 - 例如< / p>

  • s/^/ /在行的开头插入一个空格永远不会终止
  • s/$/foo/将foo附加到每一行 - 同样
  • s/[A-Z][A-Z]*/CENSORED/用CENSORED替换大写单词 - 同样

可能还有很多其他情况。当然,这些都可以用替代修饰符来解决,但在设计sed时,选择了当前的行为。

答案 4 :(得分:1)

与perl解决方案没有什么不同,这适用于我使用纯sed

sed ':repeat;
     /\t\t/{
       s|\t\t|\t\n\t|g;
       b repeat
     }'

说明

  • :repeat是一个标签,用于分支命令,类似于批处理
  • /\t\t/表示匹配模式2标签。如果匹配的模式,则执行第二个/之后的命令。
  • {} - 在这种情况下,match命令后面的命令是一个组。因此,如果满足匹配模式,则执行组中的所有命令。
  • s|\t\t|\t\n\t|g; - 标准用tab-newline-tab替换2个标签。我仍然使用全局,因为如果你说15个标签,你只需要循环两次,而不是14次。
  • b repeat表示始终转到(分支)标签repeat

所以就是这样。只要匹配2个标签的模式,就不断重复(转到repeat

虽然可以说你可以做两个相同的全局替换并且称之为好,但是同样的技术可以在更复杂的场景中工作。

正如@ thorn-blake指出的那样,sed只是不支持像超前一样的高级功能,所以你需要像这样做一个循环。

短版

可以缩短为

sed ':r;/\t\t/{s|\t\t|\t\n\t|g; b r}'

的MacOS

Mac(但仍然兼容Linux / Windows)版本:

sed $':r\n/\t\t/{ s|\t\t|\t\\\n\t|g; b r\n}'
  • 标签需要在BSD sed中为文字
  • Newlines需要同时为文字和转义,因此单斜杠(在$处理之前为\,使其成为单个文字斜杠)加上\ n成为实际换行符
  • 标签名称(:r)和分支命令(b r)必须以换行符结尾。分号和空格由BSD中的标签名称/分支命令使用,这使得它非常混乱。