我有一堆 gz 文件,它们的解压缩版本包含模式A
和B=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 的文件,得到的文件zcat
是sed
.
危险:如果一个文件同时包含 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 转换为更明显的分隔符