用Unix方式求两列之和
# 修复症状
如何有效地对以下列进行求和?
第一列
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 个回答
你可以通过使用一个命令来同时进行计数和比较,从而省去中间步骤:
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
中的值加一。
使用Python:
totals = [ int(i)+int(j) for i, j in zip ( open(fname1), open(fname2) ) ]
如果c1和c2是你的文件,你可以这样做:
$ paste c1 c2 | awk '{print $1 + $2}'
或者(不使用AWK):
$ paste c1 c2 | while read i j; do echo $(($i+$j)); done