Perl行使用单引号比使用双引号快30倍

时间:2011-08-05 13:30:02

标签: linux perl bash

我们有一项任务是将二进制文件中的某些字符串更改为小写(来自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

而以下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

任何人都可以解释原因吗?是$ file_ref是否作为字符串$ file_ref而不是用单引号版本中的值替换?在这种情况下,在这个版本中它取代了什么?我们想要的是用自己替换任何文件名的所有出现但是小写。如果我们在文件之前和之后运行字符串并搜索文件名,那么两者似乎都做了相同的更改。但是,如果我们对两个循环(diff firstloop / file1 secondloop / file1)生成的文件运行diff,那么它会报告它们不同。

这是在linux上的bash脚本中运行。

3 个答案:

答案 0 :(得分:17)

shell不对单引号字符串进行变量替换。所以,第二个是不同的程序。

答案 1 :(得分:4)

正如其他答案所说,shell不会在单引号中替换变量,因此第二个版本正在为每个文件中的每一行执行文字Perl语句s{(?i)$file_ref}{$file_ref}g

正如您在评论中所说,如果$是行尾元字符,则$file_ref永远不会匹配任何内容。 $在行尾的换行符之前匹配,因此下一个字符必须是换行符。因此,Perl不会将$解释为元字符;它将其解释为变量插值的开始。

在Perl中,变量$file_refundef,在插值时被视为空字符串。因此,您实际上正在执行s{(?i)}{}g,它表示用空字符串替换空字符串,并以不区分大小写的方式对所有事件执行此操作。好吧,每对字符之间都有一个空字符串,每行的开头和结尾都有一个字符串。 Perl找到每一个并用空字符串替换它。这是一个无操作,但它是一个昂贵的,因此3小时的运行时间。

你必须弄错两个版本进行相同的更改。正如我刚才解释的那样,单引号版本只是一个昂贵的无操作版本;它不会对文件内容进行任何更改(它只是制作每个文件的新副本)。您运行它的文件必须已经转换为小写。

答案 2 :(得分:1)

使用双引号你正在使用shell变量,单引号Perl试图使用该名称的变量。

您可能希望考虑用Perl或Bash编写全部内容以加快速度。两种语言都可以读取文件并进行模式匹配。在Perl中,您可以使用lc内置函数更改为小写,而在Bash 4中,您可以使用${file,,}