2017-09-21 9 views
2

質問Unix command to find lines common in two filesているタスクを行うにcommコマンドを使用することを示唆answercommコマンドからの出力を3つの別々のファイルに取得する方法は?

comm -12 1.sorted.txt 2.sorted.txt 

これは、2つのファイルに共通な行(-1のみ、最初のファイルにある行を抑制することを示しているが、 -2は、2番目のファイルの行だけを抑制し、両方のファイルに共通の行を出力として残します)。ファイル名が示唆するように、入力ファイルはソートされた順序でなければなりません。その質問へcomment

baporsは尋ねる:

をどのように1は、別のファイルに出力を持っているでしょうか?

は、明確化を求めて、私は尋ねた:あなたが一つのファイルにのみFile1の中の行をしたい場合は

、第三の唯一の他ではFile2の中のもの、との両方のもの、そして(なしことを提供しますファイル内の行の先頭はタブで始まります)、sedを使用して出力を3つのファイルに分割することができます。

ユーザーbaporsが確認さ:

それは私が求めていたまさにです。あなたは例を見せますか?

答えは比較的長引くため、他の質問に対する回答の単純さが損なわれることがあります(多くの情報で溺れてしまいます)ので、私はここで個別に質問しました。 。

答えて

2

sedを使用する基本的な解決策は、commが接頭辞なしの最初のファイルにのみ行を出力するという事実に依存しています。 1つのタブで2番目のファイルにしかない行を出力します。 2つのタブで両方のファイルにある行を出力します。

また、ファイルに書き込むコマンドはsedwコマンドに依存しています。

1.line-1 
1.line-2 
1.line-4 
1.line-6 
2.line-2 
3.line-5 

と含む2.sorted.txtファイル:

1.line-3 
2.line-1 
2.line-2 
2.line-4 
2.line-6 
3.line-5 

comm 1.sorted.txt 2.sorted.txtからの基本的な出力は次のとおりです。

1.line-1 
1.line-2 
     1.line-3 
1.line-4 
1.line-6 
     2.line-1 
       2.line-2 
     2.line-4 
     2.line-6 
       3.line-5 

含むファイルscript.sed考える:を含むファイル1.sorted.txtを考えると

/^\t\t/ { 
    s/// 
    w file.3 
    d 
} 
/^\t/ { 
    s/// 
    w file.2 
    d 
} 
/^[^\t]/ { 
    w file.1 
    d 
} 

あなたは、以下のコマンドを実行して、このような所望の出力を得ることができます。

$ comm 1.sorted.txt 2.sorted.txt | sed -f script.sed 
$ cat file.1 
1.line-1 
1.line-2 
1.line-4 
1.line-6 
$ cat file.2 
1.line-3 
2.line-1 
2.line-4 
2.line-6 
$ cat file.3 
2.line-2 
3.line-5 
$ 

スクリプトがで動作します。削除、

2つのタブで始まる行に一致する
  1. タブを開き、file.3に行を書き込み、行を削除して(スクリプトの残りの部分は無視されます)、
  2. 行が1のタブで始まり、行を削除して(残りのスクリプトは無視されます)、
  3. と一致する行は、タブで始まらず、行をfile.1に書き込み、その行を削除します。

ステップ3の一致および削除操作は、他のものよりも対称性があります。それらは省略することができ(ちょうどw file.1を残す)、このスクリプトは同じように動作します。ただし、対称性を維持するためのさらなる正当性については、script3.sedを参照してください。

GNU sedが必要です。 BSD sedは、\tエスケープを認識しません。明らかに、ファイルは\t表記の代わりに実際のタブで書かれていて、BSD sedはスクリプトでOKです。

すべてをコマンドラインで動作させることは可能ですが、それは厄介なことです(それは丁寧です)。バッシュのANSI C Quotingを使用して、あなたが書くことができます別の-eオプションでscript.sedの3「段落」のそれぞれを書き込み

$ comm 1.sorted.txt 2.sorted.txt | 
> sed -e $'/^\t\t/ { s///\n w file.3\n d\n }' \ 
>  -e $'/^\t/ { s///\n w file.2\n d\n }' \ 
>  -e $'/^[^\t]/ {  w file.1\n d\n }' 
$ 

を。 wコマンドは難解です。ファイル名とファイル名だけがスクリプトの同じ行にあるので、スクリプト内のファイル名の後には\nが使用されます。余りにも多くのスペースがありますが、レイアウトがわかるほど対称性が明確になります。 -f script.sedファイルを使用するほうが簡単でしょう。sedスクリプトをシングルクォート、ダブルクォート、バッククォートで動作させる必要があるため、Bashのコマンドラインにスクリプトを書くのが難しいため、問題を避けることができます。

最後に、2つのファイルにタブで始まる行を含めることができれば、この手法はそれを機能させるにはもっと力強い力が必要です。 1つの変形ソリューションは、Bashのprocess substitutionを利用してファイル内の行の前にプレフィックスを追加し、出力ファイルに書き込む前に後処理sedスクリプトで接頭辞を削除します。(最大8空白で置き換えタブ付き)

script3.sed - (dはまだ任意であるが、同様に含まれていてもよい)第三の段落に必要な代替s///あり、このとき注意:

/^    X/ { 
    s/// 
    w file.3 
    d 
} 
/^  X/ { 
    s/// 
    w file.2 
    d 
} 
/^X/ { 
    s/// 
    w file.1 
    d 
} 

とコマンドライン:同じ入力ファイルの場合

$ comm <(sed 's/^/X/' 1.sorted.txt) <(sed 's/^/X/' 2.sorted.txt) | 
> sed -f script3.sed 
$ 

、これは同じ出力を生成しますが、追加し、各行の先頭にXを除去することで、コードは、cませんデータの並べ替え順序を変更し、先頭にタブがある場合は処理します。

また、PerlやAwkを使用するソリューションや、commを使用する必要がないソリューションも簡単に作成できます(ファイルがメモリに収まる場合は、未ソートファイルで作業することもできます)。

+0

素晴らしい貢献。私たちはまだSOのドキュメントを持っていればよかったです。 BSD sedのユーザーに追加するべきことの1つ。もしあなたがFreeBSDにいるとしたら、あなたの '/ bin/sh'は* bashではないAlmquistシェルの間に、bashに似たCスタイルの引用を含みます。 – ghoti

+0

@RomanPerekhrest:行頭に複数のタブ/スペースを先頭に(そして単語間に)追加すると動作しないようです。 – RomanPerekhrest

+0

@RomanPerekhrest:あなたは、 'script3.sed'を使ったバリアントとプロセス置換がうまくいかないと言っていますか?データには先頭にタブがあるか、行内に複数の空白やタブがありますか?もしそうなら、私はサンプルデータを見たいと思います。あなたは電子メールで私にそれを送ってください - 私のプロフィールを見てください。考えられる1つの問題は、 'sort'と' comm'は、データがソートされた順序であることが何を意味するのかを目の当たりにしないということです。環境に 'LANG = C'を設定する必要があるかもしれません。 –

0

COMM + awkのソリューション:

複雑なサンプル・ファイル:

1.TXT

1. line-1 with spaces (    | | here 
1.line-2 
1.line-4 with tabs > 
1.line-6 
2.line-2 
     3.line-5 (tabs) 

2.txt

1.line-3 
    2.line-1 with spaces 
2.line-2 
2.line-4 
    2.line-6 with tabs 
     3.line-5 (tabs) 

仕事:

comm -12 1.txt 2.txt > file-common 
awk 'NR==FNR{ a[$0];next }!($0 in a){ print $0 > "file"ARGIND-1 }' file-common 1.txt 2.txt 
  • comm -12 1.txt 2.txt > file-commonは - file-commonファイル

  • awk ...に共通な行を保存します - 1.txtに特有のラインを印刷し、ファイルに2.txtますfile1そしてfile2それぞれ


表示結果:

head file* 
==> file1 <== 
1. line-1 with spaces (    | | here 
1.line-2 
1.line-4 with tabs > 
1.line-6 

==> file2 <== 
1.line-3 
    2.line-1 with spaces 
2.line-4 
    2.line-6 with tabs 

==> file-common <== 
2.line-2 
     3.line-5 (tabs) 
関連する問題