- note -

Batch

3/10

sleepの実現   - Last modified:2014/7/19

※1 XPまでのお話。 WinSrv2003以降は代替コマンドがある。 また、リソースキットにもsleepがある。 何故かWindowsにはsleepコマンドがありません(※1)。
指定時間だけ動作を待機させたいというケースは往々にしてあるものです。
というわけでWindowsにおけるsleep処理実現方法をいろいろ考えてみました。

その1 exeファイルを作る

Cなどの開発環境がある前提です。
例えばC言語なら、sleepを実現するソースファイルは
以下のような感じになります。
#include <stdlib.h>
#include <windows.h>
int main(int argc, char *argv[])
{
  int sleepTime;
  if (argc < 2) return 0;
  sleepTime = strtol(argv[1], NULL, 10);
  if (sleepTime > 0) Sleep(sleepTime * 1000);
  return 0;
}

その2 batで時間を計る

頑張ればbatのコマンドだけでsleep処理を実装することも可能です。
難点は、batファイル中でsleepを呼び出すときにcall sleepと記述する必要があることです。
call sleep … doskeyを使えば一応解決できた気になれる。 以下がその例。
@echo off
setlocal
set starttime=%time%
if "%~1" == "" exit /b
set /a second=1%starttime:~6,2% - 100 + %1
set /a minute=1%starttime:~3,2% - 100 + second / 60
set /a hour=%starttime:~0,2% + minute / 60
set /a days=hour / 24
set /a second=second %% 60
set /a minute=minute %% 60
set /a hour=hour %% 24
if "%second:~1%" == "" set second=0%second%
if "%minute:~1%" == "" set minute=0%minute%
if "%hour:~1%" == "" set hour=0%hour%
set endtime=1%hour%:%minute%:%second%.%starttime:~-2%
set starttime=1%starttime: =0%
:busy_loop
set checktime=1%time: =0%
if %days% == 0 (
  if %checktime% geq %endtime% exit /b
) else (
  if %checktime% lss %starttime% set /a days-=1
  set starttime=%checktime: =0%
)
goto busy_loop
ping … nslookupでも出来るが、pingの方がよさげ。

その3 pingを使う

実はpingコマンドを使ってsleepのような処理が実現できます。
その2と同じく、batファイルでの利用時にはcallする必要があります。
@echo off
setlocal

if "%~1" == "" exit /b
set /a sec=%1+1
ping -n %sec% localhost >nul

その4 WSHを使う

WSH(Windows Script Host)を利用する方法もあります。
これも同じく、batファイルでの利用時にはcallする必要があります。
以下はハイブリッドソースの例で、拡張子は.batになります。

WScript.Sleepを利用

@set @_=0// & @cscript //e:jscript //nologo "%~f0" %1 & @exit /b
WScript.Sleep(WScript.Arguments(0)*1000);

cscriptのタイムアウトを利用

@set @_=0;for(;;);// & @cscript //e:jscript //nologo //T:%1 "%~f0" >nul & @exit /b

その5 mshtaを使う

VBScriptだけでなく、javascriptもワンライナーで実行できます。
変な白いウィンドウが表示される? そんな細かいことを気にしていたらWindows環境でsleepはできません。
@start /wait mshta javascript:setTimeout('close()',%1)

その6 batでexeファイルを作る

最終奥義 ネタです。
debug … Windowsが64bit版の場合、debugコマンドは使えない。

※2この方法で作成されるsleep.exeは高確率でウィルス対策ソフトに引っ掛かるようなので注意。
debugでsleep.exeを構成するバイナリを直接作り出します(※2)。
@echo off
setlocal enabledelayedexpansion

