5243


このような状況で

[code]

    $ pwd
    ~/Desktop
    $ ls myzip*
    myzip.zip
    
[/code]

`~/Desktop/myzip/` 下に解凍されることを期待して間違ってこうしてしまうことがあります：

[code]

    $ unzip myzip.zip
    
[/code]

結果

[code]

    $ tree
    ├── extracted_file0
    ├── extracted_file1
     . ... 
    ├── myzip.zip
     ... # 元から ~/Desktop にあったファイルたちに混ざってしまっている
    
[/code]

大惨事となってしまったので，元の状況に戻したい． 既存のファイルがすでに上書きされてしまった場合は無視できるとして，どのようにすれば `unzip`
で意図せずディレクトリ上にばら撒かれてしまったファイル群を消し去ることができるでしょうか．

なお， すでに試みたこととしては， `unzip -l myzip.zip` で次のような書式のテキストが得られることを知り

[code]

    Archive:  myzip.zip
      Length      Date    Time    Name
    ---------  ---------- -----   ----
          119  2015-01-02 16:05   hashas.hs
          274  2015-01-25 16:05   piepie.py
    ---------                     -------
          393                     2 files
    
[/code]

とりあえずこれをファイルに書き込んで `vim` でファイル名のリストに整形し， `cat files.txt | xargs rm`
でいちおう対処しました．もう少し場当たり的でない方法を知りたいです．


CC BY-SA 3.0
2015-01-25T07:24:17.937
5241
2015-01-26T06:57:33.497
2901
question
bash
bash で，unzip の結果作られたファイルの一括削除


その発生は残念ですね。`awk`でリストを処理したとき、やさしくできます。

[code]

    rm -i `unzip -l file.zip | awk '/-----/ {p = ++p % 2; next} p {print $NF}'`
    
[/code]

`-i`選択が嫌いなら、`xargs`がつかえます。でも、`xargs`は`rm`の対話方式の互換性がありません。

**編集**

ファイルの名前に空白文字があれば、上のコマンドが足りません。このコマンドがうまく行くはずです。

[code]

    unzip -l file.zip | awk '{$1=$2=$3=""; sub(" ", ""); if (length(substr($0, 3))) printf "\"%s\"\n", substr($0, 3); }' | tail -n +3 | xargs rm
    
[/code]


CC BY-SA 3.0
2015-01-25T07:34:10.693
5242
2015-01-25T08:01:34.410
2015-01-25T08:01:34.410
7745
7745
5241
answer


`unzip -l` の結果を加工します。

[code]

    unzip -qq -l myzip.zip | perl -ane 'print "$F[-1]\n"' | xargs rm
    
[/code]

実際には、出力結果を見た上で xargs rm するのが良いと思います。

空白が混じっている場合は、NUL で区切ると良いです。

[code]

    unzip -qq -l myzip.zip | perl -ne 'chomp; @F = split " ", $_, 4; print "$F[-1]\0"' | xargs -0 rm
    
[/code]

同名のファイルがあって `unzip` で上書きしなかったからそれは残したいという場合は、最終アクセス時刻 (atime)
で判定するのが良いかもしれません。ここでは、1時間以内 (24分の1 日) のものを対象にしています。

[code]

    unzip -qq -l myzip.zip | perl -ne 'chomp; @F = split " ", $_, 4; print "$F[-1]\0" if -A $F[-1] < 1/24'|xargs -0 rm
    
[/code]

※ @heliac2001 のコメントを元に、`-qq` を使うよう修正しました。空白を気にしない場合は sed を使っても良さそうです。


CC BY-SA 3.0
2015-01-25T07:42:02.267
5243
2015-01-25T07:59:57.867
2015-01-25T07:59:57.867
76
76
5241
answer


すでに回答がいくつか付いていますが、別解を示します。

[code]

    $ unzip -qq -l file.zip |sed 's/^ *[^ ]*  [^ ]* [^ ]*   //' |tr '\n' '\0' |xargs -0 sh -c 'rm -i -- "$@" </dev/tty' -
    
[/code]

`unzip` の出力を `sed` と `tr` で加工してファイル名を抽出していますが、ほかの手段でも構いません。

こちらのほうが以下の点で優れています。

  * 空白やハイフンで始まるファイル名が含まれていても問題ない。
  * 削除するかどうかファイルごとに確認することができる。(確認が不要なら `rm` の `-i` を削除してください)
  * ファイルが大量に含まれている場合でも引数リストが長すぎる問題(「argument list too long」エラー)が発生しない。

