用Unix方式求两列之和

1 投票
6 回答
2078 浏览
提问于 2025-04-15 13:56

# 修复症状

如何有效地对以下列进行求和?

第一列

1
3
3
...   

第二列

2323
343
232
...

这应该给我

期望结果

2324
346
235
...

我在两个文件中有这些列。


# 初始情况

我有时候使用了太多的花括号,导致在我的文件中这个 { 的数量比这个 } 多了一个。我正在尝试找出多出来的那个花括号在哪里。

我在获取数据时使用了以下步骤:

查找命令

 find . * -exec grep '{' {} + > /tmp/1
 find . * -exec grep '}' {} + > /tmp/2

AWK 命令

 awk -F: '{ print $2 }' /tmp/1 > /tmp/11
 awk -F: '{ print $2 }' /tmp/2 > /tmp/22

这些列在文件 /tmp/11 和 /tmp/22 中。

我在我的操作中重复了很多类似的命令。这让我觉得这样做不是正确的方式。

请给我推荐一些方法,比如 Python、Perl 或任何 Unix 工具,可以减少步骤的数量。

6 个回答

3

你可以通过使用一个命令来同时进行计数和比较,从而省去中间步骤:

find . -type f -exec perl -nle 'END { print $ARGV if $h{"{"} != $h{"}"} } $h{$_}++ for /([}{])/g' {}\;

这个命令会对每个文件调用一次Perl程序,Perl程序会计算每种类型的花括号数量,如果数量不匹配,就会打印出文件名。

你需要小心/([}{]])/这一部分,如果你写成/([{}]])/find会认为需要对{}进行替换。

警告:如果你试图在源代码上运行这段代码,它可能会出现误报和漏报。考虑以下情况:

平衡,但字符串中有花括号:

if ($s eq '{') {
    print "I saw a {\n"
}

不平衡,但字符串中有花括号:

while (1) {
   print "}";

你可以通过使用B::Deparse来扩展Perl命令:

perl -MO=Deparse -nle 'END { print $ARGV if $h{"{"} != $h{"}"} } $h{$_}++ for /([}{])/g'

这将得到:

BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    sub END {
        print $ARGV if $h{'{'} != $h{'}'};
    }
    ;
    ++$h{$_} foreach (/([}{])/g);
}

现在我们可以逐部分分析这个程序:

BEGIN { $/ = "\n"; $\ = "\n"; }

这是由于-l选项引起的。它将输入和输出记录分隔符都设置为"\n"。这意味着读取的任何内容都会根据"\n"分成记录,任何打印语句都会在后面加上"\n"。

LINE: while (defined($_ = <ARGV>)) {
}

这是由-n选项创建的。它会遍历命令行传入的每个文件(如果没有传入文件,则从标准输入读取),逐行读取这些文件的内容。这个选项还会将$ARGV设置为最后读取的文件。

chomp $_;

这会从刚刚读取的行($_)中移除$/变量中的内容,这里没有什么实际用处。它是由-l选项引起的。

sub END {
    print $ARGV if $h{'{'} != $h{'}'};
}

这是一个结束块,这段代码会在程序结束时运行。如果存储在%h中与'{'相关的值相等,就会打印$ARGV(最后读取的文件名,见上文)。

++$h{$_} foreach (/([}{])/g);

这需要进一步分解:

/
    (    #begin capture
    [}{] #match any of the '}' or '{' characters
    )    #end capture
/gx

这是一个正则表达式,它返回在被匹配字符串中的'{'和'}'字符的列表。由于没有指定字符串,$_变量(它保存了最后从文件读取的行,见上文)将被用来匹配。这个列表会被传递给foreach语句,然后对列表中的每个项目执行它前面的语句(因此叫做foreach)。它还会将$_(如你所见,$_在Perl中是一个常用变量)设置为列表中的项目。

++h{$_}

这一行将$_(它将是'{'或'}',见上文)关联的$h中的值加一。

11

使用Python:

totals = [ int(i)+int(j) for i, j in zip ( open(fname1), open(fname2) ) ]
11

如果c1和c2是你的文件,你可以这样做:

$ paste c1 c2 | awk '{print $1 + $2}'

或者(不使用AWK):

$ paste c1 c2 | while read i j; do echo $(($i+$j)); done

撰写回答