使用Ruby解压(zip,tar,tag.gz)文件

36

我想解压大量的Zip文件。是否有可以检查zip文件格式并解压缩的模块或脚本?此功能需要在Linux系统上运行,不考虑其他操作系统。


tar cz(通常生成 *.tgz.tar.gz 文件)使用 gzip 压缩。gzipzip / unzip 不兼容(这个问题与之无关)。 - hagello
4个回答

37
从 .tar.gz 文件中提取文件,您可以使用与 Ruby 分发的软件包中提供的以下方法:
require 'rubygems/package'
require 'zlib'
tar_extract = Gem::Package::TarReader.new(Zlib::GzipReader.open('Path/To/myfile.tar.gz'))
tar_extract.rewind # The extract has to be rewinded after every iteration
tar_extract.each do |entry|
  puts entry.full_name
  puts entry.directory?
  puts entry.file?
  # puts entry.read
end
tar_extract.close

每个类型为Gem::Package::TarReader::Entry的条目指向.tar.gz文件内的文件或目录。

类似的代码可以使用(将Reader替换为Writer)来将文件写入.tar.gz文件中。


1
为了进一步解释这个很棒的答案,您可以使用Gem::Package.new("").extract_tar_gz(io, destination)将文件提取到磁盘上,其中io是像File.open("file.tar.gz", "rb")这样的对象。 - Andrew Kane
@AndrewKane的代码完美运行,尽管将''传递给包有点奇怪。它应该如何工作? - lilkunien

32

最简单的方法可能是使用Zlib

Zlib是一个Ruby库。下面是一个简单的程序,用于从特定URL获取一个压缩文件,解压缩它,并将其内容粘贴到屏幕上。

require 'zlib' 
require 'open-uri'

uri = "www.somedomain.com/filename.gz"
source = open(uri)
gz = Zlib::GzipReader.new(source) 
result = gz.read
puts result

希望这能有所帮助。


Mavryx:当我尝试运行你的代码片段时,出现了以下错误:unzip.rb:5: undefined local variable or method `uri' for #<Object:0x10018b298 @uri="" rel = "nofollow noreferrer">http://wordpress.org/latest.tar.gz"> (NameError) - maetthew
@maetthew,请检查第4行,并确保在两行中使用相同的变量(这里是uri)。 - BurmajaM

16

尽管 Florian的回答 是正确的,但它没有考虑到tar长链接(尝试从oracle提取jdk-7u40-linux-i586.tar.gz :P )。以下代码应该能够解决这个问题:

require 'rubygems/package'
require 'zlib'

TAR_LONGLINK = '././@LongLink'
tar_gz_archive = '/path/to/archive.tar.gz'
destination = '/where/extract/to'

Gem::Package::TarReader.new( Zlib::GzipReader.open tar_gz_archive ) do |tar|
  dest = nil
  tar.each do |entry|
    if entry.full_name == TAR_LONGLINK
      dest = File.join destination, entry.read.strip
      next
    end
    dest ||= File.join destination, entry.full_name
    if entry.directory?
      File.delete dest if File.file? dest
      FileUtils.mkdir_p dest, :mode => entry.header.mode, :verbose => false
    elsif entry.file?
      FileUtils.rm_rf dest if File.directory? dest
      File.open dest, "wb" do |f|
        f.print entry.read
      end
      FileUtils.chmod entry.header.mode, dest, :verbose => false
    elsif entry.header.typeflag == '2' #Symlink!
      File.symlink entry.header.linkname, dest
    end
    dest = nil
  end
end

1
:verbose => false 是必要的吗? - Jeff Dickey
1
不,我只是不想在我的用例中打印任何东西。 - Draco Ater

2
德拉科,感谢您的片段。一些TAR将目录编码为以“/”结尾的路径-请参见this维基百科。示例tar是Oracle Server JRE 7u80 for Windows。这对他们有效:
require 'fileutils'
require 'rubygems/package'
require 'zlib'

TAR_LONGLINK = '././@LongLink'

Gem::Package::TarReader.new( Zlib::GzipReader.open tar_gz_archive ) do |tar|
        dest = nil
        tar.each do |entry|
            if entry.full_name == TAR_LONGLINK
                dest = File.join destination, entry.read.strip
                next
            end
            dest ||= File.join destination, entry.full_name
            if entry.directory? || (entry.header.typeflag == '' && entry.full_name.end_with?('/'))
                File.delete dest if File.file? dest
                FileUtils.mkdir_p dest, :mode => entry.header.mode, :verbose => false
            elsif entry.file? || (entry.header.typeflag == '' && !entry.full_name.end_with?('/'))
                FileUtils.rm_rf dest if File.directory? dest
                File.open dest, "wb" do |f|
                    f.print entry.read
                end
                FileUtils.chmod entry.header.mode, dest, :verbose => false
            elsif entry.header.typeflag == '2' #Symlink!
                File.symlink entry.header.linkname, dest
            else
                puts "Unkown tar entry: #{entry.full_name} type: #{entry.header.typeflag}."
            end
            dest = nil
        end
    end
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接