ただし、ファイル名に改行が含まれていると誤動作します。場合によってはクラッカーの攻撃手段として使われるかもしれません。そのような ZIP
アーカイブを作れるのか、その場合に `unzip -l` の出力がどのようになるかは確認していません。


CC BY-SA 3.0
2015-01-25T08:44:04.193
5244
2015-01-25T09:05:11.970
2015-01-25T09:05:11.970
3061
3061
5241
answer


以下は回答ではありません。この様な事故をどの様に防いだら良いのか、というお話です。

もともと、unzip
コマンドはアーカイブの内容をそのまま展開してしまいます。そのため、アーカイブ内のファイルが単一のディレクトリに収められていない場合には今回の様な事故が発生してしまう可能性があります。もっとも、単一のディレクトリに収められていても、カレントディレクトリに同名のディレクトリがあれば同じことですが。。。  
一応、unzip には `-d`
オプションがあって、指定したディレクトリにアーカイブの内容を展開してくれます。指定ディレクトリがない場合には勝手に作成してくれます。

[code]

    $ unzip -d output myzip.zip
    $ ls output
    c001.txt  c002.txt  c003.txt ...
    
[/code]

しかし、毎回ディレクトリを指定するのは疲れますし、指定し忘れるとやはり…。

そこで、unzip
以外に何か目的に適うコマンドはないだろうかと思いまして調べてみた所、[unar](http://unarchiver.c3.cx/commandline)
が見つかりました(まぁ、以前から使っていたのですけどね)。

> unar(1)
>
> -d, -force-directory  
>  Always create a containing directory for the contents of the unpacked
> archive. **By default, a directory is created if there is more than one top-
> level file or folder**.

今回の `myzip.zip` の場合には `myzip` ディレクトリを作成してその中に展開してくれます(アーカイブの basename
でディレクトリを作成)。  
その他にもこんなオプションがあります。

> -r, -force-rename  
>  Always rename files when a file to be unpacked already exists on disk.

例えば、`a.txt` というファイルが既にある場合には `a-1.txt` にリネームしてくれます。

ご参考までにどうぞ。


CC BY-SA 3.0
2015-01-25T11:32:33.547
5251
2015-01-25T11:41:56.420
2015-01-25T11:41:56.420
5241
answer


unzip -l の出力に頼らない方法です。

# 削除したい対象がファイルのみだとわかっている場合(サブディレクトリを含まない場合)

[code]

    $ find . -cmin -60 -type -f
    
[/code]

で60分以内に新規に作成したファイルも含めてステータスが変更されたファイルを検索できます。アーカイブ中に記録されているタイムスタンプではなく、作成したタイミングです。

次のように、30分前～60分前の間、という指定もできます。

[code]

    $ find . -cmin +30 -cmin -60 -type -f
    
[/code]

検索した結果に余計なものが含まれていないことが確認できたら

[code]

    $ find . -cmin -60 -type f -print0 |xargs -0 rm
    
[/code]

で削除できます。findの`-print
0`とxargsの`-0`はファイル名に空白や改行文字、バックスラッシュが含まれていた時に意図しない動作を防ぐために必要です。`rm`に`-i`を指定すれば1つずつ確認を求められます。

除外したいファイルがある場合は`! -name filename`(下記を参照)でファイル名をキーにして除外してください。

他にもいろいろな条件が指定できますので、`man find`を確認してください。

# ディレクトリが含まれている場合

`find`のオプションを変更します。

[code]

    $ find . -cmin -60 \! -name .
    
[/code]

`\\! -name .`は`.`を除外するために必要です。

実際の削除はこうします。

[code]

    $ find . -cmin -60 \! -name . -print0 |xargs -0 rm -r
    
[/code]

ただし、出力順の都合で

[code]

    dir
     +-file1
     +-file2
    
[/code]

という構造の場合、`rm -r dir`でツリーごと削除した後`dir/file1` `dir/file2`を削除しようとするため No such file
or directoryのエラーになります。削除済みのファイルを削除しようとしているだけなので実害はありません。


CC BY-SA 3.0
2015-01-26T06:57:33.497
5313
2015-01-26T06:57:33.497
5793
5241
answer