【问题标题】:Bash working on command line but not in perl scriptBash 在命令行上工作,但不在 perl 脚本中
【发布时间】:2015-11-28 14:30:48
【问题描述】:

我有一组要清理的 .csv 文件。每个都有这样的数据:

x0,"","",""
x1,123,456,789
x2,123,456,789
x3,123,456,789
-,"","",""
x4,123,456,789
[space],____,____,____
x5,123,456,789
x6,===,====,======
x7,---,--------=--,-------

我想删除所有不是 xn,###,###,### 的行,所以在这个例子中,它将是第 1、5、7、9 和 10 行。在 cygwin 命令中行,我一一输入以下命令:

    sed -i '/"",""/d' *.csv
    sed -i '/___/d' *.csv
    sed -i '/---/d' *.csv
    sed -i '/===/d' *.csv

这些都有效。但是,当我尝试将它们一起放入 perl 脚本时(我的其余代码在 perl 中,它们失败了:

    system("sed -i '/"",""/d' *.csv");
    system("sed -i '/___/d' *.csv");
    system("sed -i '/---/d' *.csv");
    system("sed -i '/===/d' *.csv");

我得到了结果:

在 test1.pl 第 1 行的运算符预期位置找到字符串,靠近 ""sed -i '/"",""

(“,”之前缺少运算符?)

在 test1.pl 第 1 行,“”,“/d' *.csv”“附近的运算符预期的位置找到字符串

(“/d' *.csv”之前缺少运算符?)

test1.pl 第 1 行的语法错误,靠近 ""sed -i '/"",""

我注意到除了第一个命令之外的所有工作——sed 中的"" 有什么特别之处吗?任何帮助,将不胜感激!也欢迎使用更简单的解决方案!

【问题讨论】:

  • 你不能那样把双引号放在双引号内(第一个命令)。
  • 你为什么在 perl 中调用 sed?使用本机 perl 功能执行这些替换会更好。

标签: bash perl sed


【解决方案1】:

使用转义字符\ 让解释器明白 sed 命令中的 (", *, .) 与 Perl 的 (", *, .) 不同,而是应该将它们视为 sed 的字符串模式命令。

    system("sed -i '/\"\",\"\"/d' \*\.csv");
    system("sed -i '/___/d' \*\.csv");
    system("sed -i '/---/d' \*\.csv");
    system("sed -i '/===/d' \*\.csv");

【讨论】:

    【解决方案2】:

    如果您的脚本的其余部分在 Perl 中,我强烈建议您使用本机实现替换您对 sed 的调用。

    例如,您使用 sed 进行的替换可以替换为以下内容:

    use strict;
    use warnings;
    
    for my $file (glob '*.csv') {
        open my $in, '<', $file;        
        my @lines;
        while (<$in>) {
            next if /"",""/;
            next if /___/;
            next if /---/;
            next if /===/;
            push @lines, $_;
        }
        close $in;
    
        # this will overwrite your files!
        # change $file to something else to test
        open my $out, '>', $file;
        print $out $_ for @lines;
    }
    

    这将遍历每个以.csv 结尾的文件,读取每一行。它会跳过与其中一种模式匹配的任何行(如果需要,您可以在每个模式之间使用带有 | 的单个正则表达式来执行此操作,但我将其保留为与您对 sed 的调用相同)。它将任何剩余的行推送到数组中。然后它重新打开输入文件进行写入并打印数组。

    当然,它在行数方面稍长,但当 Perl 功能强大时,您不必使用system 调用外部命令。这也意味着每个文件只打开一次,而不是像原始代码那样每次替换一次。

    【讨论】:

    • 您可能会使用诸如m/^x\d,\d+,\d+,\d+$/ 之类的正则表达式来查找与所需的xn,###,###,### 行完全匹配的行,并在规则需要时进行各种调整,例如,一个x 之后的一个或多个数字(而不是如图所示的一个),或其他组中的三个数字(而不是一个或多个如图所示)。
    • @Jonathan 我故意将模式保留原样,以便更清楚地说明它们如何适合脚本,但我同意,几乎肯定可以改进它们,也许通过将行列入白名单而不是跳过你说的不匹配的。当然,这取决于输入的多样性和OP的要求。
    • @Tom Fenech:感谢您的帮助。这绝对是一个更好的解决方案——我对 Perl(和一般编程)相当陌生,所以我有时会回避使用它......有什么方法可以轻松摆脱特定角色环形?在这些文件的最后一行,第一个字符串前面有一个“-”,我试图在没有 sed 帮助的情况下摆脱它。这个字符串是“-TOTAL BE”。现在我已经插入了以下内容,但它似乎不起作用:my $str2 = "-TOTAL BE"; while (&lt;$in&gt;) { $str2 =~ s/-//g; next if /"",""/; 等等等等。
    • @Justin 您可以添加类似s/^-(?=TOTAL BE)//; 的行,这将匹配行首的-,后跟TOTAL BE 并将其删除。默认情况下,替换将应用于$_,在循环的上下文中,它是当前正在读取的行。
    【解决方案3】:

    问题是sed 参数中的双引号结束了Perl 字符串。你需要逃离他们

    system("sed -i '/`"`",`"`"/d' *.csv");
    

    或者你可以使用q(...)

    system(q(sed -i '/"",""/d' *.csv));
    

    顺便说一句,你可以给sed多个命令,所以你不必多次运行它。

    system(q(sed -i -e '/"",""/d' -e '/___/d' -e '/---/d' -e '/===/d' *.csv"));
    

    或者您可以使用带有交替的正则表达式来一次匹配所有模式。

    system(q(sed -i -e '/"",""\\|___\\|---/\\|===/d' *.csv"));
    

    【讨论】:

    • '使用q(…) 会更好,因为不需要扩展?但最好还是不要使用sed; Perl 被设计为sed-killer。这就是为什么(仍然)有 s2psed 转换为 Perl 的原因。
    • @JonathanLeffler 我刚刚意识到同样的事情。
    • 如果这只是整个 perl 脚本中的一小步,那么在 perl 中重写整个东西可能是矫枉过正。
    猜你喜欢
    • 2018-03-25
    • 1970-01-01
    • 2013-09-28
    • 1970-01-01
    • 2017-08-08
    • 2021-12-14
    • 2015-05-12
    • 1970-01-01
    • 2017-07-19
    相关资源
    最近更新 更多