【问题标题】:Perl line runs 30 times quicker with single quotes than with double quotesPerl 行用单引号比用双引号快 30 倍
【发布时间】:2011-10-20 21:19:12
【问题描述】:

我们的任务是将二进制文件中的一些字符串更改为小写(从混合/大写/随便)。相关字符串是对其他文件的引用(它与升级有关,我们也从 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(diff firstloop/file1 secondloop/file1),那么它会报告它们不同。

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

【问题讨论】:

    标签: linux perl bash


    【解决方案1】:

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

    【讨论】:

    • 对于带单引号的等效程序,请确保您在 bash shell 中使用 export file_ref 并使用表达式 s/(?i)$ENV{file_ref}/$ENV{file_ref}/g
    • 谢谢。我自己已经走了那么远,我认为双引号版本是我想要的版本(因为我希望将 $file_ref 替换为发送到 perl 的表达式中的文件名)。我提出这个问题的部分原因是要仔细检查我想要的版本是否正确,部分原因是看看是否有人可以解释另一个版本在做什么以及为什么需要 30 倍的时间?如果 $file_ref 未被替换,那么 perl 代码如何匹配任何内容($ 是行尾,$anything 永远不应该匹配 - 或者?)。
    • 这两个版本似乎都替换了我想要替换的字符串,但一个显然不止于此(生成的二进制文件根据 diff 不同,加上 30 倍的速度差异)。真的很想了解单引号版本对替换的作用
    【解决方案2】:

    带双引号的你使用的是 shell 变量,带单引号的 Perl 试图使用那个名字的变量。

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

    【讨论】:

    • 谢谢。但是,单引号版本究竟是在做什么替换(请参阅我对 yi_H 答案的评论)?
    【解决方案3】:

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

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

    在 Perl 中,变量$file_refundef,在插值时被视为空字符串。所以你真的在执行s{(?i)}{}g,它说用空字符串替换空字符串,并以不区分大小写的方式对所有出现的情况执行此操作。好吧,每对字符之间都有一个空字符串,每行的开头和结尾都有一个。 Perl 正在查找每一个并将其替换为空字符串。这是一个空操作,但它很昂贵,因此需要 3 小时的运行时间。

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

    【讨论】:

    • 我不敢相信这个很棒的答案没有更多的支持。
    猜你喜欢
    • 2013-02-16
    • 1970-01-01
    • 2021-08-12
    • 2013-03-05
    • 1970-01-01
    • 2011-09-03
    • 2011-07-03
    • 2015-11-17
    • 2018-05-12
    相关资源
    最近更新 更多