単純なgrep
と_grep -v
_を実行しようとしているため、_a.txt
_ではなく_b.txt
_に存在する_c.txt
_から行を取得します。
_a.txt
_:
_a
b
c
d
e
_
_up.txt
_:
_a.up
b.up
c.up
_
_dw.txt
_:
_a.dw
b.dw
_
望ましい出力:
_c
_
私は以下のコードを書きましたが、grep
は$(sed...)
を全体ではなく一度に1行として調べます。
_sed 's/.up//' /tmp/b.txt | grep -f /tmp/a.txt | grep -vf $(sed 's/.dw//' /tmp/c.txt)
_
ファイルがすべて並べ替えられており、プロセス置換(bash
など)を理解するシェルを使用していると仮定します。
_$ join -t . -v 1 -o 0 <( join -t . a.txt b.txt ) c.txt
c
_
または、他のシェルの場合、
_$ join -t . a.txt b.txt | join -t . -v 1 -o 0 - c.txt
c
_
これはjoin
を2回使用して、ファイル間のリレーショナル結合を実行します。データはドット区切りフィールドとして解釈されます(_-t .
_を使用)。
_a.txt
_と_b.txt
_の結合は単純であり、
_a.up
b.up
c.up
_
これらは、最初のドット区切りフィールドが両方のファイルにある2つのファイルのすべての行です。出力は、結合フィールド(a
、b
、c
)と、その後に続く両方のファイルの他のフィールドで構成されます(_b.txt
_のみが追加のデータを持っています)。
2番目の結合は少し特別です。 _-v 1
_を使用すると、2番目のファイル_c.txt
_のどの行ともペアにできない最初のファイル(上記の中間結果)のエントリを確認するよう求められます。さらに、結合フィールド自体(_-o 0
_)のみを表示するように要求します。 _-o
_フラグがないと、結果として_c.up
_が返されます。
ファイルが並べ替えられていない場合、コマンド内で出現するファイル名file
を<( sort file )
で置き換えることができます。
単一の高速GNU awk
コマンド:
awk -F'.' \
'{
if (ARGIND == 1) a[$1];
else if (ARGIND == 2 && $1 in a) comm[$1];
else if (ARGIND == 3){
delete a;
if ($1 in comm) delete comm[$1]
}
}
END{ for (i in comm) print i }' a.txt b.txt c.txt
出力:
c
-F'.'
-扱います.
フィールド区切りとしてARGIND
-処理中の現在のファイルのARGV
(コマンドライン引数の配列)内のインデックスcomm
-最初の2つのファイル間のcommon項目の配列(a.txt
およびb.txt
)ファイルがソートされ、重複する行が削除されたと想定します。
_comm -12 a.txt <(cut -d. -f1 b.txt) | comm -23 - <(cut -d. -f1 c.txt)
_
これは、BashとGNUユーティリティを使用して、Ubuntu用に書かれていますが、うまくいけば、他のOSでも機能します。
comm -12
_両方のファイルが共有する行を出力します(詳細については_man comm
_をお読みください)<(...)
プロセス置換-入力ファイルの代わりにコマンドを使用しますcut -d. -f1
_各行で、最初のドット以降をすべて削除しますcomm -23
_最初のファイルに固有の行を出力します-
_ファイルではなく標準入力から読み取る指定されたファイルが並べ替えられ、内部に重複がない場合は、次のように使用します。
_$ comm -12 a.txt <(sed 's/\.[^.]*$//' up.txt) | comm -23 - <(sed 's/\.[^.]*$//' dw.txt)
_
プロセス置換(<(…)
)を持つシェル。他のシェルについては、以下をお読みください。
この文章で説明すること:
c.txtではなくb.txtに存在するa.txtから行を取得する
集合演算に減らすことができます:
_( a intersect b ) complement c
_
ファイルに対してset操作を実行するにはいくつかの方法があります 多くはこの回答にリストされています
コマンドcomm
がほとんどの操作を実行できる方法が好きです。
しかし、あなたが提示するファイルは、使用するためのクリーンなセットではありません。拡張機能は消去/削除する必要があります。 sedで拡張機能を削除する一般的な方法は次のとおりです。
_$ sed 's/\.[^.]*$//' file
_
したがって、2つのクリーンファイルは次のように作成されます。
_$ sed 's/\.[^.]*$//' up.txt > up.txt.clean
$ sed 's/\.[^.]*$//' dw.txt > dw.txt.clean
_
これら2つのファイルを使用すると、ワンライナーソリューションは次のようになります。
_$ comm -12 a.txt up.txt.clean | comm -23 - dw.txt.clean
c
_
または、_( up.txt complement dw.txt) intersect a.txt
_を実行します。
_$ comm -23 up.txt.clean dw.txt.clean | comm -12 - a.txt
c
_
両方のコマンドは、いくつかのシェルで元のファイルから直接実装できます。
_$ comm -12 a.txt <(sed 's/\.[^.]*$//' up.txt) | comm -23 - <(sed 's/\.[^.]*$//' dw.txt)
_
プロセス置換が利用できない場合、次のように1つのファイルのみを使用することが可能です。
_$ sed 's/\.[^.]*$//' up.txt | comm -12 a.txt - >result1.txt
$ sed 's/\.[^.]*$//' dw.txt | comm -23 result1.txt -
c
$ rm result1.txt
_
grep
、sort
&uniq
、およびsed
を使用するのと同様の別の代替方法を次に示します。
$ sed 's/\.\(dw\|up\)//' up.txt dw.txt | grep -xFf a.txt | sort | uniq -u
c
これは、up.txt
をgrep
への入力ファイルとして使用し、dw.txt
およびa.txt
の各ファイルの一致リストを作成することで機能します。これにより、次のような出力が生成されます。
$ sed 's/\.\(dw\|up\)//' up.txt dw.txt | grep -xFf a.txt
a
b
c
a
b
ここでの重要な詳細は、次のとおりです。
sed
を使用して、2つのファイルup.txt
とdw.txt
から末尾の拡張子を削除するgrep
を使用して、a.txt
から対応する一致をフィルタリングしますgrep
に実行するように指示するマッチングは正確です-x
-F
はgrep
にa.txt
のパターンを固定文字列として扱うように指示します上記の出力を手に入れれば、これをsort
を介して実行し、uniq
を使用して繰り返されない行のみを取得できます。
$ grep -f a.txt <(cut -d '.' -f 1 up.txt) > common.txt
$ grep -vf <(cut -d '.' -f 1 dw.txt) common.txt
2つのファイル間で最初のWordを比較し、一致するWordにcommon.txt
を書き込みます。 dw.txt
とcommon.txt
を比較し、逆一致を出力します。 「c」。
Perl -F\\. -lane '
$h{@ARGV}{$F[0]}++,next if @ARGV;
print if exists $h{2}{$_} && !exists $h{1}{$_};
' up.txt dw.txt a.txt
ハッシュ%hを作成し、トップレベルのキーを「2」と「1」として、2が最初の引数(up.txt)を参照し、1がdw.txtを参照します。指定されたデータの場合、ハッシュ構造は次のようになります(順序は異なる場合があります)。
%h = (
1 => { a => 1, b => 1, },
2 => { a => 1, b => 1, c => 1, },
);
見てわかるように、メインハッシュ%h内には2つのミニハッシュがあります。したがって、3番目の引数(a.txt)を読み取るときが来たら、そのレコードが(キーとして)ミニハッシュ%2に表示され、かつ、メインハッシュ%h内のミニハッシュ%1(ハッシュのハッシュまたはHoHとも呼ばれます)。
Roman's answer のバリエーションで、簡単にするために:
gawk -F. 'ARGIND==1{ seen[$1]; next }
ARGIND==2{ delete seen[$1]; next }
($1 in seen)
' fileUP fileDW fileA
ARGIND==1{ seen[$1]; next }
fileUPの最初の列を、seen
という名前の関連付けられた配列に保持します。ARGIND==2{ delete seen[$1]; next }
fileDWに存在するものを削除します。($1 in seen)
がfileAにも存在する場合、残りの印刷を行います