wef*_*wef 2 bash perl awk sed xxd
假设我们在文件中有一些任意文字,我们需要用其他文字替换.
通常情况下,我们只需要达到sed(1)或awk(1)并编写如下代码:
sed "s/$target/$replacement/g" file.txt
Run Code Online (Sandbox Code Playgroud)
但是,如果$ target和/或$替换可能包含对sed(1)敏感的字符(如正则表达式),该怎么办?你可以逃脱它们但是假设你不知道它们是什么 - 它们是武断的,好吗?您需要编写代码以逃避所有可能的敏感字符 - 包括'/'分隔符.例如
t=$( echo "$target" | sed 's/\./\\./g; s/\*/\\*/g; s/\[/\\[/g; ...' ) # arghhh!
Run Code Online (Sandbox Code Playgroud)
对于这么简单的问题,这很尴尬.
perl(1)有\ Q ...\E引用,但即便如此也无法应对'/'分隔符$target.
perl -pe "s/\Q$target\E/$replacement/g" file.txt
Run Code Online (Sandbox Code Playgroud)
我刚发布了一个答案!! 所以我真正的问题是,"有没有更好的方法在sed/awk/perl中进行文字替换?"
如果没有,我会留在这里,以防它有用.
该quotemeta,它实现了\Q,压根就你问的是什么
所有不匹配的ASCII字符
/[A-Za-z_0-9]/前面都会加一个反斜杠
因为这可能是在shell脚本中,所以问题实际上是shell变量如何以及何时被插值以及Perl程序最终看到的内容.
最好的方法是避免计算插值混乱,而是将这些shell变量正确地传递给Perl单行.这可以通过几种方式完成; 有关详细信息,请参阅此帖
将shell变量简单地作为参数传递
#!/bin/bash
# define $target
perl -pe"BEGIN { $patt = shift }; s{\Q$patt}{$replacement}g" "$target" file.txt
Run Code Online (Sandbox Code Playgroud)
所需的参数从块中删除@ARGV并在BEGIN块中使用,所以在运行时之前; 然后file.txt得到处理.这里\E的正则表达式没有必要.
或者,使用-s开关,它可以为程序启用命令行开关
# define $target, etc
perl -s -pe"s{\Q$patt}{$replacement}g" -- -patt="$target" file.txt
Run Code Online (Sandbox Code Playgroud)
在--需要标记的论点开始,开关必须来自之前的文件名.
最后,您还可以导出shell变量,然后可以在Perl脚本中使用%ENV; 但总的来说,我宁愿推荐上述两种方法中的任何一种.
一个完整的例子
#!/bin/bash
# Last modified: 2019 Jan 06 (22:15)
target="/{"
replacement="&"
echo "Replace $target with $replacement"
perl -wE'
BEGIN { $p = shift; $r = shift };
$_=q(ah/{yes); s/\Q$p/$r/; say
' "$target" "$replacement"
Run Code Online (Sandbox Code Playgroud)
这打印
Replace /{ with &
ah&yes
在哪里我使用了评论中提到的字符.
另一种方法
#!/bin/bash
# Last modified: 2019 Jan 06 (22:05)
target="/{"
replacement="&"
echo "Replace $target with $replacement"
perl -s -wE'$_ = q(ah/{yes); s/\Q$patt/$repl/; say' \
-- -patt="$target" -repl="$replacement"
Run Code Online (Sandbox Code Playgroud)
代码在这里被分解为可读性(因此需要\).相同的打印输出.
又是我!
这是使用xxd (1)的更简单方法:
t=$( echo -n "$target" | xxd -p | tr -d '\n')
r=$( echo -n "$replacement" | xxd -p | tr -d '\n')
xxd -p file.txt | sed "s/$t/$r/g" | xxd -p -r
Run Code Online (Sandbox Code Playgroud)
...所以我们使用xxd (1) 对原始文本进行十六进制编码,并使用十六进制编码的搜索字符串进行搜索替换。最后我们对结果进行十六进制解码。
\n编辑:我忘记从 xxd 输出 ( ) 中删除| tr -d '\n',以便模式可以跨越 xxd 的 60 列输出。当然,这依赖于 GNUsed操作很长行的能力(仅受内存限制)。
编辑:这也适用于多行目标,例如
目标=$'foo\nbar' 替换=$'bar\nfoo'