.bat バッチファイルでファイルのタイムスタンプを更新する touch コマンドの代わり


これは何

Windows のバッチファイルで、ファイルのタイムスタンプを更新する方法を紹介します。

  • できること
    • 空ファイルの作成
    • ファイルのタイムスタンプ更新
  • できないこと
    • オプションを使うこと。タイムスタンプを指定時刻に変更したりできません
    • ディレクトリのタイムスタンプを更新すること
  • 多分できないこと
    • ネットワークパスのファイルに対する touch 操作。試せてないのでわかりません。誰か、成功したら教えて

想定読者

WSL 、 cygwinmsys2 を Windows に install したくない人
外部ツールにできるだけ依存したくない人

結論

touch.bat
@echo off
if "%~1"=="" exit /b 0
if "\\.\"=="%~dp1" exit /b 0
pushd "%~dp1"
if not exist "%~nx1" (
  copy nul "%~nx1">nul
) else if not exist "%~nx1\" (
  copy /B /Y "%~nx1"+>nul
)
if errorlevel 1 (
  popd
  exit /b %errorlevel%
)
popd
exit /b 0

実行例

正しい使い方

通常のファイルに対する touch 操作は、成功すると 0 が返ります。
アクセス権限等で失敗すると非0が返ります。

通常のファイルに対する touch

C:\a>dir
2019/10/12  00:27    <DIR>          .
2019/10/12  00:27    <DIR>          ..
C:\a>touch.bat foo.txt
C:\a>echo %errorlevel%
0
C:\a>dir
2019/10/12  00:27    <DIR>          .
2019/10/12  00:27    <DIR>          ..
2019/10/12  00:27                 0 foo.txt
C:\a>touch.bat foo.txt
C:\a>echo %errorlevel%
0
C:\a>dir
2019/10/12  00:27    <DIR>          .
2019/10/12  00:27    <DIR>          ..
2019/10/12  00:30                 0 foo.txt

読み取り専用ファイルに対する touch

読み取り専用属性を付与した foo.txt に対する touch

C:\a>touch.bat foo.txt
アクセスが拒否されました。
C:\a>echo %errorlevel%
1

正しくない使い方

特殊ファイルやディレクトリ等、 touch.bat が対応していないファイルに対する touch は 0 が返ります。

特殊ファイルに対する touch

C:\a>touch.bat nul
C:\a>echo %errorlevel%
0

ディレクトリに対する touch

C:\a>mkdir bar
C:\a>touch.bat bar
C:\a>echo %errorlevel%
0

雑談

バッチファイルの変数展開は、文を評価するタイミング展開されてしまうので、 if 文や for 文の中で変数を更新しようとすると、色々と面倒臭くて嫌いだった。しかし、touch.batを作成していて、初めて便利だと思った。
それは、copy が失敗したときの復帰処理が書きやすかったことです。
復帰処理は、copy の戻り値を呼び出し元に転送しつつ、popdを実行して元のディレクトリに戻る必要があります。つまり、copy の戻り値を exit /b の引数に渡しつつ、popdの実行をすることになります。if文の中で popdexit /b %errorlevel% を実行すると、if文を評価するタイミングで %errorlevel% が評価されるので、exit /b の引数に指定した %errorlevel%popd の英共を受けません。その結果、中間変数に %errorlevel% の値を保持する必要はなくなります。素晴らしい。
バッチファイルで一時変数を使う場合、 setlocal しないと環境変数が汚れるので、できるだけ使いたくありません。その点でも良いと思いました。

参考リンク

【Windows】空のファイルを作成する(Linuxで言うtouch)
Windows OSでファイルの最終変更時刻を更新する(touchコマンドを実現する)
batファイルでコマンドの実行結果を出力しないようにする方法
How to test if a path is a file or directory in Windows batch file?