3

私は awk を使用して複数のファイルを合計しています。これはサーバーログの解析値の要約を合計するために使用されています。最終的な全体のカウントを実際に高速化しますが、マイナーな問題に遭遇しました。ウェブは役に立ちませんでした。

次に例を示します。

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

これを実行すると、同一の列が追加されますが、ファイルの1つにのみ表示される列は追加されません

./test.sh 
------------------------------ 3
aa 3
bb 6
cc 9
ee 4

ff dd はリストに表示されません。私が見たところ、NR==FR 内にあります。

私はこれに出くわしました:

http://dbaspot.com/shell/246751-awk-comparing-two-files-problem.html

you want all the lines in file1 that are not in file2,
awk 'NR == FNR { a[$0]; next } !($0 in a)' file2 file1

If you want only uniq lines in file1 that are not in file2,
awk 'NR == FNR { a[$0]; next } !($0 in a) { print; a[$0] }'
file2
file1

しかし、他の多くのフィールドが複製されるため、これを試みたときに現在の問題がさらに複雑になるだけです

質問を投稿した後-コンテンツの更新...およびテスト....

結果を達成するためのはるかに短い方法であるように見えるので、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 
4

4 に答える 4

3
$ awk '{a[$1]+=$2}END{for (k in a) print k,a[k]}' file1 file2 file3 | sort
aa 3
bb 6
cc 9
dd 4
ee 4
ff 4

編集:

ちょっとしたハックですが、仕事はします:

$ 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
于 2013-02-14T14:14:17.327 に答える
2

awksudo_O が提案したように、これを で行うことができますが、純粋な 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

そして結果:

$ ./doit
dd 4
aa 3
ee 4
bb 6
ff 4
cc 9

そして、出力を並べ替えたい場合は、パイプでパイプすることができますsort。:)

于 2013-02-14T14:27:27.783 に答える
1

を使用する 1 つの方法を次に示し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]
        }
    }
}

結果:

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
于 2013-02-14T15:53:54.230 に答える
0

これを使用して修正基本的には各ファイルを通過し、エントリが反対側に存在する場合は、内容を合計できるように行番号を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?.*
于 2013-02-15T09:51:45.333 に答える