sed 字边界:用命令替换命令宏

sed 字边界:用命令替换命令宏

我继承了一段代码,其中使用“命令宏”代替要运行的底层命令,例如,$TR使用 代替tr.我想用来sed将命令宏转换回它们引用的 shell 命令。问题出在单词边界上:

$ echo '$TR $TRUE $TRUNCATE "$TR ($TR' | sed s/\$TR/tr/g
tr trUE trUNCATE "tr (tr

显然是错误的。但是,当我尝试使用\b设置翻译的单词边界时,这也不起作用:

$ echo '$TR $TRUE $TRUNCATE "$TR ($TR' | sed s/\$TR\b/tr/g
$TR $TRUE $TRUNCATE "$TR ($TR

按宏长度逆序搜索(即,首先替换$TRUNCATE,然后$TRUE,然后$TR)并不是一个完整的解决方案,因为还存在诸如$TS $TS_FORMAT仅将独立的内容$TS转换为 之类的情况ts

\b单词边界标签在这里不起作用有什么原因吗?我需要做什么来解决这个问题?

$ sed --version
sed (GNU sed) 4.7
Packaged by Debian
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Jay Fenlason, Tom Lord, Ken Pizzini,
Paolo Bonzini, Jim Meyering, and Assaf Gordon.
GNU sed home page: <https://www.gnu.org/software/sed/>.
General help using GNU software: <https://www.gnu.org/gethelp/>.
E-mail bug reports to: <[email protected]>.

编辑

定义命令宏的文件包含所有被替换命令的完全限定文件名,即使 FQFN 不合适(例如,$TRUE定义为/usr/bin/truewhile type truereturns true is a shell builtin)。这会导致在引用时运行磁盘上的文件$TRUE,因此使用命令宏(在本例中)会对性能产生重大影响。

目标是更新代码库中的文件,以便命令宏可以消失,从source定义它们的 d 文件中删除,以便代码的未来读者和维护者根本不必处理命令宏。

答案1

由于您的 sed 表达式未加引号,因此您的 shell 可能会转换\b为 plain b。例如在 bash 中:

set -x

$ echo '$TR $TRUE $TRUNCATE "$TR ($TR' | sed s/\$TR\b/tr/g
+ sed 's/$TRb/tr/g'
+ echo '$TR $TRUE $TRUNCATE "$TR ($TR'
$TR $TRUE $TRUNCATE "$TR ($TR

然而

$ echo '$TR $TRUE $TRUNCATE "$TR ($TR' | sed 's/\$TR\b/tr/g'
+ sed 's/\$TR\b/tr/g'
+ echo '$TR $TRUE $TRUNCATE "$TR ($TR'
tr $TRUE $TRUNCATE "tr (tr

然而我质疑 sed 是否适合这项任务 - 也许考虑一下类似envsubstex 的东西。

$ echo '$TR $TRUE $TRUNCATE "$TR ($TR' | env TR=tr TRUE=true TRUNCATE=truncate envsubst
tr true truncate "tr (tr

答案2

对于便携式解决方案,您可以使用perl(GNUsed复制\b自的地方):

perl -pe 's/\$(?|(TR|TRUE|TRUNCATE|TS)\b|\{((?1))\})/\L$1/g'

替换中后面的小写内容\L实际上来自ex/ vi。它也可以在 GNU 实现中使用,但在其他实现sed中不是标准的,也不是可移植的。sed

  • (?1)(TR|TRUE|TRUNCATE|TS)调用第一对裸括号中包含的正则表达式,并在此处使用,以便不必在与变体匹配的交替部分中重复${VAR}
  • (?|...)是个分支重置的版本(?:...)。主要区别在于 in ,由end up 、 by in和包围的in(a(x)|b(y))匹配的内容;与, -> , -> ,一样x$2y$3(...)$1(?:a(x)|b(y))x$1y$2(?:...)分组但不是捕获,并且在 中,与或者(?|a(x)|b(y))匹配的内容最终在 中。xy$1

使用 GNU sed,您可以执行以下操作:

vars='(TR|TRUE|TRUNCATE|TS)'
sed -E "s/\\\$($vars\>|\{$vars\})/\L\2\3/g"

这里使用\>(也来自ex/ vi)而不是 perl 风格,\b以与其他\L前主义保持一致,但\b这里的工作原理是一样的。

相关内容