如何在 tar.gz 文件列表上执行与“grep something * -Rin”等效的操作?

如何在 tar.gz 文件列表上执行与“grep something * -Rin”等效的操作?

我有一堆 tar.gz 文件,我想对它们执行“grep something * -Rin”,就像对它们执行非 tar.gzed 操作一样。我想保留它们的 tar.gzed 原样,但动态地对它们执行 grep,并查找带有前缀文件和行号的 grep 出现的情况。

就像是:

grep mytoken1 *.tar.gz -Rin

并得到如下结果:

my1.tar.gz,dir1/file2:123:mytoken1 在这一行  
my2.tar.gz,dir2/file3:233:mytoken1 也在另一行中  
[...]  

有办法吗?

答案1

zgrep(或者,我们认为带有 -Z 标志的 grep)将允许您 grep 压缩文件,我认为它将告诉您很多您想要的信息,但是,如果您不花更多功夫查看标题,这不会给您文件名 :(

答案2

在发现在 .tar 或 .gz 文件中搜索的 Unix 脚本

剧本 :

for file in $(tar -tzf file.tar.gz | grep '\.txt'); do 
    tar -Oxzf file.tar.gz "$file" | grep -B 3 --label="$file" -H "string-or-regex"
done

将尊重文件边界并报告文件名。该| grep '\.txt部分可以根据您的需要进行调整或删除。

-z表示tar它已被gzip压缩。-t列出内容。 -x提取。 -O重定向到标准输出而不是文件系统。较旧的tars 可能没有-O-z标志,并且需要没有-: 的标志,例如tar tz file.tar.gz

如果您的 grep 不支持这些标志,那么可以使用 awk :

#!/usr/bin/awk -f
BEGIN { context=3; }
{ add_buffer($0) }
/pattern/ { print_buffer() }
function add_buffer(line)
{
    buffer[NR % context]=line
}
function print_buffer()
{
    for(i = max(1, NR-context+1); i <= NR; i++) {
        print buffer[i % context]
    }
}
function max(a,b)
{
    if (a > b) { return a } else { return b }
}

与 grep -B 不同,这不会合并相邻的匹配,因此可以重复两个不同匹配的 3 行内的行。

答案3

一种方法是使用这个快速技巧:

#!/usr/bin/ruby

=begin
Quick-and-dirty way to grep in *.tar.gz archives

Assumption:
    each and every file read from any of the supplied tar archives
    will fit into memory. If not, the data reading has to be rewritten
    (a proxy that reads line-by-line would have to be inserted)
=end

require 'rubygems'
gem 'minitar'
require 'zlib'
require 'archive/tar/minitar'

if ARGV.size < 2
    STDERR.puts "#{File.basename($0)} <regexp> <file>+"
    exit 1
end

regexp = Regexp.new(ARGV.shift, Regexp::IGNORECASE)

for file in ARGV
    zr = Zlib::GzipReader.new(File.open(file, 'rb'))
    Archive::Tar::Minitar::Reader.new(zr).each do |e|
        next unless e.file?
        data = e.read
        if regexp =~ data
            data.split(/\n/).each_with_index do |l, i|
                puts "#{file},#{e.full_name}:#{i+1}:#{l}" if regexp =~ l
            end
        end
    end
end

这并不是说我会推荐它用于更大的档案,因为档案中的每个文件都会被读入内存(实际上是两次)。

如果您想要一个内存效率更高的版本,那么您要么采用不同的循环实现e.read……或者,也许,采用完全不同的语言。;)

如果您真的感兴趣,我可以让它变得更高效一些......但就原始速度而言,它绝对无法与 C 或其他编译语言相比。

答案4

*nix 工具的模块化方法意味着没有简单的方法可以用 grep / tar / zcat 高效地完成此操作。理想情况下,你只需要解压文件一次,然后一次处理每个 tar 文件。这是我的尝试tgz-grep

#!/usr/bin/python
import re,sys,tarfile

exp=re.compile(sys.argv[1])
tarfiles=sys.argv[2:]

for tfile in tarfiles:
  tar=tarfile.open(tfile, mode='r|gz')
  for file in tar:
    name=file.name
    count=0
    for line in tar.extractfile(file):
      count += 1
      if exp.search(line):
        print "%s,%s:%d:%s" % (tfile, name, count, line),

注意:这不执行目录递归(-R)或不区分大小写(-i),或 GNU grep 支持的其他选项,但添加它们并不困难。

相关内容