我们有一项任务是将二进制文件中的某些字符串更改为小写(来自mixed/upper/whatever).相关的字符串是对其他文件的引用(它与升级相关,我们也将Windows作为服务器环境从Windows迁移到Linux,因此案例突然变得很重要).我们编写了一个使用perl循环执行此操作的脚本.我们有一个包含大约300个文件的目录(目录的总大小约为150M)所以它是一些数据但不是很大的数量.
以下perl代码大约需要6分钟才能完成工作:
for file_ref in `ls -1F $forms6_convert_dir/ | grep -v "/" | sed 's/\(.*\)\..*/\1/'`
do
(( updated++ ))
write_line "Converting case of string: $file_ref "
perl -i -pe "s{(?i)$file_ref}{$file_ref}g" $forms6_convert_dir/*
done
Run Code Online (Sandbox Code Playgroud)
而以下perl代码需要3个小时!
for file_ref in `ls -1F $forms6_convert_dir/ | grep -v "/" | sed 's/\(.*\)\..*/\1/'`
do
(( updated++ ))
write_line "Converting case of string: $file_ref "
perl -i -pe 's{(?i)$file_ref}{$file_ref}g' $forms6_convert_dir/*
done
Run Code Online (Sandbox Code Playgroud)
有谁能解释为什么?这是$ file_ref是否作为字符串$ file_ref而不是用单引号版本中的值替换?在这种情况下,在这个版本中它取代了什么?我们想要的是用自己替换任何文件名的所有出现但是小写.如果我们在文件之前和之后运行字符串并搜索文件名,那么两者似乎都做了相同的更改.但是,如果我们对两个循环(diff firstloop/file1 secondloop/file1)生成的文件运行diff,那么它会报告它们不同.
这是在linux上的bash脚本中运行的.
正如其他答案所说,shell 不会替换单引号内的变量,因此第二个版本正在s{(?i)$file_ref}{$file_ref}g
为每个文件中的每一行执行文字 Perl 语句。
正如您在评论中所说, if$
是行尾元字符,$file_ref
永远无法匹配任何内容。 $
在行尾的换行符之前匹配,因此下一个字符必须是换行符。因此,Perl 不解释$
为元字符;而是将其解释为元字符。它将其解释为变量插值的开始。
在 Perl 中,变量$file_ref
是undef
,插值时被视为空字符串。所以你实际上是在执行s{(?i)}{}g
,它表示用空字符串替换空字符串,并以不区分大小写的方式对所有出现的情况执行此操作。好吧,每对字符之间都有一个空字符串,并且每行的开头和结尾都有一个空字符串。Perl 正在查找每一个并将其替换为空字符串。这是一项无操作,但成本高昂,因此需要 3 小时的运行时间。
您肯定误认为两个版本都进行了相同的更改。正如我刚才所解释的,单引号版本只是一个昂贵的无操作;它根本不对文件内容进行任何更改(它只是为每个文件创建一个新副本)。您运行它的文件必须已经转换为小写。