Linux awk汇总多个文件显示两组文件上都没有的行

Linux awk汇总多个文件显示两组文件上都没有的行,linux,bash,shell,sorting,awk,Linux,Bash,Shell,Sorting,Awk,我一直在使用awk汇总多个文件,这是用来汇总服务器日志解析值的摘要,它确实加快了最终的总体计数,但我遇到了一个小问题,我在web上遇到的典型示例没有帮助 以下是一个例子: cat file1 aa 1 bb 2 cc 3 ee 4 cat file2 aa 1 bb 2 cc 3 dd 4 cat file3 aa 1 bb 2 cc 3 ff 4 还有剧本: cat test.sh #!/bin/bash files="file1 file2 file3" i=0; oldname

我一直在使用awk汇总多个文件,这是用来汇总服务器日志解析值的摘要,它确实加快了最终的总体计数,但我遇到了一个小问题,我在web上遇到的典型示例没有帮助

以下是一个例子:

cat file1
aa 1
bb 2
cc 3
ee 4

cat file2
aa 1
bb 2
cc 3
dd 4

cat file3
aa 1
bb 2
cc 3
ff 4
还有剧本:

cat test.sh 
#!/bin/bash

files="file1 file2 file3"

i=0;
oldname="";
for names in $(echo $files); do
        ((i++));
        if [ $i == 1 ]; then
                oldname=$names
                #echo "-- $i $names"
                shift;
        else
               oldname1=$names.$$
        awk  'NR==FNR { _[$1]=$2 } NR!=FNR { if(_[$1] != "") nn=0; nn=($2+_[$1]); print $1" "nn }' $names $oldname> $oldname1
        if [ $i -gt 2 ]; then
            rm $oldname;
        fi
                oldname=$oldname1

    fi
done
echo "------------------------------ $i"
cat $oldname
当我运行这个程序时,相同的列被加起来了,但是那些只出现在其中一个文件中的列却没有

./test.sh 
------------------------------ 3
aa 3
bb 6
cc 9
ee 4
从我在NR==FR中看到的情况来看,ff dd没有出现在列表中

我遇到过这样的情况:

但这只会在尝试时使当前问题进一步复杂化,因为许多其他字段会被复制

发布问题后-更新内容。。。和测试……

我想坚持使用awk,因为它看起来确实是一种短得多的实现结果的方法,但仍然存在一个问题

awk '{a[$1]+=$2}END{for (k in a) print k,a[k]}'  file1 file2 file3
aa 3
bb 6
cc 9
ee 4
ff 4
gg 4
RESULT_SET_4 0
RESULT_SET_3 0
RESULT_SET_2 0
RESULT_SET_1 0
$ cat file1 
RESULT_SET_1
aa 1
RESULT_SET_2
bb 2
RESULT_SET_3
cc 3
RESULT_SET_4
ff 4
$ cat file2
RESULT_SET_1
aa 1
RESULT_SET_2
bb 2
RESULT_SET_3
cc 3
RESULT_SET_4
ee 4
文件内容没有保留原来的内容,即结果不在标题下,我原来的方法确实保持了完整

更新的预期输出-正确上下文中的标题

cat file1 
RESULT_SET_1
aa 1
RESULT_SET_2
bb 2
RESULT_SET_3
cc 3
RESULT_SET_4
ff 4



cat file2 
RESULT_SET_1
aa 1
RESULT_SET_2
bb 2
RESULT_SET_3
cc 3
RESULT_SET_4
ee 4


cat file3
RESULT_SET_1
aa 1
RESULT_SET_2
bb 2
RESULT_SET_3
cc 3
RESULT_SET_4
gg 4
test.sh awk line to produce above is :

awk -v i=$i 'NR==FNR { _[$1]=$2 } NR!=FNR { if (_[$1] != "") { if  ($2 ~ /[0-9]/)   { nn=($2+_[$1]); print $1" "nn; } else { print;} }else { print; } }' $names $oldname> $oldname1

./test.sh 
------------------------------ 3
RESULT_SET_1
aa 3
RESULT_SET_2
bb 6
RESULT_SET_3
cc 9
RESULT_SET_4
ff 4
可以工作,但会破坏所需的格式

  awk '($2 != "")  {a[$1]+=$2};  ($2 == "") {  a[$1]=$2 } END {for (k in a) print k,a[k]} '  file1 file2 file3
    aa 3
    bb 6
    cc 9
    ee 4
    ff 4
    gg 4
    RESULT_SET_4 
    RESULT_SET_3 
    RESULT_SET_2 
    RESULT_SET_1 
