`wget http://~`でファイルを保存する時の話です。

バックグラウンドで`wget`させて複数のファイルを保存している時、どれが完了しているファイルなのかが`ls`ではわかりません。  
そこで、ダウンロード中のものはファイル名の先頭に`_incomplete_`というものを付け、完了後にオリジナルのファイル名にしたいのですが、なにかよい方法はないでしょうか。
CC BY-SA 3.0
2016-01-23T03:56:07.510
21210
2016-01-23T11:51:43.200
9149
question
linux macos
wgetでダウンロード中のファイル名を変える方法
条件を満たすかわかりませんが大抵のコンテンツにはタイムスタンプがあるので `wget -N` でタイムスタンプを保持させます。  
そうするとダウンロード完了後にタイムスタンプが設定されるので、`ls -t`でタイムスタンプ順に並べることで完了を知ることができます。
CC BY-SA 3.0
2016-01-23T04:04:46.333
21211
2016-01-23T04:04:46.333
4236
21210
answer
以下のお話は **Linux OS 限定** になります。

Linux OS には `inotify(monitoring filesystem events)` と呼ばれる機構が用意されています。詳しい話は
`man 7 inotify` でも参照いただくとして、この機構を利用するとファイルに対する `open(2)/close(2)`
の発行を捉えて任意の処理を追加する事が可能になります。

具体的は `inotify-tools` パッケージに含まれる `inotifywait` コマンドを利用します。以下のサンプルスクリプトでは
`$HOME/downloads` ディレクトリにファイルをダウンロードする事を前提としています。

**mark_incomplete.sh**

```

    #!/bin/bash
    
    inotifywait -q -m $HOME/downloads -e open,close --format '%e %w %f' |
    while read -r event dir file
    do
      [[ ! -f $dir$file ]] && continue
      p=($(fuser "$dir$file" 2>/dev/null))
      case "$event" in
        OPEN)
          [[ ${#p[@]} == 1 ]] && [[ $(basename "$(readlink -e /proc/${p[0]}/exe)") == wget ]] &&
          ln -sf "$dir$file" "${dir}_incomplete_$file";;
        *CLOSE*)
          ( [[ ${#p[@]} == 0 ]] ||
          ( [[ ${#p[@]} > 1 ]] && [[ $(basename "$(readlink -e /proc/${p[0]}/exe)") != wget ]] )) &&
          rm -f "${dir}_incomplete_${file}";;
      esac
    done
    
```

以下は動作例です。

```

    $ ./mark_incomplete.sh &
    $ cd $HOME/downloads
    $ wget http://greengenes.lbl.gov/Data/JD_Tutorial/UnAligSeq24606.txt
    
    # 別の端末で確認
    $ ls -l $HOME/downloads
    -rw-r--r--  1 nemo 1.4K Jan 23 20:48 UnAligSeq24606.txt
    lrwxrwxrwx  1 nemo   43 Jan 23 20:48 _incomplete_UnAligSeq24606.txt -> /home/nemo/downloads/UnAligSeq24606.txt
    
    # ダウンロード完了
    $ ls -l $HOME/downloads
    -rw-r--r--  1 nemo 35144 Aug 11  2006 UnAligSeq24606.txt
    
```

このスクリプトでは mv コマンドではなく、symbolic link を作成しています。理由は、`mv "$dir/$file"
"$dir/_incomplete_$file"` としてしまうと、ダウンロード完了後のタイムスタンプの変更ができなくなってしまうからです。

```

    $ wget http://greengenes.lbl.gov/Data/JD_Tutorial/UnAligSeq24606.txt
                     :
    utime(UnAligSeq24606.txt): No such file or directory
    
```

さて、このスクリプトにはいくつかの問題があります。

  * ダウンロード先ディレクトリの名前に空白文字類が含まれているとエラー
  * ダウンロードするファイルの名前に改行コード(0x0a)が含まれているとエラー

`inotifywait` コマンドの出力文字列を `NULL(0x00)` 文字で区切って渡したい所なのですが、`--format`
オプションで指定するフォーマット文字列に NULL を含められません(github のリポジトリに issue
として上がっていますが、現状では進展がありません)。

  * ダウンロード先のディレクトリで、`wget` という名前の別のコマンドを使ってファイルをオープンすると symbolic link ファイルが作成される

`inotifywait` は event を発生させた process の情報(PID など)は通知しませんので、`/proc`
から必要な情報を取得しています。

以上の様な状況ですので、これを利用するとしてもかなり限定的になります。参考程度に捉えて下さい。
CC BY-SA 3.0
2016-01-23T11:51:43.200
21229
2016-01-23T11:51:43.200
21210
answer