Bash在命令行上工作,但不是在perl脚本中

Bash在命令行上工作,但不是在perl脚本中,bash,perl,sed,Bash,Perl,Sed,我有一组.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,---,--------=--,-------

我有一组.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,---,--------=--,-------
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;
}
我想删除所有不是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中的
有什么特别之处吗?如果有任何帮助,我们将不胜感激!也欢迎一个更简单的解决方案!

问题是
sed
参数中的双引号正在结束Perl字符串。您需要对它们进行转义

system("sed -i '/`"`",`"`"/d' *.csv");
或者您可以使用
q(…)

顺便说一句,您可以为sed提供多个命令,因此不必多次运行它

system(q(sed -i -e '/"",""/d' -e '/___/d' -e '/---/d' -e '/===/d' *.csv"));
或者,您可以交替使用正则表达式来同时匹配所有模式

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

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

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

x0,"","",""
x1,123,456,789
x2,123,456,789
x3,123,456,789
-,"","",""
x4,123,456,789
[space],____,____,____
x5,123,456,789
x6,===,====,======
x7,---,--------=--,-------
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;
}
使用严格;
使用警告;
对于我的$file(glob'*.csv'){
打开我的$in,,$file;
为@行打印$out$u;
}
它循环遍历以
.csv
结尾的每个文件,读取每一行。它跳过与其中一个模式匹配的任何行(如果需要,可以使用单个正则表达式在每个模式之间使用
|
,但我将其保留为与对sed的调用相同)。它将所有剩余的行推送到数组中。然后重新打开输入文件进行写入并打印数组


诚然,就行数而言,它稍微长一点,但当Perl功能强大时,您无需使用
system
调用外部命令。这还意味着每个文件只打开一次,而不是像原始代码那样每次替换打开一次。

使用转义字符
\
进行解释R理解SED命令中的(“,*,”)与Perl的(“,*,.”)不一样,而应该将它们视为SED命令的字符串模式。
    system("sed -i '/\"\",\"\"/d' \*\.csv");
    system("sed -i '/___/d' \*\.csv");
    system("sed -i '/---/d' \*\.csv");
    system("sed -i '/===/d' \*\.csv");

您不能以这种方式将双引号放在双引号内(第一个命令)。为什么要从perl中调用sed?最好使用本机perl功能执行这些替换。't最好使用
q(…)
因为不需要扩展?但最好还是不要使用
sed
;Perl被设计成
sed
-杀手。这就是为什么(仍然)有
s2p
用于将
sed
转换为Perl。@JonathanLeffler我刚刚意识到了同样的事情。如果这只是整个Perl脚本中的一小步,那么用Perl重写整个脚本可能会有些过火。您可能可以使用正则表达式,例如
m/^x\d\d+,\d+,\d+$/
来查找与
xn,#完全匹配的行###,###
行是必需的,如果规则要求,例如,
x
后面有一个或多个数字(而不是如图所示的一个数字),或者其他组中有三个数字(而不是如图所示的一个或多个数字),则需要进行各种调整“@Jonathan我故意将模式保留原样,以使其更清楚地融入脚本中,但我同意,几乎肯定有可能对其进行改进,可能是将行白名单化,而不是像你所说的跳过不匹配的行。当然,这取决于输入的多样性和OP的要求。@Tom Fen”ech:谢谢你的帮助。这绝对是一个更好的解决方案——我对Perl(以及一般的编程)相当陌生,所以有时我倾向于避免使用它……有没有办法轻松摆脱while循环中的特定字符?在这些文件的最后一行,是“-”在第一个字符串之前,我正试图在没有sed帮助的情况下去掉它。这个字符串是“-TOTAL BE”。现在我插入了以下内容,但它似乎不起作用:
my$str2=“-TOTAL BE”;while(){$str2=~s/-//g;next if/”,“/;
等等。@Justin您可以添加一行类似
s的内容/^-(?=TOTAL BE)/;
,它将匹配行开始处的
-
,后跟
TOTAL BE
,并将其删除。默认情况下,替换将应用于
$\uCode>,在循环上下文中,它是当前正在读取的行。