web-dev-qa-db-ja.com

tar.gzファイルのリストで「grepsomething * -Rin」と同等の方法を実行するにはどうすればよいですか?

Tar.gzファイルがたくさんあるので、tar.gzedでない場合と同じように、「grepsomething * -Rin」を実行したいと思います。 tar.gzedをそのままにしておきたいのですが、grepをオンザフライで実行し、ファイルと行番号がプレフィックスとして付いたgrepの出現箇所を見つけます。

何かのようなもの:

 grep mytoken1 * .tar.gz -Rin 

次のようなものを取得します。

 my1.tar.gz、dir1/file2:123:mytoken1はこの行にあります
 my2.tar.gz、dir2/file3:233:mytoken1もこの別の行にあります
 [...] 

これを行う方法はありますか?

5
719016

zgrep(または、-Zフラグを指定したgrep)を使用すると、圧縮ファイルをgrepできます。必要な情報の多くがわかると思いますが、これでは、ファイル名を確認する必要があります。ヘッダー:(

5
Joe

。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パーツは、ニーズに合わせて調整することも、削除することもできます。

-ztargzip圧縮されていることを通知します。-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とは異なり、隣接する一致を合体させないため、2つの異なる一致の3行以内にある行を繰り返すことができます。

4
harrymc

1つの方法は、このクイックハックを使用することです。

#!/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

これは、アーカイブの各ファイルがメモリに読み込まれるため(実際には2回)、より大きなアーカイブに推奨するということではありません。

もう少しメモリ効率の高いバージョンが必要な場合は、e.readループの別の実装を使用するか、おそらく完全に別の言語を使用する必要があります。 ;)

あなたが本当に興味を持っているなら、私はそれをもう少し効率的にすることができます...しかし、それは生の速度の点で、Cや他のコンパイルされた言語とは間違いなく比較されません。

2
Wejn

これは非常に難しいと思います。

実際、tarは基本的に、ヘッダーが追加された、すべてのインクルードファイルの連結です。したがって、基本的にはgrep-in-tar関数を記述して、それを処理し、ファイルと行番号に関する情報を提供できます(ヘッダーの読み取りと行番号の減算を伴う基本的なgrep)。私はそのようなプログラムについて聞いたことがありません。

問題はgzipにあります。これは圧縮形式であるため、コンテンツにアクセスする場合は解凍する必要があります。

gunzip -c files.tgz | grep-in-tar

あなたがやりたいことをする方法になるでしょう。現時点ではgunzip -c files.tgz | grep -Rinを試すことができますが、バイナリファイルが一致すると表示されます。

0
M'vy

* nixツールへのモジュラーアプローチは、grep/tar/zcatでこれを効率的に行う簡単な方法がないことを意味します。理想的には、ファイルを1回だけ解凍し、各tarファイルを1回のパスで処理する必要があります。これが私の試みです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)やcase-insensitvity(-i)、またはGNU grepでサポートされている他のオプションは実行しませんが、追加するのは難しいことではありません。

0
Phil