AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 687900
Accepted
geminary
geminary
Asked: 2022-01-26 09:13:27 +0800 CST2022-01-26 09:13:27 +0800 CST 2022-01-26 09:13:27 +0800 CST

取消连接 gzip 文件

  • 772

前几天,我从远程服务器收集了一些日志,然后不假思索地将文件压缩到一个文件中,而不是将目录添加到压缩包中。我可以手动分离出一些日志文件,但其中一些已经被 gzip 压缩了。所以原始文件看起来像:

ex_access.log
ex_access.log.1.gz
ex_access.log.2.gz
ex_debug.log
ex_debug.log.1.gz
ex_debug.log.2.gz
ex_update.log
ex_update.log.1.gz
ex_update.log.2.gz

并被压缩到 exlogs.gz 中,如您所料,解压后它是一个包含所有原始文件的文件。有没有办法分离出原始的gz文件,以便它们可以正常解压缩而不是打印出二进制文件:

^_<8B>^H^H<9B>C<E8>a^@
^Cex_access.log.1^@<C4><FD><U+076E>-Kr<9D>       <DE><F7>S<9C>^W<E8><CE><F0><FF><88>y[<D5><EA>+<A1>^EHuU<A8>^K<B6><94><AA>L4E^R̤^Z^B<EA><E1><DB>}<AE>̳<B6><D6>I<C6><F8><9C><DB><C6>
<F1>@G`<E6><D6><FE><E0>3<C2><C3>ٰ̆|<E4><FC><BB>#<FD><EE><B8>~9<EA>+<A7>W+<FF><FB><FF><F6><9F><FE><97><FF><E3><97><FF><FD>^Z<E3><FF><F8><E5><FF><FE><CB><C7><FF>Iy<FC>?<8E><F9>?<F3>?<EF><B5><F7><F9><BF><FF>ß<FF>
[etc]

是的,我可以再次收集日志(因为我确实有意识保持原件完好无损),但是获得访问服务器的批准是一件痛苦的事情,如果可能的话,我想避免它。

编辑:我使用的命令是

gzip -c ex_* > exlogs.gz

gzip
  • 3 3 个回答
  • 312 Views

3 个回答

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2022-01-27T09:41:09+08:002022-01-27T09:41:09+08:00

    碰巧的是,ingzip -c file1 file2 > result确实gzip为每个文件创建了两个单独的压缩流,甚至存储了文件名和文件的修改时间。

    它不允许您在解压缩时使用该信息,但您可以使用perl'IO::Uncompress::Gunzip模块来代替。例如:

    #! /usr/bin/perl
    use IO::Uncompress::Gunzip;
    
    $z = IO::Uncompress::Gunzip->new("-");
    
    do {
      $h = $z->getHeaderInfo() or die "can't get headerinfo";
      open $out, ">", $h->{Name} or die "can't open $h->{Name} for writing";
      print $out $buf while $z->read($buf) > 0;
      close $out;
      utime(undef, $h->{Time}, $h->{Name}) or warn "can't update $h->{Name}'s mtime";
    } while $z->nextStream;
    

    并将该脚本称为that-script < exlogs.gz,它将使用其原始名称和修改时间(不包括未存储的亚秒部分gzip)在当前工作目录中恢复文件。

    • 3
  2. cg909
    2022-01-29T19:06:38+08:002022-01-29T19:06:38+08:00

    将文件 gzip 压缩到单个文件时,gzip会创建一个包含多个 gzip 流的文件,就像您首先单独压缩文件然后将它们连接起来一样。

    手册页中简要提到了此行为。

    -c --stdout --to-stdout

    在标准输出上写输出;保持原始文件不变。如果有多个输入文件,则输出由一系列独立压缩的成员组成。

    这意味着每个源文件都有一个单独的 gzip 标头(其中包含原始文件名)。所以原则上它们可以在解压时分开。

    不幸的是,gzip开发人员选择不支持这一点gunzip:

    如果您希望创建具有多个成员的单个存档文件,以便以后可以独立提取成员,请使用 tar 或 zip 等存档程序。[…] gzip 被设计为 tar 的补充,而不是替代。

    取消连接文件并非易事,因为 gzip 标头和页脚都不包含压缩数据流的长度。这意味着,为了可靠地找到第二个流的开始,您需要对整个 deflate 数据流进行解码,这是解压缩整个数据流的一半。

    据我所知,目前还没有工具可以只浏览数据流以找出它的结束位置,即使该领域有一些研究支持对 gzip 压缩文件内容的准随机访问。

    幸运的是,一些编程库可用于单独解压缩 gzip 流,例如 Perl 的IO::Uncompress::Gunzip,如 Stéphane Chazelas 在他的回答中提到的,或者 Rust 的flate2。

    最后,作为解决方案,我编写了工具gunzip-split。它单独解压缩每个文件,也可以解连接文件。对于后者,它解压缩每个文件,记录 gzip 流开始的偏移量,同时丢弃结果。这可以进一步优化,但即使对于千兆字节大小的文件也可以相当快地工作。

    $ ./gunzip-split --help
    gunzip-split 0.1.1
    Uncompress concatenated gzip files back into separate files.
    
    USAGE:
        gunzip-split [OPTIONS] <FILE>
    
    ARGS:
        <FILE>    concatenated gzip input file
    
    OPTIONS:
        -d, --decompress                      Decompressing all files (default)
        -f, --force                           Overwrite existing files
        -h, --help                            Print help information
        -l, --list-only                       List all contained files instead of decompressing
        -o, --output-directory <DIRECTORY>    Output directory for deconcatenated files
        -s, --split-only                      Split into multiple .gz files instead of decompressing
        -V, --version                         Print version information
    
    $ ./gunzip-split -s -o ./out/ combined.gz
    file_1: OK.
    file_2: OK.
    
    $ ls ./out
    file_1.gz file_2.gz
    
    • 3
  3. FelixJN
    2022-01-26T11:18:36+08:002022-01-26T11:18:36+08:00

    这有点复杂,但在使用以下要求时有效:

    • merged.gz是清晰的 ASCII 数据和 gzip 文件的混合
    • 它来自类似的操作cat log0 log1.gz log2.gz log3 log4.gz > merged.gz
    • 明文 ASCII 文件中的行仅来自可打印字符
    • gzip 压缩文件的魔术字节是完整的(十六进制1F 8B)

    大多数程序应该可用,sponge可以moreutils通过手动写入临时文件来避免。

    做了什么:

    1. 将具有排他可打印字符的行放入每个连续块的文件中。请注意,如果您连续合并了两个清晰的 ASCII 文件,这不会将它们分开(在这种情况下使用日志的时间戳来分隔文件)并且原始文件名会丢失
    2. 将其他行放入中间gz_only.gz文件
    3. 使用魔术字节来分隔文件

    最后一点使用csplit,只有在还有换行符的情况下才能拆分 - 所以这是在拆分之前引入并在之后删除。目前假设合并系统中的 gzip 压缩文件不超过 1000 个。

    #!/bin/bash
    
    #lines with printable characters go to separate files for each consecutive block
    awk '{ if ($0 ~ /^[[:print:]]+$/) { print > "file_"i+0}
           else {if (oldi==i) {i++}}}' merged.gz
    
    #get lines with non-printables to other merged file
    grep -av '^[[:print:]]$' merged.gz > gz_only.gz
    
    #split into files and remember their count
    #sed introduces newline before magic bytes
    #csplit splits on occurrence of magic bytes and returns info on file lengths
    nfiles=$( sed "s/$(printf '\x1f\x8b')/\n&/g" gz_only.gz |
              csplit - -z "/$(printf '\x1f\x8b')/" '{*}' -b'%03d.gz' |
              wc -l )
    
    #first file is empty, due to introduced newline
    rm -fv xx000.gz
    
    #for all other remove newline
    #note: the above grep introduced a newline to the last file
    #if splitting is done for a file only concatenated from
    #gz-files (no previous grep), the last file would have to
    #be excluded from this operation.
    for (( i=1 ; i<nfiles ; i++ )) ; do
        name=xx$(printf '%03d.gz' $i)
        head -c -1 $name | sponge $name
    done
    
    #retrieve original file name
    for f in xx*gz ; do
        #this is ready for simple filenames like the suggested logs,
        #e.g. no " as file name character
        mv $f "$(file $f | awk -F'"' '{print $2}').gz"
    done
    
    #unzip files
    find -name '*gz' ! -name gz_only.gz ! -name merged.gz -exec gunzip {} +
    

    我有点觉得 ASCII 和非 ASCII 的分离以及拆分可能会更优雅地完成perl,但我不熟悉。

    • 0

相关问题

  • 我的wget怎么会缺少--compression?

  • 如何将 tar 文件转换为 tgz 文件?

  • 使用偏移量读取部分下载的 gzip

  • 为什么 gzip 不创建相同大小的文件?

  • 解压缩文件夹中的 *.Z 文件返回错误

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve