5

У меня есть куча файлов tar.gz, и я хочу сделать «grep что-то * -Rin», как если бы они не были tar.gzed. Я хочу, чтобы они сохранялись в неизменном виде, но сгораю на них на лету и нахожу вхождения моего grep с префиксным файлом и номером строки.

Что-то вроде:

grep mytoken1 *.tar.gz -Rin

и получить что-то вроде:

my1.tar.gz,dir1/file2:123:mytoken1 is in this line  
my2.tar.gz,dir2/file3:233:mytoken1 is also in this other line  
[...]  

Есть ли способ сделать это?

5 ответов5

5

zgrep (или, как мы полагаем, grep с флагом -Z) позволит вам создавать сжатые файлы grep, и я думаю, что многое скажет вам о том, что вы хотите, но это не даст вам имя файла, если немного больше не смотреть на заголовок :(

4

Найден в скрипте Unix для поиска в файле .tar или .gz :

Сценарий :

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 перенаправляет на стандартный вывод, а не на файловую систему. Старые tar ы могут не иметь флаг -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 строк двух разных совпадений.

2

Одним из способов было бы использовать этот быстрый взлом:

#!/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 или другими скомпилированными языками с точки зрения сырой скорости.

0

Я думаю, что это будет очень сложно.

На самом деле tar представляет собой объединение всех включаемых файлов с добавлением заголовков. Таким образом, в основном можно написать функцию grep-in-tar чтобы справиться с этим и предоставить информацию о файле и номере строки (базовый grep с чтением заголовка и вычитанием номера строки). Я не слышал о такой программе.

Проблема с gzip. Это формат сжатия, поэтому вам нужно распаковать его, если вы хотите получить доступ к содержимому.

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

был бы способ сделать то, что вы хотите. На данный момент вы можете попробовать gunzip -c files.tgz | grep -Rin но он просто скажет, что двоичный файл совпадает.

0

Модульный подход к * nix tools означает, что не существует простого способа сделать это эффективно с помощью 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, но добавить их будет непросто.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .