I'm looking for a means to extract individual and marked files from archives (e.g., *.zip; *.gz, etc.) to the hard-drive (without opening them in a buffer) using archive-mode. A suggested modification of that major-mode (that would permit this new feature) will be greatly appreciated.

http://www.gnu.org/software/emacs/manual/html_node/emacs/File-Archives.html

up vote 6 down vote accepted

Here's a sketch of an implementation.

Archive mode stores maps from archive types to commands used to extract data in variables archive-TYPE-extract; we can find the right variable using (archive-name "extract").

All commands are tailored for extracting to standard out; luckily we can still use them if we redirect stdout to a file of our choice.

(defun archive-extract-to-file (archive-name item-name command dir)
  "Extract ITEM-NAME from ARCHIVE-NAME using COMMAND. Save to
DIR."
  (unwind-protect
      ;; remove the leading / from the file name to force
      ;; expand-file-name to interpret its path as relative to dir
      (let* ((file-name (if (string-match "\\`/" item-name)
                            (substring item-name 1)
                          item-name))
             (output-file (expand-file-name file-name dir))
             (output-dir (file-name-directory output-file)))
        ;; create the output directory (and its parents) if it does
        ;; not exist yet
        (unless (file-directory-p output-dir)
          (make-directory output-dir t))
        ;; execute COMMAND, redirecting output to output-file
        (apply #'call-process
               (car command)            ;program
               nil                      ;infile
               `(:file ,output-file)    ;destination
               nil                      ;display
               (append (cdr command) (list archive-name item-name))))
    ;; FIXME: add unwind forms
    nil))

I modified archive-extract-by-file to get this.

(defun archive-extract-marked-to-file (output-dir)
  "Extract marked archive items to OUTPUT-DIR."
  (interactive "sOutput directory: ")
  (let ((command (symbol-value (archive-name "extract")))
        (archive (buffer-file-name))
        (items (archive-get-marked ?* t))) ; get marked items; t means
                                           ; get item under point if
                                           ; nothing is marked
    (mapc
     (lambda (item)
       (archive-extract-to-file archive
                                (aref item 0) ; get the name from the descriptor
                                command output-dir))
     items)))

Here I use mapc to loop over all marked files and extract them.

Now we just need to add a key binding:

(require 'arc-mode)
(define-key archive-mode-map "F" #'archive-extract-marked-to-file)

I tested this on a dummy .zip file containing a subdirectory, but your mileage may vary.

Note that archive-mode supports Arc, Lzh, Zip, Zoo, Rar, and 7z. It does not support .tgz, .tbz, .tar.gz and friends, which are opened using tar-mode and uncompress.el.

  • @lawlist: Please see the updated answer; now it should be able to extract files contained in subdirectories. Note that it re-creates the directory structure: foo/bar.txt in an archive gets extracted to output-dir/foo/bar.txt, not to output-dir/bar.txt. The latter is possible, of course, but would require some more code. – Constantine Nov 25 '14 at 8:10
  • That's really nice. Once you feel confident with it, would you consider adding it to archive-mode itself? – Malabarba Nov 25 '14 at 10:17
  • @Malabarba: Yes, I would. I'm waiting for a reply from copyright-clerk@fsf.org (I got this e-mail from Lars Ingebrigtsen) right now; in the meantime I'll clean this up and put it on GitHub. – Constantine Nov 25 '14 at 18:53
  • This new feature is awesome! Thank you so very much -- greatly appreciated! This would indeed be a wonderful addition to archive-mode. As you noted, the ability to target a specific output directory without necessarily re-creating the zip-archive directory structure (in the output directory) would be handy. As is, it is certainly worthy of a checkmark+ for the correct answer :) – lawlist Nov 25 '14 at 18:57
  • Thanks a lot for this code! One thing though: it'd be better to (interactive "G...") this way it will suggest the current directory, plus it'll autocomplete and interact with Ido mode etc. – wvxvw Mar 10 '15 at 12:20

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.