我们有一项任务是将二进制文件中的某些字符串更改为小写(来自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脚本中运行。
答案 0 :(得分:17)
shell不对单引号字符串进行变量替换。所以,第二个是不同的程序。
答案 1 :(得分:4)
正如其他答案所说,shell不会在单引号中替换变量,因此第二个版本正在为每个文件中的每一行执行文字Perl语句s{(?i)$file_ref}{$file_ref}g
。
正如您在评论中所说,如果$
是行尾元字符,则$file_ref
永远不会匹配任何内容。 $
在行尾的换行符之前匹配,因此下一个字符必须是换行符。因此,Perl不会将$
解释为元字符;它将其解释为变量插值的开始。
在Perl中,变量$file_ref
是undef
,在插值时被视为空字符串。因此,您实际上正在执行s{(?i)}{}g
,它表示用空字符串替换空字符串,并以不区分大小写的方式对所有事件执行此操作。好吧,每对字符之间都有一个空字符串,每行的开头和结尾都有一个字符串。 Perl找到每一个并用空字符串替换它。这是一个无操作,但它是一个昂贵的,因此3小时的运行时间。
你必须弄错两个版本进行相同的更改。正如我刚才解释的那样,单引号版本只是一个昂贵的无操作版本;它不会对文件内容进行任何更改(它只是制作每个文件的新副本)。您运行它的文件必须已经转换为小写。
答案 2 :(得分:1)
使用双引号你正在使用shell变量,单引号Perl试图使用该名称的变量。
您可能希望考虑用Perl或Bash编写全部内容以加快速度。两种语言都可以读取文件并进行模式匹配。在Perl中,您可以使用lc
内置函数更改为小写,而在Bash 4中,您可以使用${file,,}
。