set line1=f 100 300 0
set line2=e 100 "MZ",e8,c1,0,0,0,81,ec,c,0,0,0,89,4,24,89,c7,b8,0,0,0,0,b9,0,2,0,0,fc,f2
set line3=e 11e ae,b8,0,2,0,0,29,c8,48,89,44,24,4,81,ef,2,0,0,0,b8,20,0,0,0,8b,4c,24,4
set line4=e 13a eb,4,c0,0,0,0,fd,f3,ae,41,47,89,4c,24,4,89,7c,24,8,b8,22,0,0,0,f2,ae,41
set line5=e 155 47,3b,7c,24,8,74,5a,81,f9,1,0,0,0,75,17,8b,3c,24,3,7c,24,4,4f,b8,20,0,0
set line6=e 170 0,8b,4c,24,4,f2,ae,85,c9,74,3b,fc,8b,44,24,4,29,c8,48,89,c1,81,c7,2,0,0
set line7=e 18a 0,b8,0,0,0,0,ba,0,0,0,0,bb,a,0,0,0,f7,e3,8a,17,81,e2,f,0,0,0,1,d0,47,e2
set line8=e 1a8 f1,bb,e8,3,0,0,f7,e3,50,e8,18,0,0,0,fc,81,c4,c,0,0,0,c3,0,0,"PE",0,0,4c
set line9=e 1c5 1,1,0,ff,25,9e,11,40,0,ff,25,a2,11,40,0,78,0,3,1,b,1,0,0,78,11,0,0,9e,11
set line10=e 1e8 2,10
set line11=e 1f6 40,0,0,10,0,0,0,2
set line12=e 208 4
set line13=e 211 20,0,0,0,2,0,0,0,0,0,0,3,0,0,0,0,0,10,0,0,10,0,0,0,0,10,0,0,10
set line14=e 234 3
set line15=e 240 d0,10,0,0,20
set line16=e 259 10,0,0,0,10,0,0,0,2,0,0,1
set line17=e 277 "`kernel32.dll",0,0,"GetCommandLineA",0,0,0,"Sleep",0,84,11,0,0,96
set line18=e 2a3 11,0,0,0,0,0,0,"DOUDEMOEXEDOUDEMOEXEDOUDEMOEXEDOUDEMOEXEDOUDEMOEX"
set line19=e 2db "EDOUDEMOEXEDOUDEMOEXEDOUDEMOEXEDOUDEMO"
set line20=r bx
set line21=0
set line22=r cx
set line23=201
set line24=n _sleep
set line25=w
set line26=q

set cmd=
for /l %%I in (1,1,26) do (
  set cmd=!cmd!echo !line%%I!#
)

set cmd=!cmd:~0,-1!
set cmd=!cmd:#=^&!

(%cmd%) | debug

if exist sleep.exe del sleep.exe
rename _sleep sleep.exe
16bit(COM)の場合↓
@echo off
setlocal enabledelayedexpansion

set line1=e 100 8a,e,80,0,81,c1,2,0,bb,80,0,43,49,80,3f,20,74,f9,4b,c6,7,30,ba,10,0,f7
set line2=e 11a e2,8a,17,1,d0,2d,30,0,43,e2,f1,89,e5,81,ec,4,0,89,c1,8d,46,fc,8c,d2,e,e8
set line3=e 134 38,0,8a,7e,ff,80,ff,5a,77,19,85,c9,74,1a,8a,5e,fe,8d,46,fc,8c,d2,e,e8,20
set line4=e 14d 0,3a,5e,fe,74,f2,49,e9,e7,ff,b7,5a,e9,e2,ff,8d,46,fc,8c,d2,0e,e8,9,0,3a
set line5=e 166 7e,ff,77,f2,b4,4c,cd,21,52,51,53,1e,8e,da,89,c3,b4,2c,cd,21,88,2f,88,4f
set line6=e 17e 1,88,77,2,88,57,3,1f,5b,59,5a,cb
set line7=r bx
set line8=0
set line9=r cx
set line10=8a
set line11=n _sleep
set line12=w
set line13=q

set cmd=
for /l %%I in (1,1,13) do (
  set cmd=!cmd!echo !line%%I!#
)

set cmd=!cmd:~0,-1!
set cmd=!cmd:#=^&!

(%cmd%) | debug

if exist sleep.com del sleep.com
rename _sleep sleep.com

その7 timeoutを使う

Windows Server 2003以降のWindowsにはsleepの代わりとして使える
timeout … 他にはchoiceやwaitforなんてものも。 timeoutコマンドが用意されています。
vistaや7などではこれを使えばsleep処理が実現できます。

その8 rundllexを使う

困ったときのrundllexです。
rundllex Sleep,1000
使えそうなものはこんなところでしょうか。
最後に上記1~8それぞれの精度を計ってみたいと思います。
計測方法はめんどくさかったので
echo %time%&sleep 5&call echo ^%time^%
という感じに経過時間を計りました。
以下がその結果です。
1(Cで作ったexeファイル) : 5.016秒
2(bat処理で時間を計測) : 5.014秒
3(ping) : 5.048秒
4(WSH) : 5.075秒
5(mshta) : 5.150秒
6(batで作ったexeファイル) : 5.016秒
7(timeout) : 4.745秒
8(rundllex) : 5.034秒
0.4~0.8秒 … 誤差ってレベルじゃない。 ……何度やってもtimeoutだけ初めの1秒が0.4~0.8秒で進みました。
自分でexe作ったほうが良さそうですね。
0.
[No name]
clipboardData