用于 gzip 文件的 Sed

用于 gzip 文件的 Sed

我有一堆 gz 文件,它们的解压缩版本包含模式AB=1(这些肯定位于A首先出现的不同行上)。

我想编写一个命令,给出当前A行和B=1当前行的内容。或者至少是介于A和之间的内容B=1

输入文件1:

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

输入文件2:

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

我的命令必须file2 的输出A ....B=1没有什么对于文件 1。

我做了类似的事情,但没有按预期工作:

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

这里有什么问题?

答案1

现在让我们忽略压缩。您想要输出A和之间的行B=1,但前提是两者都出现。您sed使用的不会这样做,因为它一A看到就开始输出,并且不检查B=1.我们可以使用保持缓冲区来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,但如果某些文件可以有部分块(A没有B),则将文件连接在一起会导致问题。假设情况并非如此,我们可以将 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脚本可能应该有自己的文件。

或者只是在 shell 中执行(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=.../ {...}

答案2

当然不是最好的方法,但它对我有用:

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

首先,我们得到一个文件列表,然后过滤包含 A 的文件,然后过滤包含 B=1 的文件,得到的文件zcatsed.

危险:如果一个文件同时包含 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

答案3

如果您有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

答案4

zcat *.gz | \
sed  's/B=[0-9].*/&\x00/'  | \
grep  -zo 'A.*B=1' | \
sed 's/\x00/\n=====\n/'
  • 第 1 行(可以用​​ find 命令替换)
  • 第 2 行在“B=...”行后添加一个 null 以显式分隔寄存器
  • 第 3 行 grep 以空分隔的寄存器序列,A...B=1 模式
  • 第 4 行(如果有用)将 null 转换为更明显的分隔符

相关内容