Skip to content

Instantly share code, notes, and snippets.

@martinus
Created July 24, 2008 17:35
Show Gist options
  • Save martinus/2226 to your computer and use it in GitHub Desktop.
Save martinus/2226 to your computer and use it in GitHub Desktop.
extract any archive
#!/usr/bin/ruby
# The program e is a command line utility that extracts lots of
# different archives. It is very simple and can be extended very easily.
#
# It is inspired by how firewall use their rulesets, and works like this:
#
# * For each file that has to be extracted, the rules are matched one after the other.
# * When a rule matches (either by the filetype or filename), the command is executed.
# * If the command does not return an error code the extraction is considered successful,
# otherwise the next rules are matched.
#
# It currently has rules for zip, rar, 7zip, gzip, bzip2, rpm, cab, arj, ace, ppmd,
# lzo, tar.bz2, tar.gz, ar, cpio, dar, uharc, zzip, ...
#
# Author:: Martin Ankerl (mailto:martin.ankerl@gmail.com)
# Copyright:: Copyright (c) 2006-2008 Martin Ankerl
# License:: public domain
#
# Homepage:: http://martin.ankerl.com/
# Each rule consists of 3 parts:
#
# 1. type of the match: currently either :name or :type. If you use :name, then the regular expression
# is matched against the filename. If you use :type, the regular expression is matched against the
# output of the "file" command. Usually :type is a better choice because you are independent from the
# actual filename, but for file types that cannot be detected use :name.
#
# 2. The regular expression is used in a match. If the regular expression matches, the command
# (3rd parameter of the rule) is executed.
#
# 3. The command to execute. %FILE% is replaced by the filename (with apostrophe).
#
# On extraction, each of the rules are matched one after the other. The command of
# the first rule that matches is executed. If the executed command does not return an
# error code, extraction was successful. If the command could not be executed (e.g. if do not
# have unrar but only rar) or returns with an error code, the next rules are matched until
# either no rule is available any more, or extraction was successful.
#
rules = Array.new
rules.push [ :name, /(\.tar\.gz|\.tgz)$/, "tar xzvf %FILE%" ]
rules.push [ :name, /(\.tar\.bz2|\.tbz)$/, "tar xjvf %FILE%" ]
rules.push [ :name, /(\.tar\.lzo|\.tzo|\.tar\.lzop)$/, "lzop -c -d %FILE% |tar -xv" ]
rules.push [ :type, /tar archive/, "tar -xvf %FILE%" ]
rules.push [ :type, /^Zip archive/, "unzip %FILE%" ]
rules.push [ :type, / ZIP /, "unzip %FILE%" ]
rules.push [ :type, /^7-zip/, "7zr x %FILE%" ]
rules.push [ :type, /^7-zip/, "7z x %FILE%" ]
rules.push [ :type, /^7-zip/, "7za x %FILE%" ]
rules.push [ :type, /^RAR archive/, "unrar x -o+ %FILE%" ]
rules.push [ :type, /^RAR archive/, "rar e %FILE%" ]
rules.push [ :type, /^Debian.*package/, "dpkg -x %FILE% ." ]
rules.push [ :type, /^Debian.*package/, "ar -x %FILE%" ]
rules.push [ :type, / ar archive/, "ar -x %FILE%" ]
rules.push [ :type, /^lzop /, "lzop -x -f %FILE%" ]
rules.push [ :name, /\.rz$/, "rzip -d -k -v %FILE%" ]
rules.push [ :type, /gzip /, "gzip -d %FILE%" ] # TODO gzip is the only operation that removes the original file
rules.push [ :type, /bzip2 /, "bzip2 -dk %FILE%" ]
rules.push [ :type, /^RPM/, "rpm2cpio < %FILE% | cpio -i -d --verbose" ]
rules.push [ :type, / cpio archive/, "cpio -i -d --verbose < %FILE%" ]
rules.push [ :type, /\ ar archive/, "ar xv %FILE%" ]
rules.push [ :type, /^LHarc /, "lha x %FILE%" ]
rules.push [ :type, /^ARJ /, "arj x %FILE%" ]
rules.push [ :type, /MS CAB-Installer/, "cabextract %FILE%" ]
rules.push [ :type, /^ACE /, "unace e %FILE%" ]
rules.push [ :type, /^PPMD archive/, "ppmd d %FILE%" ]
rules.push [ :name, /(\.tar\.lzma|\.tlz)$/, "lzma d -si -so < %FILE% |tar -xv" ]
rules.push [ :name, /\.dar$/, "dar -v -x %FILE%" ]
rules.push [ :name, /\.uha$/, "wine uharc x %FILE%" ] # wine and uharc.exe in path required
rules.push [ :type, /ZZip archive/, "zzip x %FILE%" ]
rules.push [ :type, /Zoo archive/, "zoo -extract %FILE%" ]
rules.push [ :name, /(\.zip|\.ZIP)$/, "unzip %FILE%" ] # if all previous type checks fail
rules.push [ :name, /\.arc$/, "arc x %FILE%" ] # FreeArc
# check arguments
if ARGV.empty?
puts "e, Version 2008-02-24 (c) Martin Ankerl"
puts "Usage: e archive [ archive archive ...]"
exit 0
end
# collect all filenames that were not extractable
errors = []
# extract each archive
ARGV.each do |filename|
# get filetype
filenameWithComma = '"' + filename + '"'
filetype = `file -b #{filenameWithComma}`
# find command
extractionSuccess = rules.find do |matchType, regexp, command|
# find out if it is matching
doesMatch = case matchType
when :name then regexp.match(filename)
when :type then regexp.match(filetype)
end
# only return true if extraction was successful (to stop find)
if doesMatch
# matching rule found, try to extract
executable = command.gsub("%FILE%", filenameWithComma)
# returns true if execution was successful
system(executable)
else
false
end
end
# no extraction attempt completed successful, show error message
if extractionSuccess.nil?
errors.push filenameWithComma
end
end
# show error message
unless errors.empty?
puts "\nERROR extraction not successful with these files:"
errors.each do |fileName|
system("file #{fileName}")
end
exit(1)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment