両方のファイルに次の内容があるとします。
$ cat ttest1.txt
x = 1
x = 3
y = 5
$ cat ttest2.txt
x = 4
x = 10
y = 3
x
ファイルを再帰的にgrepし、各ファイルの最後のインスタンスを印刷したいと思います。したがって、希望の出力は次のようになります。
ttest1.txt:x = 3
ttest2.txt:x = 10
次のgrep
組み合わせはtail
1つのファイルに対しては機能しますが、複数のファイルでは機能しません。
$ grep x ttest1.txt
x = 1
x = 3
$ grep x ttest1.txt | tail -n 1
x = 3
$ grep -r x ttest* | tail -n 1
ttest2.txt:x = 10
この問題をどのように解決できますか?
答え1
するとき
grep -r x ttest*
あなたはストリームを得る
ttest1.txt:x = 1
ttest1.txt:x = 3
ttest2.txt:x = 4
ttest2.txt:x = 10
これは予想通りtail -1
ですttest2.txt:x = 10
。
grep | tail
ただし、マージされたストリームではなく、個々のファイルごとに結合を実行したいと思います。したがって、個々のファイルごとにこれを実行してください。
for f in ttest* ; do
grep x "$f" | tail -n 1
done
答え2
を使用すると、awk
正規表現文字列を使用できます(参照:動的正規表現)、各入力ファイルの最後の一致を覚えて印刷します。
$ awk -v regex="x" 'FNR==1 && m!=""{print m;m=""}
$0 ~ regex{m=FILENAME ":" $0}
END{if (m!="")print m}' ttest*
ttest1.txt:x = 3
ttest2.txt:x = 10
以下と再帰的に結合されますfind
。
$ find . -name 'ttest*' -type f -exec awk -v regex="x" '
FNR==1 && m!=""{print m;m=""}
$0 ~ regex{m=FILENAME ":" $0}
END{if (m!="")print m}' {} +
./ttest1.txt:x = 3
./ttest2.txt:x = 10
答え3
GNU sedの使用:
sed -sn '/x/h; $ { x; F; p; }' ttest*
出力:
ttest1.txt
x = 3
ttest2.txt
x = 10
paste -d: - -
結果を1行に表示するには、このオプションを渡してください。 -changeコマンドx
の後にチェックが含まれていないファイルから出力したくない場合は、x
次のようにします。{ x; /./ { F; p; z; }; }
答え4
1つの方法は次のとおりです。
grep x <yourpath>* | tac | awk 'BEGIN{FS=":"};seen!=$1{print $0;seen=$1}'
tac は awk ジョブを準備するために grep 結果を反転し、各ファイルから最初の一致のみを印刷します。