web-dev-qa-db-ja.com

Sed gzipファイルの場合

私はたくさんのgzファイルを持っていて、それらの解凍されたバージョンにはパターンAB=1が含まれています(これらは確かにAが最初に現れる別の行にあります)。

Aが存在し、B=1が存在する行の内容を表示するコマンドを記述したいと思います。または、少なくともAB=1の間のコンテンツを含みます。

入力ファイル1:

..A ...
...
...B=0..
...

入力ファイル2:

..A ...
...
...B=1..
...

私のコマンドは必須出力A ....B=1 for file2およびnothing file1。

私はこのようなことをしましたが、期待どおりに機能していません:

find . -name \*.gz -print0 | xargs -0 zcat | sed -n -e '/A/,/B=1/p'

ここの問題は何ですか?

4
cehptr

ここでは、圧縮を無視します。 AB=1の間の行を出力したいが、両方が表示された場合のみ。 sedが表示されるとすぐに出力を開始し、B=1をチェックしないため、使用したAはそれを行いません。 sedのホールドバッファーを使用して、B=1が見つかるまですべてを保持することができますが、awkの方が使いやすいので、次のようにします。

$ echo -en 'not this\nA\nthis\nB=1\nnot this\n' | 
  awk '/A/ {save=1} save {data = data $0 ORS} /B=0/ {save=0; data=""}  /B=1/ {save=0; printf "%s", data; data=""} '
A
this
B=1

B=0ルールは、印刷されるべきではないブロックを処理します。

次に、圧縮と複数のファイルを処理します。 find + xargsは機能しましたが、一部のファイルに部分的なブロックが含まれる場合(ABがない場合)、ファイルを連結すると問題が発生します。そうでないと仮定すると、awkを最後に固定できます。

$ find . -name foo\*.gz -print0 | xargs -0 zcat | \
  awk '/A/ {s=1} s {d = d $0 ORS} /B=0/ {s=0; d=""} 
  /B=1/ {s=0; printf "%s", d; d=""} '      

部分的なブロックを処理する必要がある場合は、各ファイルを個別に処理する必要があります。

$ find . -name foo\*.gz -print0 | xargs -0 sh -c '
  for f; do zcat "$f" |  awk '\''/A/ {s=1} s {d = d $0 ORS} 
    /B=0/ {s=0; d=""} /B=1/ {s=0; printf "%s", d; d=""} '\''; done' sh

引用は恐ろしいので、awkスクリプトはおそらく独自のファイルにする必要があります。

または、シェル(Bash/ksh/zsh)で実行するだけです。

$ shopt -s globstar    # set -o globstar in ksh
$ for f in **/*.gz ; do zcat "$f" |
  awk '/A/ {s=1} s {d = d $0 ORS} /B=0/ {s=0; d=""} 
  /B=1/ {s=0; printf "%s", d; d=""} ' ; done

AおよびB=1行ではなく)介在する行のみを印刷する場合は、/A/ {...}および/B=.../ {...}ブロックの位置を交換します。

4
ilkkachu

確かに最善の方法ではありませんが、それは私にとってはうまくいきます:

find -name "*.gz" | xargs zgrep -l A | xargs zgrep -l "B=1" | xargs zcat | sed -n '/A/,/B=1/p

最初にファイルのリストを取得し、次にAを含むファイルをフィルター処理し、次にB = 1を含むファイルをフィルター処理します。結果のファイルはzcatからsedです。

危険:1つのファイルにB = 1とAの両方がこの順序で含まれている場合、そのファイルの内容が最後に書き込まれます。

例:

$ ls /tmp/file*gz
/tmp/filea.gz  /tmp/fileb.gz
$ zcat /tmp/filea.gz
one
two
three
A
four five
six
B=1
seven
eight
nine
$ zcat /tmp/fileb.gz
one
two
three
A
four five
six
B=0
seven
eight
nine
$ find /tmp -type f -name "file*.gz" | xargs zgrep -l A | xargs zgrep -l "B=1" | xargs zcat | sed -n '/A/,/B=1/p'
A
four five
six
B=1
2
thecarpy

pcregrepがあり、それがlibzサポート付きでビルドされている場合、次のことができます。

pcregrep --include='\.gz$' -rM '(?s)A.*?B=1' .

例:

$ pcregrep --help | grep zlib
Files whose names end in .gz are read using zlib.
Files whose names end in .bz2 are read using bzlib2.
$ pcregrep --include='\.gz$' -rM '(?s)A.*?B=1' .
./1/2/3/x.gz:AAA
blih
BOB=123
./b.gz:A
blah
B=1
1
zcat *.gz | \
sed  's/B=[0-9].*/&\x00/'  | \
grep  -zo 'A.*B=1' | \
sed 's/\x00/\n=====\n/'
  • 1行目(find-commandで置き換えることができます)
  • 2行目は、「B = ...」行の後にヌルを追加して、レジスタを明示的に分離します。
  • nullで区切られたレジスタシーケンスの3行目のgreps、A ... B = 1パターン
  • 4行目(有用な場合)nullをより見やすいセパレータに変換
0
JJoao