dired で tar.gz 圧縮 その5

この前の関数を受けて, plus さんにメールをもらいました.
ものすごく便利になってます.

(defvar dired-do-create-archival-method
  '(("tar+gzip" "\\.t\\(ar\\.\\)?gz$" ".tar.gz" "tar cvzf")
    ("tar+bzip2" "\\.t\\(ar\\.bz2\\|bz\\)$" ".tar.bz2" "tar cvjf")
    ("zip" "\\.zip$" ".zip" "zip -r")))

(defun dired-do-create-archived-file (output-filename arg)
  "マークしたファイルをまとめて圧縮する."
  (interactive "FOUTPUT FILENAME: \nP")
  (let ((def (caar dired-do-create-archival-method))
        opt)
    (mapc
     (lambda (x)
       (when (string-match (nth 1 x) output-filename)
         (setq opt x)))
     dired-do-create-archival-method)
    (setq opt (or opt
                  (assoc (completing-read (format "Select archival method (default %s): " def)
                                          dired-do-create-archival-method nil t nil nil def)
                         dired-do-create-archival-method)))
    (or (when (string= "" (file-name-nondirectory output-filename))
          ;; 出力ファイル名が入力されていなかったらエラーとする.
          (message "ERROR: Output filename is empty."))
        (when (file-exists-p
               (setq output-filename
                     (concat output-filename (unless (string-match (nth 1 opt) output-filename) (nth 2 opt)))))
          ;; 出力ファイル名と同じ名前のファイルが, 既にあった場合, 上書きするか問い合わせる.
          (not (y-or-n-p (concat "file " output-filename " exists. overwrite the file ?"))))
        ;; 実際に圧縮をする.
        (let (add-file)
          (when arg
            (while (y-or-n-p "ADD FILE?")
              (add-to-list 'add-file (file-relative-name (read-file-name "" nil nil t)))))
          (dired-do-shell-command (format "%s %s *" (nth 3 opt) output-filename) nil
                                  (append (dired-get-marked-files t) add-file))))))

使い方は, 簡単.
この関数を dired で起動すると, マークしているファイルやディレクトリをまとめて圧縮できます.
動作は, 以下のとおり

  1. 出力ファイル名の入力を促される.
    • 出力ファイル名を入力しないとエラー.
  2. 入力された出力ファイル名より, 圧縮形式を判断する.
    1. 出力ファイルの拡張子が変数 dired-do-create-archival-method に登録されているものだった.
      • その形式で圧縮を行う.
    2. 出力ファイルの拡張子が入力されていなかったり, 変数 dired-do-create-archival-method に登録されているものでなかった.
      • 登録されている圧縮形式の中から, 使用するものの選択を促される.
      • デフォルトは, tar.gz 形式. 何も入力しないで決定をすると, この形式が選択される.
  3. 出力ファイル名と同じ名前のファイルが, 出力先ディレクトリに存在すれば, 上書きをしていいかどうか尋ねられる.
    • n を押すと圧縮しないで終了.
  4. 圧縮を行う.
    もし, 前置引数(C-u)を与えていれば, カレントディレクトリ以外のファイルの圧縮メンバへの追加をミニバッファより行う. この動作は, "ADD FILE" で n を選択するまで続く.



関数を読みといてみる.

  1. 局所変数 def へ変数 dired-do-create-archival-method の最初のリストの第1要素を割り当てる.
    • ここでは, tar+gzip が def の値となる
  2. mapc で第1引数の関数へ第2引数のリストの中身を1つづつ適用する.
    • 入力された出力ファイル名の拡張子が, 局所大域変数 dired-do-create-archival-method の第2要素の正規表現とマッチしたならば, そのリストを opt とする.
  3. opt の値が nil でなかった
    • opt は, そのまま. つまり, mapc の部分で opt へリストが代入されていれば, そのまま.
  4. opt の値が nil だった
    • ミニバッファから, 局所大域変数 dired-do-create-archival-method に登録されている圧縮形式を選択させる.
    • assoc で選択された圧縮形式(値は, (caar dired-do-create-archival-method).)を要素に持つリストを取得し, そのリストを opt とする.
  5. あとは, 今まで同じ動作.

mapc と assoc の使い方をやはり忘れてて調べた.

  • mapc が第1引数の関数へ, 第2引数のリストの中身1つ1つを適用.
  • assoc が第1引数の値にマッチする, 連想リストである第2引数の最初のものを返す, と.

こんな感じかな.
連想リストは, リストの中にリストがあるようなリスト, と考えていいのかな.


自分で局所大域変数 dired-do-create-archival-method へ値を追加してやれば, 色々な方式に対応できるね.
でも足すとしても, lzh ぐらい・・・?
compress はねえ・・・. Momonga というか, Fedora には, 入ってすらいないし. yum で取ってくることもできない.
たしか, Ubuntu 6.06だと, apt-get で取ってこれたと思う. たしかだけどね.
あっ, 7zip 足すと面白いかも.
7zip は, 圧縮の仕方が色々あった覚えがある. 圧縮率優先とかいろいろ.



自分で形式追加するとホントイイかも.
忘れがちな圧縮のオプションを, dired 側で管理してくれる.
オプションを自分で覚えておく必要もないし, 忘れてて man やネットでいちいち調べる必要もない.
関数起動して, 圧縮形式を選択するだけ.


カレントディレクトリ以外のファイルサポート.
複数圧縮形式サポート.
他に何があるだろう?思い付かない.



とりあえず, lzh を追加.
add-to-list の第3引数に t を指定しないと変数の先頭に追加されて焦った.
# デフォルトが, tar+gzip から lzh に変わっちゃうよ.

(add-to-list 'dired-do-create-archival-method '("lzh" "\\.lzh$" ".lzh" "lha a") t)

7zip は, また今度圧縮の仕方を調べてやる.

追記(0217)

plus さんのメールでの御指摘により, dired-do-create-archival-method のところの局所変数を大域変数に修正.
あらら. やっちまったorz
御指摘ありがとうございました.