来自tar.gz的grep没有提取[更快的]

时间:2012-12-21 02:11:13

标签: linux bash grep

我试图从十几个文件.tar.gz grep模式,但它很慢

正在使用

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

8 个答案:

答案 0 :(得分:96)

如果您有zgrep,则可以使用

zgrep -a string file.tar.gz

答案 1 :(得分:28)

您可以使用--to-command选项将文件传输到任意脚本。使用此功能,您可以一次性处理存档(并且没有临时文件)。另请参阅this questionmanual。 有了上述信息,您可以尝试以下方式:

$ tar xf file.tar.gz --to-command "awk '/bar/ { print ENVIRON[\"TAR_FILENAME\"]; exit }'"
bfe2/.bferc
bfe2/CHANGELOG
bfe2/README.bferc

答案 2 :(得分:4)

如果这真的很慢,我怀疑你正在处理一个大型存档文件。它将解压缩一次以提取文件列表,然后将其解压缩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打印任何匹配文件的名称,在第一次匹配后退出,如果不匹配则保持沉默。仅这一点就可以加快命令的抓取部分,所以即使你没有足够的空间来提取整个档案,grep -l也会有所帮助。如果文件很大,那将会有很大帮助。

答案 3 :(得分:4)

我知道这个问题是4岁,但我有几个不同的选择:

选项1:使用tar --to-command grep

以下一行将在example.tgz中查看PATTERN。这类似于@ Jester的例子,但我无法使他的模式匹配起作用。

tar xzf example.tgz --to-command 'grep --label="$TAR_FILENAME" -H PATTERN ; true'

选项2:使用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"

答案 4 :(得分:2)

对于初学者,您可以启动多个流程:

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

( ... ) &创建一个新的分离(读取:父shell不等待子进程) 过程

之后,您应该优化档案的提取。读取没问题, 因为操作系统应该已经缓存了文件访问权限。但是,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中的文件列表,我们正在迭代这些文件,搜索每个搜索字符串的文件。

编辑:正如Jim指出的那样,使用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.)

答案 5 :(得分:0)

上面的所有代码确实很有帮助,但是没有一个能完全满足我的需要: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 -xzfx提取,z通过gzip过滤,f基于以下存档文件...

"$f":for循环提供的存档文件(例如,通过执行ls得到的双引号),以允许变量扩展并确保脚本是不会被带有空格等的任何文件名破坏。

--to-command:将tar命令的输出传递给另一个命令,而不是将文件实际提取到文件系统中。此后的所有内容均指定该命令是什么(grep),以及我们要传递给该命令的参数。

让我们自己分解这部分,因为它是这里的“秘密调味料”。

'grep --label="`basename $TAR_FILENAME`" -Hin '"$pattern ; true"

首先,我们使用单引号引起该块,以便立即扩展/解析执行的子命令(basename $TAR_FILENAME)。稍后再讨论。

grep:要在(不是实际)提取的文件上运行的命令

--label=:要在结果前加上标签,其值用双引号引起来,因为我们 do 希望让grep命令来解析{{ 1}}环境变量,由$TAR_FILENAME命令传递。

tar:作为命令运行(由反引号包围)并删除目录路径,仅输出文件名

basename $TAR_FILENAME-Hin显示文件名(由标签提供),H不区分大小写的搜索,i显示匹配的行号

然后,我们以单引号“结束”命令字符串的第一部分,并以双引号开始第二部分,以便可以解析作为第一个参数传入的n。 / p>

意识到我需要使用哪些引号是使我跳得最长的部分。希望这对您有意义并可以帮助其他人。另外,我希望我可以在一年内再次需要它时发现它(而且我已经忘记了为此编写的脚本!)


距离我编写上面的内容已经有几个星期了,它仍然非常有用...但是由于文件堆积和搜索内容变得更加混乱,它还不够好。我需要一种方法来限制文件日期(仅查看最新文件)的显示方式。所以这是代码。希望它是不言自明的。

$pattern

我不能停止调整这个东西。我在tar文件中添加了一个参数以按输出文件的名称进行过滤。通配符也可以。

用法:

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

示例:

targrep.sh [-d <start date>] [-f <filename to include>] <string to search for>

targrep.sh -d "1/1/2019" -f "*vehicle_models.csv" ford

答案 6 :(得分:0)

此选项确实可行: 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

答案 7 :(得分:0)

我正在尝试从十几个文件.tar.gz中进行grep模式,但速度非常慢

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

使用ugrep选项-z实际上很容易:

-z, --decompress
        Decompress files to search, when compressed.  Archives (.cpio,
        .pax, .tar, and .zip) and compressed archives (e.g. .taz, .tgz,
        .tpz, .tbz, .tbz2, .tb2, .tz2, .tlz, and .txz) are searched and
        matching pathnames of files in archives are output in braces.  If
        -g, -O, -M, or -t is specified, searches files within archives
        whose name matches globs, matches file name extensions, matches
        file signature magic bytes, or matches file types, respectively.
        Supported compression formats: gzip (.gz), compress (.Z), zip,
        bzip2 (requires suffix .bz, .bz2, .bzip2, .tbz, .tbz2, .tb2, .tz2),
        lzma and xz (requires suffix .lzma, .tlz, .xz, .txz).

只需一个命令即可搜索file.tar.gz,如下所示:

ugrep -z "string" file.tar.gz

这将抓住每个已存档的文件以显示匹配项。归档文件名用大括号显示,以区别于普通文件名。例如:

$ ugrep -z "Hello" archive.tgz
{Hello.bat}:echo "Hello World!"
Binary file archive.tgz{Hello.class} matches
{Hello.java}:public class Hello // prints a Hello World! greeting
{Hello.java}:  { System.out.println("Hello World!");
{Hello.pdf}:(Hello)
{Hello.sh}:echo "Hello World!"
{Hello.txt}:Hello

如果只需要文件名,请使用选项-l--files-with-matches)并使用选项--format="%z%~"自定义文件名输出以删除花括号:

$ ugrep -z Hello -l --format="%z%~" archive.tgz
Hello.bat
Hello.class
Hello.java
Hello.pdf
Hello.sh
Hello.txt