编辑:

这有点像黑客,但它确实起到了作用:

$ awk 'FNR==NR&&!/RESULT/{a[$1]=$2;next}($1 in a){a[$1]+=$2}END{for (k in a) print k,a[k]}' file1 file2 file3 | sort | awk '$1="RESULTS_SET_"NR"\n"$1'
RESULTS_SET_1
aa 3
RESULTS_SET_2
bb 6
RESULTS_SET_3
cc 9
RESULTS_SET_4
ff 4

正如sudo_O所建议的,您可以在
awk
中执行此操作,但也可以在纯bash中执行此操作

#!/bin/bash

# We'll use an associative array, where the indexes are strings.
declare -A a

# Our list of files, in an array (not associative)
files=(file1 file2 file3)

# Walk through array of files...
for file in "${files[@]}"; do
  # And for each file, increment the array index with the value.
  while read index value; do
    ((a[$index]+=$value))
  done < "$file"
done 

# Walk through array. ${!...} returns a list of indexes.
for i in ${!a[@]}; do
  echo "$i ${a[$i]}"
done

如果您想对输出进行排序。。。您可以通过管道将其排序(
sort
:)

这里有一种使用
GNU awk
的方法。运行方式如下:

awk -f script.awk File1 File2 File3
script.awk的内容

sub(/RESULT_SET_/,"") {

    i = $1
    next
}

{
    a[i][$1]+=$2
}

END {
    for (j=1;j<=length(a);j++) {

        print "RESULT_SET_" j

        for (k in a[j]) {
            print k, a[j][k]
        }
    }
}
或者,这里有一个班轮:

awk 'sub(/RESULT_SET_/,"") { i = $1; next } { a[i][$1]+=$2 } END { for (j=1;j<=length(a);j++) { print "RESULT_SET_" j; for (k in a[j]) print k, a[j][k] } }' File1 File2 File3
awk'sub(/RESULT\u SET\uu/,“”){i=$1;next}{a[i][$1]+=$2}END{for(j=1;j使用此
基本上,它会遍历每个文件,如果条目在另一端存在,它会将条目添加到一个0值的近似行号,这样它就可以对内容进行汇总-在我当前的输出上测试了这个,并且似乎工作得很好

#!/bin/bash

 files="file1 file2 file3 file4 file5 file6 file7 file8"
RAND="$$"
i=0;
oldname="";
for names in $(echo $files); do
        ((i++));
        if [ $i == 1 ]; then
                oldname=$names
                shift;
        else
               oldname1=$names.$RAND
        for entries in $(awk -v i=$i 'NR==FNR { _[$1]=$2 } NR!=FNR { if (_[$1] == "") { if  ($2 ~ /[0-9]/)   { nn=0; nn=(_[$1]+=$2);  print FNR"-"$1"%0"} else { } } else { } }' $oldname $names); do
                line=$(echo ${entries%%-*})
                content=$(echo ${entries#*-})
                content=$(echo $content|tr "%" " ")

edit=$(ed -s $oldname  << EOF
$line
a
$content
.
w
q
EOF 
)

$edit  >/dev/null 2>&1

done

                awk -v i=$i 'NR==FNR { _[$1]=$2 } NR!=FNR { if (_[$1] != "") { if  ($2 ~ /[0-9]/)   { nn=0; nn=($2+_[$1]); print $1" "nn; } else { print $1;} }else { print; } }' $names $oldname> $oldname1
        oldname=$oldname1
    fi
done

cat $oldname
#rm file?.*
!/bin/bash
files=“file1 file2 file3 file4 file5 file6 file7 file8”
兰德=“$$”
i=0;
oldname=“”;
对于$(echo$文件)中的名称,请执行以下操作
((i++);
如果[$i==1];则
oldname=$names
转移;
其他的
oldname1=$names.$RAND
对于$(awk-vi=$i'NR==FNR{{u[$1]=$2}NR!=FNR{if({u[$1]==“”){if($2~/[0-9]/){nn=0;nn=({u[$1]+=$2);打印FNR-“$1”%0}其他{}}其他{oldname$names);执行以下操作
行=$(回显${entries%%-*})
content=$(echo${entries#*-})
内容=$(echo$content | tr“%”)
编辑=$(ed-s$oldname/dev/null 2>&1
完成
awk-v i=$i'NR==FNR{{u[$1]=$2}NR!=FNR{if({u[$1]!=”){if($2~/[0-9]/){nn=0;nn=($2+{$1]);print$1''nn else{print$1;}else{print;}else{print;}'$names$oldname>$oldname1
oldname=$oldname1
fi
完成
猫$oldname
#rm文件*

+1我几乎就是这么做的。(我可能选择了不同的单字母变量和数组名称。;)在不应该添加行的情况下仍然存在一个问题,我已经更新了原始PostThank Sudo,只要每个标题中的内容是1个字段,就可以正常工作,如果标题下有多个字段值,它将只显示第一个字段:(是的,当然。复选标记在正确的位置。我之所以包括这个问题,是因为这个问题也被标记为bash,bash脚本很有趣。什么是“标题”?在您第一次发布的示例数据中,我没有看到类似的内容。这使问题变得不同。如果您在回答问题后对问题进行了如此重大的更改,您可能不应该期望人们再次回答。是的,对不起:)我的错,报告确实有标题,那么字段值我应该重新发布一个新问题吗?现在的预期输出是什么?用预期输出更新了问题,基本上每个服务器输出都有每个段的标题,后面跟着字段及其值…有点像file1 file2的上次更新内容,预期结果是produ根据原始脚本进行ced-明显的问题仍然存在-下面的过程确实有效,但顺序丢失,标题的格式不正确。我认为这对其他人非常有用,只要他们在第一时间以不同的方式思考日志。因此,我把它作为一个项目放在这里,这是在Linux?awk’sub(/RESULT\u SET\uu/)上运行的吗,“){i=$1;next}{a[i][$1]+=$2}END{for(j=1;j./script.awk file1 file2./script.awk:line 1:意外标记附近的语法错误
/RESULT\u SET\u/,”。/script.awk:line 1:
sub(/RESULT\u SET\u/,”){'必须是gawk 4+初始gawk 3.XX不工作-cool@vahid:是的,我希望看到使用
GNU awk
解决您的问题的方法(我假设您已经安装了最新版本)。使用多维数组可以使解决方案本身变得更简单,而且它也更易于维护。如果您对它满意,请不要忘记接受它。如果不满意,请让我知道。干杯。嗨,Steve,我之前在测试这个问题,虽然在一个更简单的示例上看起来一切都很好,但在我的实际结果中,效果不太好,or每个子标题中的项目顺序都发生了变化,我还必须将标题定义为结果集、1、2,以此类推,才能使其发挥作用
sub(/RESULT_SET_/,"") {

    i = $1
    next
}

{
    a[i][$1]+=$2
}

END {
    for (j=1;j<=length(a);j++) {

        print "RESULT_SET_" j

        for (k in a[j]) {
            print k, a[j][k]
        }
    }
}
RESULT_SET_1
aa 3
RESULT_SET_2
bb 6
RESULT_SET_3
cc 9
RESULT_SET_4
ee 4
ff 4
gg 4
awk 'sub(/RESULT_SET_/,"") { i = $1; next } { a[i][$1]+=$2 } END { for (j=1;j<=length(a);j++) { print "RESULT_SET_" j; for (k in a[j]) print k, a[j][k] } }' File1 File2 File3
#!/bin/bash

 files="file1 file2 file3 file4 file5 file6 file7 file8"
RAND="$$"
i=0;
oldname="";
for names in $(echo $files); do
        ((i++));
        if [ $i == 1 ]; then
                oldname=$names
                shift;
        else
               oldname1=$names.$RAND
        for entries in $(awk -v i=$i 'NR==FNR { _[$1]=$2 } NR!=FNR { if (_[$1] == "") { if  ($2 ~ /[0-9]/)   { nn=0; nn=(_[$1]+=$2);  print FNR"-"$1"%0"} else { } } else { } }' $oldname $names); do
                line=$(echo ${entries%%-*})
                content=$(echo ${entries#*-})
                content=$(echo $content|tr "%" " ")

edit=$(ed -s $oldname  << EOF
$line
a
$content
.
w
q
EOF 
)

$edit  >/dev/null 2>&1

done

                awk -v i=$i 'NR==FNR { _[$1]=$2 } NR!=FNR { if (_[$1] != "") { if  ($2 ~ /[0-9]/)   { nn=0; nn=($2+_[$1]); print $1" "nn; } else { print $1;} }else { print; } }' $names $oldname> $oldname1
        oldname=$oldname1
    fi
done

cat $oldname
#rm file?.*