我有一些文件驻留在 Linux 系统上,其中包含一些占位符,例如下面的文件:
测试.txt:
This is a line with <VARIABLE1>@<VARIABLE2>.
This is a line with <VARIABLE3>.
This is a line with <VARIABLE_UNKNOWN>.
This is another line contains a<b.
我想更改此文件,如下所示:
This is a test line with $VARIABLE1@$VARIABLE2.
This is a test line with $VARIABLE3.
This is a test line with $VARIABLE_UNKNOWN.
This is another line contains a<b.
请注意,所有这些以 结尾的变量<>
仅包含大写字母、数字和下划线。
我本可以使用以下方法,但a<b
变成了a$b
。
file_contents=$(<$file_path)
file_contents=${file_contents//</$}
file_contents=$(echo "$file_contents" | tr -d '>')
echo "$file_contents" > test.txt
我试图避免sed
使用变得非常难以调试的复杂命令。我怎样才能实现这个目标?
答案1
由于这是对各行的简单替换,因此执行此操作的 sed 命令并不复杂:
$ sed -E 's/<([[:upper:][:digit:]_]+)>/$\1/g' file
This is a line with $VARIABLE1@$VARIABLE2.
This is a line with $VARIABLE3.
This is a line with $VARIABLE_UNKNOWN.
This is another line contains a<b.
或者如果您的 sed 没有-E
任何 POSIX sed:
sed 's/<\([[:upper:][:digit:]_]\{1,\}\)>/$\1/g' file
如果您的 sed 甚至不兼容 POSIX 那么买一个新的但对于这个特定的事情你可能会做:
sed 's/<\([A-Z0-9_][A-Z0-9_]*\)>/$\1/g' file
作为@chrisdavies 指出,但这可能不是您真正应该尝试获得的输出,因为它会变成<VAR1>27
,$VAR127
您应该真正瞄准其中任何一个:
$ sed -E 's/<([[:alnum:]_]+)>/${\1}/g' file
This is a line with ${VARIABLE1}@${VARIABLE2}.
This is a line with ${VARIABLE3}.
This is a line with ${VARIABLE_UNKNOWN}.
This is another line contains a<b.
$ sed -E 's/<([[:alnum:]_]+)>/"${\1}"/g' file
This is a line with "${VARIABLE1}"@"${VARIABLE2}".
This is a line with "${VARIABLE3}".
This is a line with "${VARIABLE_UNKNOWN}".
This is another line contains a<b.
取决于您计划如何处理该输出。
FWIW 通常,当人们在文本中有占位符字符串时,他们不会将其替换为名字shell 变量,他们将它们替换为内容shell 变量,因为否则当您可以首先使用 shell 变量名称创建文本时,在文本中使用占位符有什么意义呢?
答案2
如果问题可以表述为“我想转换所有包含在其中< >
且仅由一个或多个大写字母、数字和下划线组成的字符串,这些字符串包含在< >
它们自身中,但用 a$
代替< >
”,那么您可以这样做:
$ perl -pe 's/<([A-Z\d_]+)>/\$$1/g' file
This is a line with $VARIABLE1@$VARIABLE2.
This is a line with $VARIABLE3.
This is a line with $VARIABLE_UNKNOWN.
This is another line contains a<b.
这-pe
意味着“逐行读取输入文件,应用给出的脚本-e
,然后打印每一行”。然后,我们将替换运算符 ( s/OLD/NEW/
) 与g
标志一起使用,以便它替换该行中的所有匹配项。最后,正则表达式查找<
后跟一个或多个(+
指一个或多个)大写 ASCII 字母 ( A-Z
)、ASCII1 十进制数字 ( \d
) 或_
以 结尾的下划线 ( ) >
。现在,因为模式位于括号 ( ) 中,所以我们已“捕获”它并可以在替换中([A-Z\d_]+)
引用它。$1
因此,我们将匹配的内容替换为 a $
(需要转义为\$
),然后替换匹配的内容:\$$1
。
至少只要$PERL_UNICODE
未设置环境变量,在这种情况下它就可以匹配其他类型的十进制数字。使用该a
标志给s///
操作符保证只有0123456789被匹配,或者替换\d
为0-9
or0123456789
答案3
使用乐(以前称为 Perl_6)
~$ raku -pe 's:g/ \< ( <[A..Z0..9_]>+ ) \> /\$$0/;' file
再说一遍,这几乎是 @terdon 优秀 Perl 答案的直接翻译。
在乐,
- 正则表达式修饰符(例如
g
lobal)位于匹配器的头部,前面带有冒号,以提供s:g///
“替代全局”形式。 <[ ... ]>
在识别域内,使用尖括号包围的方括号创建自定义字符类。方括号单独用于将正则表达式原子和/或属性分组在一起(参见底部的示例)。- 字符范围用
..
双点指定,如 (例如)A..Z
或0..9
。 - Raku 简化了转义规则,使得所有非字母数字非下划线字符必须加引号或(反斜杠)转义才能被理解为正则表达式中的文字,因此需要在识别域中使用
\<
and 。\>
- 编号捕获在 Raku 中通过括号指定(与 Perl 中相同),但是这些编号捕获从替换域开始
$0
并且必须在替换域中使用。 - OP 指的是错误的替换,并且通常要求反斜杠某些字符可能会出现问题。为了提供帮助,Raku 允许
{...}
在替换域中使用代码块,以便可以编写上述替换{"\$$0"}
(即内插字符串)。对于在输出中执行简单的数学运算(求和等)非常有用。
输入示例:
This is a line with <VARIABLE1>@<VARIABLE2>.
This is a line with <VARIABLE3>.
This is a line with <VARIABLE_UNKNOWN>.
This is another line contains a<b.
示例输出:
This is a line with $VARIABLE1@$VARIABLE2.
This is a line with $VARIABLE3.
This is a line with $VARIABLE_UNKNOWN.
This is another line contains a<b.
最后,@terdon 很好地概述了所有正则表达式系统的问题,以便需要准确理解数字,即这些是 ASCII 数字还是 Unicode 数字?
Raku 以其成为“Unicode 就绪”语言而自豪,您可以在底部的第一个链接中找到用于 Raku 正则表达式的 Unicode 字符类的广泛列表。因此在 Raku 中\d
数字简写包括 Unicode 数字。您可以按照上面的方式枚举数字,或者使用字符类与连词组合将数字0..9
子集/限制\d
为 ASCII 数字,如下所示:<:ASCII>
&&
[<:ASCII> && \d]
~$ raku -pe ' s:g/ \< ( [ [<:ASCII> && \d] | <:Lu> | _ ]+ ) \> /\$$0/;' file
上面<:Lu>
是“Letters-uppercase”的 Unicode 字符类。如果需要,<:ASCII>
可以使用相同的技巧来子集为“ASCII 字母大写”(或更简单地,重新排列上面的方括号以重新组合)。
https://docs.raku.org/language/regexes#Unicode_properties
https://www.unicode.org/terminology/digits.html
https://docs.raku.org/language/regexes#Conjunction:_&&
https://docs.raku.org/language/regexes
https://raku.org
答案4
使用gawk
:
$ awk '{print gensub(/<([[:alnum:]_]+)>/, "$\\1", "g")}' file
# Or
$ awk '{print gensub(/<(\w+)>/, "$\\1", "g")}' file
This is a line with $VARIABLE1@$VARIABLE2.
This is a line with $VARIABLE3.
This is a line with $VARIABLE_UNKNOWN.
This is another line contains a<b.
在 gensub() 函数中,可以使用括号 as 来记忆子模式,captured group
并在稍后使用backreference
as引用它\n
。
来自GNU awk
手册:
gensub()提供了一个附加功能,该功能不可用子()或者gsub():能够在替换文本中指定正则表达式的组件。这是通过在正则表达式中使用括号来标记组件然后指定来完成的'\N'在替换文本中,其中氮是 1 到 9 之间的数字。