我正在尝试使用shell脚本将每个单词(存储在名为_id
的tmp文件中(替换为一个数字。它工作正常,除了 unicode 单词,生成了一个数字,但使用 Perl 替换不起作用。有问题的 bash 代码如下:
x=0
for id in `cat _id`; do
echo $x $id
perl -p -i -e "s/b$idb/$x/g" x_graph.dot
x=$(($x + 1))
done
有人可以指出错误在哪里吗?
假设您使用 UTF-8 编码了é
(U+00E9(:C3 A9
。由于您不执行任何解码,因此您将获得由"xC3xA9"
生成的字符串。
正则表达式(或者更确切地说是b
、w
、d
等(期望输入是 Unicode 码位,这意味着您有效地提供了 U+00C3 和 U+00A9 而不是 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/bQ$idEb/$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/bQ$idEb/$x/g
' "$id" "$x" x_graph.dot
$ cat x_graph.dot
[0]
参见 perldoc perlrun:
-C
[数量/列表]
-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$idb/$x/g" x_graph.dot
请注意使用单引号而不是双引号,以避免意外插值。
-
添加
-Mutf8
(相当于use utf8;
(:这将在源代码中启用 UTF-8(在您的情况下-e
单行(。 -
添加
-CSDA
:这将使perl
使用 UTF-8 作为输入和输出流的默认层。
以下测试在LANG=en_US.UTF-8
下产生了预期的结果
echo "a ó b" > z.txt
id=ó
x=ń
perl -CD -Mutf8 -p -i -e "s/b$idb/$x/g" z.txt
cat z.txt
man perlrun
-C [number/list]
-C 标志控制着一些 Perl Unicode 的特性。
...
S 8 I + O + E [ STDIN 假定为 UTF-8,STDOUT 和 STDERR 将为 UTF-8]
D 24 i + o [ UTF-8 是输入和输出流的默认 PerlIO 层]
A 32 @ARGV元素应为字符串编码 在 UTF-8 中