在 sed 中使用特殊字符转义变量 - 注释和取消注释任意一行源代码
Escaping a variable with special characters within sed - comment and uncomment an arbitrary line of source code
我需要通过脚本在crontab文件中注释掉一行,所以它包含目录、空格和符号。这个特定的行存储在一个变量中,我开始对如何转义变量感到困惑。由于线路会定期更改,因此我不想在其中进行任何转义。我不想简单地在它前面添加#,因为我还需要切换它并用没有# 的原始行再次替换该行。
所以我们的目标是将 $line 替换为 #$line(注释),并有可能以相反的方式进行替换(取消注释)。
所以我有一个变量:
line="* * * hello/this/line & /still/this/line"
这是文件中出现的一行,file.txt。需要发表评论。
第一次尝试:
sed -i "s/^${line}/#${line}/" file.txt
第二次尝试:
sed -i 's|'${line}'|'"#${line}"'|g' file.txt
Perl 有办法为您完成quoting/escaping:
line=$line perl -i~ -pe '$regex = quotemeta $ENV{line}; s/^$regex/#$ENV{line}/' -- input.txt
显示了使用 perl
.
的有效解决方案
sed
解法
如果你想使用sed
,你必须使用单独的sed
命令来转义$line
变量值,因为sed
没有 built-in 转义字符串以在正则表达式上下文中用作文字的方法:
lineEscaped=$(sed 's/[^^]/[&]/g; s/\^/\^/g' <<<"$line") # escape $line for use in regex
sed -i "s/^$lineEscaped$/#&/" file.txt # Note the $ to escape the end-of-line anchor $
使用 BSD/macOS sed
,使用 -i ''
而不是仅 -i
进行 in-place 无备份更新。
反之(un-评论):
sed -i "s/^#\($lineEscaped\)$//" file.txt
请参阅我的 以了解用于转义的 sed
命令的解释,该命令适用于任何输入字符串。
还要注意变量 $lineEscaped
在 s
的 regex 部分中如何仅被引用 一次命令,而 substitution-string 部分仅引用正则表达式匹配的内容(这避免了使用不同规则再次转义变量的需要):
替换字符串中的&
代表整个匹配,</code>第一个捕获组(带括号的子表达式,<code>\(...\)
)。
为简单起见,第二个sed
命令使用双引号,以便在sed
脚本中嵌入shell变量$lineEscaped
的值,但通常最好使用 单 引号脚本,以避免混淆 shell 预先解释的内容与 sed
最终看到的内容。
例如,$
对于shell和sed
都是特殊的,在上面的脚本中end-of-line锚点$
在sed
因此必须将正则表达式转义为 $
以防止 shell 解释它。
避免混淆的一种方法是有选择地将 double-quoted shell-variable 引用拼接到其他 single-quoted 脚本中:
sed -i 's/^'"$lineEscaped"'$/#&/' file.txt
awk
解决方案
awk
提供文字字符串匹配,这避免了转义的需要:
awk -v line="$line" '[=13=] == line { [=13=] = "#" [=13=] } 1' file.txt > $$.tmp && mv $$.tmp file.txt
如果你有 GNU Awk v4.1+,你可以使用 -i inplace
进行 in-place 更新。
反之(un-评论):
awk -v line="#$line" '[=14=] == line { [=14=] = substr([=14=], 2) } 1' file.txt > $$.tmp &&
mv $$.tmp file.txt
我需要通过脚本在crontab文件中注释掉一行,所以它包含目录、空格和符号。这个特定的行存储在一个变量中,我开始对如何转义变量感到困惑。由于线路会定期更改,因此我不想在其中进行任何转义。我不想简单地在它前面添加#,因为我还需要切换它并用没有# 的原始行再次替换该行。
所以我们的目标是将 $line 替换为 #$line(注释),并有可能以相反的方式进行替换(取消注释)。
所以我有一个变量:
line="* * * hello/this/line & /still/this/line"
这是文件中出现的一行,file.txt。需要发表评论。
第一次尝试:
sed -i "s/^${line}/#${line}/" file.txt
第二次尝试:
sed -i 's|'${line}'|'"#${line}"'|g' file.txt
Perl 有办法为您完成quoting/escaping:
line=$line perl -i~ -pe '$regex = quotemeta $ENV{line}; s/^$regex/#$ENV{line}/' -- input.txt
perl
.
sed
解法
如果你想使用sed
,你必须使用单独的sed
命令来转义$line
变量值,因为sed
没有 built-in 转义字符串以在正则表达式上下文中用作文字的方法:
lineEscaped=$(sed 's/[^^]/[&]/g; s/\^/\^/g' <<<"$line") # escape $line for use in regex
sed -i "s/^$lineEscaped$/#&/" file.txt # Note the $ to escape the end-of-line anchor $
使用 BSD/macOS sed
,使用 -i ''
而不是仅 -i
进行 in-place 无备份更新。
反之(un-评论):
sed -i "s/^#\($lineEscaped\)$//" file.txt
请参阅我的 sed
命令的解释,该命令适用于任何输入字符串。
还要注意变量 $lineEscaped
在 s
的 regex 部分中如何仅被引用 一次命令,而 substitution-string 部分仅引用正则表达式匹配的内容(这避免了使用不同规则再次转义变量的需要):
替换字符串中的&
代表整个匹配,</code>第一个捕获组(带括号的子表达式,<code>\(...\)
)。
为简单起见,第二个sed
命令使用双引号,以便在sed
脚本中嵌入shell变量$lineEscaped
的值,但通常最好使用 单 引号脚本,以避免混淆 shell 预先解释的内容与 sed
最终看到的内容。
例如,$
对于shell和sed
都是特殊的,在上面的脚本中end-of-line锚点$
在sed
因此必须将正则表达式转义为 $
以防止 shell 解释它。
避免混淆的一种方法是有选择地将 double-quoted shell-variable 引用拼接到其他 single-quoted 脚本中:
sed -i 's/^'"$lineEscaped"'$/#&/' file.txt
awk
解决方案
awk
提供文字字符串匹配,这避免了转义的需要:
awk -v line="$line" '[=13=] == line { [=13=] = "#" [=13=] } 1' file.txt > $$.tmp && mv $$.tmp file.txt
如果你有 GNU Awk v4.1+,你可以使用 -i inplace
进行 in-place 更新。
反之(un-评论):
awk -v line="#$line" '[=14=] == line { [=14=] = substr([=14=], 2) } 1' file.txt > $$.tmp &&
mv $$.tmp file.txt