(本ページは今後更新されません。最新版は zenn.devのBookをご覧ください)
ここに書いてあるようなことはだいたい「CMD.EXE /?」「for /?」で出るんだけど、長いので sh 経験者の観点でかいつまんでみた。
コマンド出力の引用
ls | while read line ; do echo ">>$line" ; done
には
for /F %%I IN ('dir /b') DO echo ">> %%I"
が対応する(丸括弧の内側にシングルクォートがあることに注意)
逆クォート相当のものも1行程度なら
for /F %%I IN ('echo AHAHA') DO SET "OUTPUT=%%I"
と出来なくもない。
for は頑張れば AWK に近いことも出来るので、「for /?」を見ることをお勧めする。
basename と dirname
dirname
相当は次のような感じで出来る。
[c:tmp]
$ type dirname.cmd
@echo off
for %%I IN ( c:\path\to\file ) do set "DIRNAME=%%~dpI"
echo %DIRNAME%
[c:tmp]
$ dirname
c:\path\to\
basename
の場合は %%~dp のところを %%~nx か %%~n にすれば OK。
%~ の後の「英小文字」は変数の中身を加工する意味があるので、変数名自身は「英大文字」にしておいた方がよい。
find -exec
これ、自分の %HOME%\bin に置いている、%HOME%以下のツリーにあるゴミファイルを片づけるバッチ。
cd %~dp0..
for /R %%I in (*.orig *~ svn-commit*.tmp .*.swp *.rej ) do (del /P "%%I")
最初の「%~dp0..」はバッチが置いてあるディレクトリの親ディレクトリの意味。
forfiles.exe (追記:2015.08.10)
最近の Windows では forfiles.exe という find 相当のコマンドも標準装備されている
forfiles /S /M *.cpp /C "cmd /c echo @path"
こちらは一度に /M オプションで指定できるパターンは一つだけのようだ。
だが、ファイル名の展開用マクロ(@path,@isdir)がいろいろ使える
ブロックIF
古い単語出た。IF や FOR は1行で書いてしまわないといけないかというと、丸括弧を使うと複数行に展開できる。
if "%1" == "a" (
echo arg is a
echo aaaaaaaa
) else (
echo arg is else
echo bbbbbbbb
)
ただし、環境変数の展開のタイミングが if 文実行前なので、そこは工夫が必要だ。
(詳しくは SET /?
を参照)
(追記:2018.09.15)
SETLOCAL ENABLEDELAYEDEXPANSION
を使えば「!環境変数名!
」形式の遅延環境変数の展開が使える(詳しくは SETLOCAL /?
のヘルプを見よう)。ただし、自分は綴りを覚えられないので、FOR の中からCALL :ラベル "%%I"
を使って誤魔化すことが多い。
シェルレベルでの二重起動防止
- ps は tasklist で代用可能
- grep は findstr で代用可能
- findstr の成否は ERRORLEVEL に出る
- IF ERRORLEVEL 1 くらいは知ってるよね
あとは分かるな?
tasklist | findstr ttermpro.exe
if errorlevel 1 (
rem 起動していない
)else(
rem 起動してる
)
where.exe (追記:2018.09.15)
C:\Windows\System32\where.exe は which と find の両方の機能がある。
オプションなしだと、全PATHを検索する which のような機能になる。
$ where nyagos.exe
C:\Users\hymko\Share\bin64\nyagos.exe
C:\Users\hymko\Share\bin\nyagos.exe
C:\Users\hymko\go\bin\nyagos.exe
/R オプションをつけると find に近くなる。
$ where /R . *.go *.md
C:\Users\hymko\go\src\github.com\zetamatta\findo\findo.go
C:\Users\hymko\go\src\github.com\zetamatta\findo\hoge and hoge.md
C:\Users\hymko\go\src\github.com\zetamatta\findo\readme.md
C:\Users\hymko\go\src\github.com\zetamatta\findo\system_linux.go
C:\Users\hymko\go\src\github.com\zetamatta\findo\system_windows.go
タイムスタンプの取得 (追記:2018.09.15)
※ 地域設定によって変わるかどうか、要確認
分以上(まだ、お手軽・早い)
$ type f.cmd
@echo off
for %%I in (readme.md) do set "STAMP=%%~tI"
echo %STAMP%
$ f
2018/09/13 22:35
秒まで(forfiles版)
$ type m.cmd
@echo off
for /F %%I in ('forfiles /P "%~dp1." /M "%~nx1" /C "cmd.exe /c echo @fdate-@ftime"') do set "STAMP=%%I"
echo %STAMP%
$ m readme.md
2018/09/13-22:35:40
※ 0時~9時までは、時部分が一桁になるので注意が必要
秒まで(where版)
$ type x.cmd
@echo off
for /F "tokens=2,3" %%I in ('where /R . /T "%1"') do set STAMP=%%I-%%J
echo %STAMP%
$ x readme.md
2018/09/13-22:35:40
※ 0時~9時までは、時部分が一桁になるので注意が必要
0~9時のタイムスタンプに対する対策(修正:2018.09.24)
where でも forfiles でも時間部分が一桁の場合、タイムスタンプの比較が不具合を起こす。やむをえないので正規表現で判別するというベタな方法で対応する。
for /F "tokens=2,3" %%I in ('where /R . /T "%~1"') do set STAMP=%%I_%%J
echo %STAMP% | findstr _[0-9]: > nul && set STAMP=%STAMP:_=_0%
文字列置換 (追記:2018.09.24)
bash だと
$ FOO=abcdef
$ echo ${FOO/cd/12}
ab12ef
という置換が使える。CMD.exe でも似たようなことが出来る。
$ set FOO=abcdef
$ echo %FOO:cd=12%
ab12ef
改行しない出力 (追記:2020.01.19)
DOSコマンドで、好きな形式に「パスのコピー」する - Qiitaより
echo -n MESSAGE
相当の改行しない echo は
SET /P ="MESSAGE" < NUL
で実現できるようだ。この SET は本来 SET /P VAR=PROMPT
という形式で、PROMPT を表示した後、ユーザが入力した値を環境変数 VAR に設定するコマンドだが、環境変数名は省略できる。さらに標準入力を NUL でリダイレクトすることで、ユーザ入力自体も素通りさせられる。
今日の日付の取得 (追記:2020/04/03)
地域を日本に設定している場合、%DATE%
で yyyy/mm/dd
形式の日付を得られる。スラッシュを削除して yyyymmdd
形式で得る場合、%DATE:/=%
とすればよい。
地域設定関係なく、yyyymmdd
形式で得たい場合、以下のコマンドで、
$ wmic os get LocalDateTime
LocalDateTime
20200403103451.442000+540
と得られるので、
for /F %%I in ('wmic os get LocalDateTime ^| findstr ^^[1-9]') do set DT=%%I
echo %DT:~0,8%
といった方式で 20200403
といった文字列が表示される。
(wmic については…ちょっと出典失念)
複数行 echo を括弧でまとめる (追記 2020/10/09)
( echo (arxload "%ROOT%/hoge1.arx"^)
echo (arxload "%ROOT%/hoge2.arx"^)
echo (arxload "%ROOT%/hoge3.arx"^)
) > "%CADDIR%\hoge.scr"
- 複数の echo を丸括弧でまとめて、一つのリダイレクトで出力させることができる。
- echo 中で丸括弧の閉じる方を出力する場合は
^
でエスケープする必要がある
パスの末尾の余分な \ を削除する (追記 2020/10/09)
echo %CADDIR%| findstr "\\$" >nul
if not errorlevel 1 set "CADDIR=%CADDIR:~0,-1%"
- パイプ記号
|
の直前に空白を入れると機能しなくなるので、注意!
バッチファイルでサブコマンド (追記:2020.10.18)
call :"%~1"
のように、ラベル名を二重引用符で囲むと、サブコマンド名が渡されなかった時も処理できる。
setlocal
call :"%~1"
endlocal
exit /b
:""
:"all"
go fmt
for %%I in (386 amd64) do (
set GOARCH=%%I
if not exist cmd\%%I mkdir cmd\%%I
go build -o cmd\%%I\findo.exe -ldflags "-s -w"
)
exit /b
:"package"
for %%I in (386 amd64) do zip -j findo-%%I-%DATE:/=%.zip cmd\%%I\findo.exe
exit /b
:"upgrade"
for /F %%I in ('where findo') do copy /-Y findo.exe "%%I"
exit /b