Linuxシステムには6つの.txtファイルがあり、各ファイルには約1700行があります。この6つのファイルのうち6つ、次に5/6、4/6、3/6、最後の6つのファイルに共通の行を見たいと思います。次のコードを試してみました。
comm -12 2.txt 3.txt | comm -12 - 4.txt | comm -12 - 5.txt | comm -12 - 6.txt | comm -12 - 7.txt
しかし、結果は不完全で互換性がないことがわかりました。その後、いくつかの調査を行い、次のコードを試しました。
awk 'NR==FNR { lines[$0]=1; next } $0 in lines' 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt
しかし、それも動作しません。
私はUbuntuに初めて触れたので、非常に基本的なものだけを知っています。私を助けてくれてありがとう。よろしくお願いします。
編集1:@terdonの提案に従って、次のコードも試しました。
sort file1 file2 file3 file4 file5 file6 | uniq -c
それは私に次のような結果を与えます:
3 VAL455-Main VAL451-Main
1 VAL455-Main VAL451-Main
3 VAL463-Main ALA459-Main
1 VAL463-Main ALA459-Main
3 VAL468-Main LEU466-Main
1 VAL468-Main LEU466-Main
3 VAL468-Main LYS464-Main
「VAL468-Main LEU466-Main」を見てみましょう。ご覧のとおり、出力には2つの異なる結果があります。これは、対応する行が3つのファイルに同時に存在し、1つのファイルにのみ存在することを意味します。ただし、この行はすべてのファイルに表示されます。
だから我々はまだ解決策を見つけることができませんでした。
編集2:@FelixJNのおかげで問題の原因を見つけました。同様の状況に直面している場合は、彼の答えを読んでください。 :)
答え1
一緒にawk
次のことができます。
#skip if multiple appearance in one file
{if ( seenin[$0] ~ FILENAME ) {next}}
#add filename to list of files the line has been seen in, increase seen counter
{seenin[$0]=seenin[$0]" "FILENAME ; nseen[$0]++}
#print
END {for (line in nseen) { if (nseen[line]>1) {
printf "%s \"%s\" %s %d %s %s\n",
"line",line,"seen in",nseen[line],"files:",seenin[line]} } }
制限事項:すべての行がRAMに保持されるため、メモリです。
発生回数に基づいてソートするには、それに応じて印刷コマンドを調整する必要があります(例:値によるソート)nseen
。簡単ですgawk
。END
ブロックに次のbefore -loopを追加しますfor
。
PROCINFO["sorted_in"]="@val_num_desc"
入力ファイル:
$ cat file1
a
a
b
b
c
d
e
$ cat file2
c
c
x
z
e
y
z
f
$ cat file3
f
i
a
c
z
i
k
出力(gawk
配列巡回機能付きPROCINFO
)
$awk -f compare_lines_multifiles.awk file1 file2 file3
line "c" seen in 3 files: file1 file2 file3
line "z" seen in 2 files: file2 file3
line "a" seen in 2 files: file1 file3
line "e" seen in 2 files: file1 file2
line "f" seen in 2 files: file2 file3
編集する:
提供されたファイルはMSDOS形式です。変換して
dos2unix file1.txt file2.txt ....
またはでレコード区切り記号を調整しますawk
。コードの最初の項目として次を追加します。
BEGIN { RS="\r\n" }
編集2:ファイルに不規則な区切り記号があります。問題は、 と がa<tab>b
別a<tab>b<tab>
の行で処理されるのに対し、同じと考えることができるということです。
ファイルごとに2つの関心フィールドがある特別な場合は、行全体ではなく2つのフィールドの内容を比較することをお勧めします。 MSDOS 形式も考慮してください。
BEGIN { RS="\r\n" }
#skip if multiple appearance in one file
{if ( seenin[$1"\t"$2] ~ FILENAME ) {next}}
#add filename to list of files the line has been seen in, increase seen counter
{seenin[$1"\t"$2]=seenin[$1"\t"$2]" "FILENAME ; nseen[$1"\t"$2]++}
#print
END {for (line in nseen) { if (nseen[line]>1) {
printf "%s \"%s\" %s %d %s %s\n",
"line",line,"seen in",nseen[line],"files:",seenin[line]} } }
結局、6つのファイルはすべて重複していました。タブ区切り記号がある2つのフィールドに焦点を合わせ、1行の出力を印刷します。
答え2
私は別のアプローチを提案したいと思います。すべてを繰り返しながら、sort
各行uniq -c
が何回表示されるかを計算します。
sort 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt | uniq -c
これにより、各行が1回印刷されますが、その行が表示された回数も印刷されます。たとえば、次の3つのファイルがあるとします。
$ cat file1
dog
cat
bird
$ cat file2
fly
bird
moose
$ cat file3
bird
dog
flea
以下の結果が出力されます。
$ sort file1 file2 file3 | uniq -c
3 bird
1 cat
2 dog
1 flea
1 fly
1 moose
したがって、見つかった回数に基づいて行を区切るには、次のようにして3つ(またはあなたの場合は6つ)ファイルのすべてに表示される行のみを表示できます。
$ sort file1 file2 file3 | uniq -c | awk '$1==3'
3 bird
$ sort file1 file2 file3 | uniq -c | awk '$1==2'
2 dog
$ sort file1 file2 file3 | uniq -c | awk '$1==1'
1 cat
1 flea
1 fly
1 moose
答え3
最初の試みは正しいアプローチです。
comm -12 2.txt 3.txt | comm -12 - 4.txt | comm -12 - 5.txt | comm -12 - 6.txt | comm -12 - 7.txt
これは、ジョブを並列に完了するストリームのように機能します。原則として、数百万行のファイルをこの方法で処理できます。
あなたが直面する問題コミュニケーション(1) 入力の問題、つまり空白や行末が原因で発生したようです。このようなものを先にまとめてみると、元の方法が早くて便利であることがわかります。
これを示す例があります。少数の配列に分けることができる数字を見つけます。
$ for D in 2 3 5 7 11 13
> do seq 1 1000 |
> awk -v D=$D '$0 % D == 0 { print $0 }' |
> sort > $D
> done
$ comm -12 2 3 | comm -12 - 5 | comm -12 - 7
210
420
630
840
1から1000までの数字は2、3、5、7、11に分割されていないことがわかりました。
答え4
# spits a random three char (a..e)
# string each time it's called
rx() {
< /dev/urandom \
tr -cd 'a-e' |
fold -w3 |
head -n 1 \
;
}
# modify this section
numFiles=6
numLines=70
commIn=5
# generate random files
for j in $(seq "$numFiles"); do
for i in $(seq "$numLines"); do
rx
done > $j.txt
done
# uniquify each file
# then sort the combined pool
# count n tabulate uniques from this pool
for i in $(seq "$numFiles"); do
sort -u < "$i.txt"
done |
sort | uniq -c |
sort -b -k1,1nr -k2,2 |
awk -v n="$commIn" '$1+0==n' ;
このタスクの結果:
5 acc
5 bcd
5 dac
5 eea