Windows でたくさんのファイルの作成日時を変更したくなったときに、Perl・Ruby・Python などのスクリプト言語を使おうとしたんだけど、作成日時を変更するための関数がなくて困った。
そもそも UNIX にファイルの作成日時がない
stat(2) で取得できる情報のうち時刻に関するものは次の 3 つがある。
atime
: 最終アクセス日時 (access time)utime
: 最終更新日時 (modify time)ctime
: 最終変更日時 (change time)
名前からすると ctime
が作成日時 (creation time) っぽいけど、ctime
はファイルを更新したときとか chmod や chown したときなどに自動的に現在時刻に更新されるようだ。
atime
と utime
は utimes(2) で任意の時刻に変更できるんだけど、ctime
を変更する手段は表立っては用意されていない。
ということで、UNIX 文化には「ファイルの作成日時」という概念がないので、UNIX 由来なスクリプト言語を使って「Windows のファイルの作成日時」を変更する公式の手法はない。
ということで、これを何とかするための補助アプリケーション setctime.exe を作ってみた。
setctime.exe の使い方
とてもシンプル。
- コマンドライン引数でファイル名を受け取る
- 受け取ったファイルに設定されている更新日時を、作成日時にそろえる
- 成功したときのみ exit code が 0 になる
日時の指定方法って、言語ごとにロケールやらタイムゾーンやらフォーマットやらが色々あってややこしいので、更新日時を、作成日時にそろえるという機能だけを提供することにしてみた。
こんだけシンプルにしたおかげで、いろんなスクリプト言語から使いやすくなってる。
例をあげておく。
Perl
更新日時・作成日時を現在時刻にする Perl スクリプト。
utime undef, 0, 'test.txt';
system('setctime test.txt') == 0 or die "failed";
utime()
してから、setctime.exe
を呼び出すだけ。
mtime も変わっちゃっうけど、嫌なら stat()
で取得して、utime()
で戻してやればよい。
Ruby
同じく、更新日時・作成日時を現在時刻にする Ruby スクリプト。
t = Time.now
File::utime(t, t, 'test.txt')
system 'setctime.exe test.txt' or abort 'failed'
File::utime()
してから、setctime.exe
を呼び出すだけ。Ruby では atime を省略できないらしいので、一時変数 t
を使って atime も更新している。
Python
同じく、更新日時・作成日時を現在時刻にする Python スクリプト。
import os
import sys
import time
t = time.time()
os.utime('test.txt', (t, t))
if os.system('setctime.exe test.txt') != 0:
sys.exit('failed')
実装詳細
50 行ほどの簡単な Windows プログラミングになっている。ファイルを開いて、GetFileTime()
して SetFileTime()
しているだけ。
開発は Visual C++ 2008 で行っている。もっと新しいやつでやってもいいんだけど、新しいやつで開いたら自動でプロジェクトファイルが変換されるので、手元にある一番古い Visual C++ で開発している。
Visual C++ 再頒布可能ランタイム問題
Visual C++ で開発すると、再頒布可能ランタイムの扱いをどうするかが悩ましい。
静的リンクするとサイズが大きくなる。動的リンクすると開発者と同じバージョンの Visual C++ ランタイムが入ってないと、実行時にエラーになる。
今回は小さなアプリケーションだったので、libc.lib
をリンクしないようにした。printf()
が使えなくなったり、argc
argv
が参照できなかったり、それなりに制限はあるけど Win32 のネイティブな API を使って回避できる。
そんなアクロバットな努力の結果、実行ファイルのサイズが 4KB と小さくなって、ランタイムライブラリーのバージョン問題に悩まなくてよくなった。
まとめ
どうぞご利用ください。