Ответ 1
Если у вас zgrep
, вы можете использовать
zgrep -a string file.tar.gz
Я пытаюсь использовать grep шаблон из дюжины файлов .tar.gz, но очень медленный
используя
tar -ztf file.tar.gz | while read FILENAME
do
if tar -zxf file.tar.gz "$FILENAME" -O | grep "string" > /dev/null
then
echo "$FILENAME contains string"
fi
done
Если у вас zgrep
, вы можете использовать
zgrep -a string file.tar.gz
Вы можете использовать опцию --to-command
для передачи файлов в произвольный скрипт. Используя это, вы можете обработать архив за один проход (и без временного файла). Смотрите также этот вопрос и руководство. Вооружившись приведенной выше информацией, вы можете попробовать что-то вроде:
$ tar xf file.tar.gz --to-command "awk '/bar/ { print ENVIRON[\"TAR_FILENAME\"]; exit }'"
bfe2/.bferc
bfe2/CHANGELOG
bfe2/README.bferc
Если это происходит очень медленно, я подозреваю, что вы имеете дело с большим архивом. Он собирается распаковать его один раз, чтобы извлечь список файлов, а затем разархивировать его N раз - где N - количество файлов в архиве - для grep. В дополнение ко всему разжатию, вам придется сканировать честный бит в архив каждый раз, чтобы извлечь каждый файл. Один из tar
самых больших недостатков заключается в том, что в начале нет оглавления. Там нет эффективного способа получить информацию обо всех файлах в архиве и только прочитать эту часть файла. По сути, он должен читать весь файл до того, что вы извлекаете каждый раз; он не может сразу перейти к местоположению имени файла.
Самое простое, что вы можете сделать, чтобы ускорить это, - сначала распаковать файл (gunzip file.tar.gz
), а затем работать с файлом .tar
. Это само по себе может помочь. Тем не менее, он все равно будет проходить через весь архив N раз.
Если вы действительно хотите, чтобы это было эффективно, единственный вариант - полностью извлечь все из архива перед его обработкой. Поскольку ваша проблема - это скорость, я подозреваю, что это гигантский файл, который вы не хотите извлекать первым, но если это возможно, это ускорит многое:
tar zxf file.tar.gz
for f in hopefullySomeSubdir/*; do
grep -l "string" $f
done
Обратите внимание, что grep -l
печатает имя любого подходящего файла, завершает работу после первого совпадения и не работает, если нет совпадения. Только это ускорит часть grepping вашей команды, поэтому даже если у вас нет места для извлечения всего архива, grep -l
поможет. Если файлы огромны, это очень поможет.
Я знаю, что этот вопрос 4 года, но у меня есть несколько разных вариантов:
tar --to-command grep
Следующая строка будет выглядеть в example.tgz
для PATTERN
. Это похоже на пример @Jester, но я не мог заставить его шаблон работать.
tar xzf example.tgz --to-command 'grep --label="$TAR_FILENAME" -H PATTERN ; true'
tar -tzf
Вторая опция использует tar -tzf
, чтобы перечислить файлы, а затем пройти через grep
. Вы можете создавать функцию для ее использования снова и снова:
targrep () {
for i in $(tar -tzf "$1"); do
results=$(tar -Oxzf "$1" "$i" | grep --label="$i" -H "$2")
echo "$results"
done
}
Использование:
targrep example.tar.gz "pattern"
Для начала вы можете запустить несколько процессов:
tar -ztf file.tar.gz | while read FILENAME
do
(if tar -zxf file.tar.gz "$FILENAME" -O | grep -l "string"
then
echo "$FILENAME contains string"
fi) &
done
( ... ) &
создает новый отсоединенный (read: родительская оболочка не ждет дочернего элемента)
процесс.
После этого вы должны оптимизировать извлечение своего архива. Чтение не представляет проблемы, поскольку ОС должна была кэшировать доступ к файлу уже. Однако, tar необходимо распаковать архив каждый раз, когда цикл работает, что может быть медленным. Распаковка архива один раз и повторение результата может помочь здесь:
local tempPath=`tempfile`
mkdir $tempPath && tar -zxf file.tar.gz -C $tempPath &&
find $tempPath -type f | while read FILENAME
do
(if grep -l "string" "$FILENAME"
then
echo "$FILENAME contains string"
fi) &
done && rm -r $tempPath
find
используется здесь, чтобы получить список файлов в целевом каталоге tar
, который мы итерируем, для каждого файла, который ищет строку.
Изменить: Используйте grep -l
, чтобы ускорить работу, как заметил Джим. От man grep
:
-l, --files-with-matches
Suppress normal output; instead print the name of each input file from which output would
normally have been printed. The scanning will stop on the first match. (-l is specified
by POSIX.)
Весь приведенный выше код был действительно полезен, но ни один из них полностью не отвечал моей собственной потребности: grep
всех файлов *.tar.gz
в текущем каталоге, чтобы найти шаблон, указанный в качестве аргумента в повторно используемом сценарии для вывода:
Это то, на что я действительно надеялся, что zgrep
может сделать для меня, а это просто невозможно.
Вот мое решение:
pattern=$1
for f in *.tar.gz; do
echo "$f:"
tar -xzf "$f" --to-command 'grep --label="'basename $TAR_FILENAME'" -Hin '"$pattern ; true";
done
Вы также можете заменить строку tar
на следующую, если вы хотите проверить правильность расширения всех переменных с помощью базового выражения echo
:
tar -xzf "$f" --to-command 'echo "f:'basename $TAR_FILENAME' s:'"$pattern\""
Позвольте мне объяснить, что происходит. Надеемся, что цикл for
и echo
файла архива, о котором идет речь, очевидны.
tar -xzf
: x
extract, z
filter через gzip, f
на основе следующего архивного файла...
"$f"
: файл архива, предоставленный циклом for (например, то, что вы получите, выполнив ls
) в двойных кавычках, чтобы позволить переменной расширяться и гарантировать, что скрипт не будет разбит любыми именами файлов с пробелами, так далее.
--to-command
: передать вывод команды tar другой команде, вместо того, чтобы фактически извлекать файлы в файловую систему. Все, что после этого указывает, что это за команда (grep
) и какие аргументы мы передаем этой команде.
Давайте разбить эту часть на себя, так как это "секретный соус" здесь.
'grep --label="'basename $TAR_FILENAME'" -Hin '"$pattern ; true"
Во-первых, мы используем одинарную кавычку для запуска этого чанка, чтобы выполняемая basename $TAR_FILENAME
(basename $TAR_FILENAME
) не была сразу расширена/разрешена. Подробнее об этом через минуту.
grep
: команда, запускаемая с извлеченных файлов (но не с них)
--label=
: Метка для добавления результатов, значение которых заключено в двойные кавычки, поскольку мы хотим, чтобы команда grep
разрешила переменную среды $TAR_FILENAME
переданную командой tar
.
basename $TAR_FILENAME
: запускается как команда (в окружении обратных галочек), удаляет путь к каталогу и выводит только имя файла
-Hin
: H
Показать имя файла (-Hin
меткой), i
Поиск без -Hin
регистра, n
Показать номер строки соответствия
Затем мы "заканчиваем" первую часть командной строки одинарной кавычкой и запускаем следующую часть двойной кавычкой, чтобы можно было разрешить $pattern
, переданный в качестве первого аргумента.
Понимая, какие цитаты мне нужно было использовать, это была та часть, которая дала о себе знать больше всего. Надеюсь, все это имеет смысл для вас и помогает кому-то еще. Кроме того, я надеюсь, что смогу найти это через год, когда мне это понадобится снова (и я забыл о сценарии, который я уже сделал для него!)
И прошло несколько недель с тех пор, как я написал выше, и это все еще супер полезно... но это было не совсем достаточно хорошо, так как файлы накапливались, и поиск вещей стал более запутанным. Мне нужен был способ ограничить то, на что я смотрел, датой файла (только просматривая более свежие файлы). Так вот этот код. Надеюсь, это довольно очевидно.
if [ -z "$1" ]; then
echo "Look within all tar.gz files for a string pattern, optionally only in recent files"
echo "Usage: targrep <string to search for> [start date]"
fi
pattern=$1
startdatein=$2
startdate=$(date -d "$startdatein" +%s)
for f in *.tar.gz; do
filedate=$(date -r "$f" +%s)
if [[ -z "$startdatein" ]] || [[ $filedate -ge $startdate ]]; then
echo "$f:"
tar -xzf "$f" --to-command 'grep --label="'basename $TAR_FILENAME'" -Hin '"$pattern ; true"
fi
done
И я не могу перестать подправлять эту штуку. Я добавил аргумент для фильтрации по имени выходных файлов в файле tar. Подстановочные знаки тоже работают.
Использование:
targrep.sh [-d <start date>] [-f <filename to include>] <string to search for>
Пример:
targrep.sh -d "1/1/2019" -f "*vehicle_models.csv" ford
while getopts "d:f:" opt; do
case $opt in
d) startdatein=$OPTARG;;
f) targetfile=$OPTARG;;
esac
done
shift "$((OPTIND-1))" # Discard options and bring forward remaining arguments
pattern=$1
echo "Searching for: $pattern"
if [[ -n $targetfile ]]; then
echo "in filenames: $targetfile"
fi
startdate=$(date -d "$startdatein" +%s)
for f in *.tar.gz; do
filedate=$(date -r "$f" +%s)
if [[ -z "$startdatein" ]] || [[ $filedate -ge $startdate ]]; then
echo "$f:"
if [[ -z "$targetfile" ]]; then
tar -xzf "$f" --to-command 'grep --label="'basename $TAR_FILENAME'" -Hin '"$pattern ; true"
else
tar -xzf "$f" --no-anchored "$targetfile" --to-command 'grep --label="'basename $TAR_FILENAME'" -Hin '"$pattern ; true"
fi
fi
done
Эта опция действительно жизнеспособна: zcat log.tar.gz | grep -a -i "строка"
Это напечатает всю строку, которая соответствует вашему шаблону. zgrep действительно не дает полезного вывода.
$ zgrep -i 'CDF_FEED' FeedService.log.1.05-31-2019-150003.tar.gz | more
Binary file (standard input) matches
$ zcat FeedService.log.1.05-31-2019-150003.tar.gz | grep -ai 'CDF_FEED'
2019-05-30 19:20:14.568 ERROR 281 --- [http-nio-8007-exec-360] DrupalFeedService : CDF_FEED_SERVICE::CLASSIFICATION_ERROR:408: Classification failed even after maximum retries for url : abcd.html