我正在尝试用shell脚本替换每个单词(存储在名为_id
的tmp文件中)。它工作正常,除了unicode单词,生成一个数字但使用Perl替换不起作用。有问题的bash代码如下:
x=0
for id in `cat _id`; do
echo $x $id
perl -p -i -e "s/\b$id\b/$x/g" x_graph.dot
x=$(($x + 1))
done
有人可以指出错误的位置吗?
答案 0 :(得分:2)
假设您使用UTF-8编码é
(U + 00E9):C3 A9
。由于您不进行任何解码,因此您将获得由"\xC3\xA9"
生成的字符串。
正则表达式 - 或者\b
,\w
,\d
等等 - 期望输入为Unicode代码点,这意味着您有效地提供U+00C3和{ {3}}而不是U + 00E9。 U + 00C3是单词字符,但U + 00A9不是,因此第二个\b
与预期匹配的位置不匹配。
因此您需要解码输入并对输出进行编码。 -C
为UTF-8提供了一种方便的方法。
perl -i -CSDA -pe'
BEGIN {
($id, $x) = splice(@ARGV, 0, 2);
die "Bad id" if $id !~ /^\w(?:.*\w)?\z/s;
}
s/\b\Q$id\E\b/$x/g
' "$id" "$x" x_graph.dot
注意:
通过使用命令行参数传递参数,我修复了注入错误。
\b
的使用假定$id
始终以\w
字符开头,并始终以\w
字符结尾,因此我添加了一个检查验证这个假设。
通过使用\Q..\E
将id转换为正则表达式模式,我修复了注入错误。
测试:
$ printf "é\n" >_id
$ printf "[é]\n" >x_graph.dot
$ x=0
$ id=`cat _id`
$ perl -i -CSDA -pe'
BEGIN {
($id, $x) = splice(@ARGV, 0, 2);
die "Bad id" if $id !~ /^\w(?:.*\w)?\z/s;
}
s/\b\Q$id\E\b/$x/g
' "$id" "$x" x_graph.dot
$ cat x_graph.dot
[0]
答案 1 :(得分:1)
请参阅perldoc perlrun:
-C
[number / list]
-C
标志控制一些Perl Unicode功能:I 1 STDIN is assumed to be in UTF-8 O 2 STDOUT will be in UTF-8 E 4 STDERR will be in UTF-8 S 7 I + O + E i 8 UTF-8 is the default PerlIO layer for input streams o 16 UTF-8 is the default PerlIO layer for output streams D 24 i + o A 32 the @ARGV elements are expected to be strings encoded in UTF-8
所以,至少,您需要perl -COi
,但perl -CSD
看起来更整洁。
此外,您可能想要使用
根据Unicode规则
u
匹配
与您的s///
。或者,写:
perl -CSD -Mutf8 -Mfeature=unicode_strings -p -i -e "s/\b$id\b/$x/g" x_graph.dot
请注意使用单引号而不是双引号,以避免意外插值。
答案 2 :(得分:1)
添加-Mutf8
(相当于use utf8;
):这将在源代码中启用UTF-8(在您的情况下为-e
一行)。
添加-CD
:这会使perl
使用UTF-8作为输入和输出流的默认图层。
以下测试在LANG=en_US.UTF-8
echo "a ó b" > z.txt
id=ó
x=ń
perl -CD -Mutf8 -p -i -e "s/\b$id\b/$x/g" z.txt
cat z.txt