- 北の空からみなみへ -
exblog staff

カテゴリ:&Tips;&code;( 46 )
実用的ワンライナー(スタートアップのOUTLOOK起動時に一定時間のWaitを入れる)
2018年 04月 04日
Windows 10 の sturtupで OUTLOOKを起動時に自動起動していたのだが、このところコケまくり。
そこで20秒ほどの待ちを入れてみた。
C:\Windows\System32\cmd.exe /c"timeout.exe 20&start "" "C:\Program Files\Microsoft Office 15\root\office15\outlook.exe" /cleanreminders"

引用符の入れ子っぽいが、ちゃんと動く。ショートカットに書くならワンライナーだと思う。(バッチとするほどじゃない)
/cleanreminders は OUTLOOK起動時にリマインダーアラームを初期化(音を停止)するオプション。
[PR]
by bucmacoto | 2018-04-04 20:29 | &Tips;&code; | Trackback | Comments(0)
コマンドプロンプトの1行ベンチマーク
2018年 03月 11日
先の1行FizzBuzzを反復実行して、所要時間から処理速度を判別しようとしたら、老化脳では「どのくらい?」という程度感覚が掴めなかった。
(また、処理出力をNULリダイレクトで隠してしまえばいいが、表示させてしまうと、100行×1000試行=10万行の彼方に開始時刻が流れ去っていて確認に困った)

なので、一行FizzBuzzの反復試行ルーチンのさらに前後に手を入れて、開始時間を表示し→計算反復の実行→終了時間を表示し所要時間を表示する。ように書き加えてみた。
(例によって後で再度使い回す場合のために、備忘録として記録)

説明文を簡便化するために、実行する本体を仮に FIZZBUZZとおこう(代入だね)。

実行土台となる CMD.EXE は、スイッチ /v:on で遅延変数展開ONでコマンドプロンプトを起動しておく。
(現在時刻変数 time の一行内の時間差から、所要時間を算出するのだから、これは必須。)
このところ、砂場遊びでは次の一行をコマンドプロンプト起動直後に入れるのがルーチン化している。(自動でこうなるようにできるが、それは禁じ手=職場PCなので)

color F0&cmd/v:on/k prompt $g ^&title SandBox

FIZZBUZZの1,000回実行コードはこんな感じ。

for /L %z in (1,1,1000) do FIZZBUZZ
これを画面に表示させずNULへ流すには、括弧で括ってリダイレクトアウト。
(for /L %z in (1,1,1000) do FIZZBUZZ)>nul

この前に現在時刻を取得し、深夜0時からのミリ秒数計算のコードをおく。(バッチ内でない限り、set/a で代入するための計算結果はコンソール表示されてしまうので、まとまるように書く)

バッチ出力を最低限の改行で済ませるために、コードは set/p&nul="改行なし表示出力" の形式を多用。
投稿ボックス内で自動改行されると空白などが分かりにくいから横長表示で。
(ついでにマウスオン解説表示も多用)
この上には表示や計算結果や解説が表示されます。
set/p&nul=start=%tilme%[
start= 01:23:45.67[5025670]
set/p&nul=start=%tilme%[
&set stime=%time%
変数 stime(=start time) に時刻を代入(※実のところ1行処理なら無用=後で解説)
set stime=%time%
&set/a start
変数 start にミリ秒変換した開始時刻を代入(表示もされる)
set/a start
=1000*(!stime:~0,2!*3600+(1!stime:~3;2!-100)60+(1!stime:~6,2!-100)
start= 01:23:45.67[5025670] "時刻文字列"を秒数に
!stime:~0,2!*3600+(1!stime:~3;2!-100)60+(1!stime:~6,2!-100)
)+1!stime:~-2!0-1000
start= 01:23:45.67[5025670]
+1!stime:~-2!0-1000
&echo.]
start= 01:23:45.67[5025670]
echo.]
&

後半もほとんど同じコードですね。
この上には表示や計算結果や解説が表示されます。
&set etime=!time!
変数 etime(=end time) に時刻を代入完了「直後」の時刻
set etime=!time!
&set/p&nul=end=!etime![
start= 02:08:45.67[7725670] average=270ms
set/p&nul=end=!etime![
&set/a end
変数 end にミリ秒変換した処理完了時刻を代入(表示もされる)
set/a end
=1000*(!etime:~0,2!*3600+(1!etime:~3;2!-100)60+(1!etime:~6,2!-100)
end= 02:08:45.67[[7725670] average=270ms "時刻文字列"を秒数に
!etime:~0,2!*3600+(1!etime:~3;2!-100)60+(1!etime:~6,2!-100)
)+1!etime:~-2!0-1000
end= 02:08:45.67[7725670] average=270ms
+1!etime:~-2!0-1000
&set/p&nul=] average=
end= 02:08:45.67[7725670] average=270ms
set/p&nul=] average=
&anp;set/a ltime=(!end!-!start!)/1000
end= 02:08:45.67[7725670] average=270ms(計算結果は改行なしで表示される)
set/a ltime=(!end!-!start!)/1000
&echo.ms
end= 02:08:45.67[7725670] average=270ms
echo.ms

あとは、前半コード+FIZZBUZZコード+後半コード のサンドイッチで出来上がり。
ということで職場で私が触れる範囲のPCやWSでベンチマークを実行してみました。(結果はmore欄で)

ですが、この投稿にまとめていて気づいたのです。
遅延変数展開下の環境では、変数参照に !変数!(遅延展開=実行時に動的変数として展開)と %変数%(時即時展開=コマンドライン改行の瞬間に一行内の変数は確定)とが、「どちらも」使えるのでした。

つまり、サンドイッチに挟まなくても、
(処理) & (終了時刻「!time!」・開始時刻「%time%」で所要計算)
みたいにすれば、より簡明な記述ができたのですね。。。。orz...

さらにループ回数を柔軟に設定できるように書き換えると、こうなります。
(実地検証でこけまして修正)
(if not defined $count set/a $count=1000)&(for /L %z in (1,1,!$count!) do FIZZBUZZ)>nul&set etime=!time!&set/p<nul=%time%-!etime!/!$count!Loop Average = &set/a ltime=(1000*(3600*!etime:~0,2!+60*(1!etime:~3,2!-100)+1!etime:~6,2!-100)+1!etime:~-2!0-1000-(1000*(3600*%time:~0,2%+60*(1%time:~3,2%-100)+1%time:~6,2%-100)+1%time:~-2%0-1000))/!$count!&echo ms
1日を跨ぐ場合には、ltime が負数になるので、86400000を加え86400000で剰余を取るなどして正数化したミリ秒数を 試行回数 $countで割る。このように扱う必要もあるけど、そこまで使うケースはおそらくない。
(if not defined $count set/a $count=1000)&(for /L %z in (1,1,!$count!) do cmd/q/v:on/c"for /L %a in (0,1,6) do for %A in (1 2 Fi 4 Bu Fi 7 8 Fi Bu 11 Fi 13 14 FizzBu) do if %a11==6%A (exit) else if %A gtr a (echo.%Azz) else set/a $=15*%a+%A&echo.")&set etime=!time!&set/p<nul=%time%-!etime!/!$count!Loop Average = &set/a ltime=((1000*(3600*!etime:~0,2!+60*(1!etime:~3,2!-100)+1!etime:~6,2!-100)+1!etime:~-2!0-1000-(1000*(3600*%time:~0,2%+60*(1%time:~3,2%-100)+1%time:~6,2%-100)+1%time:~-2%0-1000))+86400000)%86400000/!$count!&echo ms


じっこうけっか と 小さな説
[PR]
by bucmacoto | 2018-03-11 22:09 | &Tips;&code; | Trackback | Comments(1)
砂場遊び(1行FizzBuzz)/後片付け
2018年 03月 08日
砂場遊び(1行FizzBuzz)

bugmacotoとも言うべきお間抜けコードを前投稿(more欄)で晒した。Windows実機でちゃんと動くコードに直したので再掲する。
なお、ちょこっとずつ解説入れておこう。(自分でも後から可読する自信はないらしい)

まずは、最短(なんちゃってなので105までカウントアップが突っ走り)なBizzBuzz方言版

cmd/q/v:on/c "for /L %a in (0,1,6) do for %A in (1 2 i 4 u i 7 8 i u B i D E izzBu) do if %A gtr f (echo.B%Azz) else set/a $=15*%a+0x%A&echo."
142 ↑ byte
    cmdのオプション
  • /q : echo off状態で起動
  • /v:on : 変数の遅延展開を有効にした状態で起動
    バッチで SETLOCAL ENABLEEXTENSIONS 宣言下と同じ状態
  • /c : 残り全部を引用符で囲まれたパラメータ(引数)を続けると、最初と最後の引用符が外されてコマンドライン上で実行される感じになる。
    (マイクロソフトのcmd.exeの文字列を扱う実装は、ちょっとずつ状況に応じて変化する=一貫性はない感じ。
     BASIC時代から、対称性の破れみたいな実装を好んでいる感じがする。)
    遅延展開有効下での例 : 比較文字として キャレットで「|」と引用符「"」を比較
    引用符にキャレットって無効では?(なんと有効でした。むしろ \" なんて書くとエラーします)

    > if ^| gtr ^" echo !time: =0!
    20:00:00.00

    > if ^| gtr ^" echo.!time: =0!
    !time: =0!

    echoの後ろに、空白区切りとピリオド区切りとで、変数の拡張展開されたり、されずにそのまま文字列表示になったり。

次に16進アイデアを温存したままで、方言ではないFizzBuzzへの対応版
無理やりな文字列比較をして動くようになったバージョン
なお、16進コードの A~F は大文字でも小文字でもいいらしい。
さらには、引用符あっても(0x"F" と書くとそれは 15 になる)OKという実装。

cmd/q/v:on/c "for /L %a in (0,1,6) do for %A in (1 2 Fi 4 Bu Fi 7 8 Fi Bu "b" Fi "d" "e" FizzBu) do if %A gtr "f" (echo.%Azz) else set/a $=15*%a+0x%A&echo."
156 ↑ byte
    if での比較は、記号 < 数字 < 文字
    (同じ文字なら大文字が大きいが、大文字の A は小文字の b より小さいという定義 )
  • 例 :
    >if b gtr A echo
    ECHO は <ON> です。
  • set/a での変数へ代入する計算結果はバッチ内では結果は表示されないが、コマンドライン上では表示される。
    なお、改行はされない。
    (echo ではなく set/p<nul= で出力したのと同様な吐き出し方をしてる)
  • set/a $= : $は仮変数(ダミー)
    昨日の寝ぼけた式は間違いで、こちらがちゃんとしたカウントアップ。
  • &echo. : 数値は吐き出されても改行はされていない。なので空改行を入れている
    (echo だけでは 「ECHO は <ON> です。」が出るのはおなじみ)

文字比較式があんまりなので、16進数のアイデアを放棄したらむしろ短くなった。
これでかなりすっきりして、分かりやすくなった。

cmd/q/v:on/c "for /L %a in (0,1,6) do for %A in (1 2 Fi 4 Bu Fi 7 8 Fi Bu 11 Fi 13 14 FizzBu) do if %A gtr a (echo.%Azz) else set/a $=15*%a+%A&echo."
149 ↑ byte
  • >ここでは、gtr で 最小の英文字 a と文字比較している。
    より一般的にするなら、if %A geq a とすることで、変数%Aに代入された全ての文字(記号と数字以外)が処理対象となる。
    結果、else 以降は数字処理に専念となる。
  • %A=Fi : 後ろに zz で Fizz
  • %A=Bu : 後ろに zz で Buzz
  • %A=FizzBu : 後ろに zz で FizzBuzz

最後に仕上げ。

FizzBuzz問題は、1から100 という条件なので、それに対応する IF文を加える。
cmdの /c スイッチに続くのが引用符つきのパラメータの場合は、直後の空白を削っても正しく解釈されるのでそうした。

cmd/q/v:on/c"for /L %a in (0,1,6) do for %A in (1 2 Fi 4 Bu Fi 7 8 Fi Bu 11 Fi 13 14 FizzBu) do if %a11==6%A (exit) else if %A gtr a (echo.%Azz) else set/a $=15*%a+%A&echo."
173 ↑ byte
    遅延変数展開下とはいっても、%変数%の形式はしっかり有効なまま。
    (改行するまでは、展開=反映されない変数として普通の環境同様に扱われる)
  • if %a11==6%A (exit) ← これはOK
  • if %a%A==611 (exit) ← これNG
  • ※ なぜなら %a% という変数展開を優先されてしまうので 下の例では
    「%a%A」という4文字を 611 と比較する式に解釈される。
    そこで、たすき掛けにしてやるとまともに動いた。


(more欄のスクリーンショットは撤廃)→ 反復実行の前後時間から平均所要時間を算定の 1行ベンチマークテスト については次の投稿で。
[PR]
by bucmacoto | 2018-03-08 21:13 | &Tips;&code; | Trackback | Comments(0)
砂場遊び(1行FizzBuzz)
2018年 03月 08日
へっぽこ腕試しに、やってみた。(手元にwindowsマシンがないのでアイデアを練ること1日。コード作業に試行錯誤で数時間)
環境文字指定の自己参照、と、文字切り出し位置を変数で指定するのに for 文で変数を作ればできそうというアイデアで挑戦。
cmd/q/v:on/c "for /L %A in (1,1,100) do (set/a I=%A%3,Z=%A%5>nul&(for %B in (!I!) do set I=!I:~%B,1!)&for %B in (!Z!) do set Z=!Z:~%B,1!)&set $= &(if defined I set $=!$!Fizz)&(if defined Z set $=!$!Buzz)&set $=!$:~1!&(if not defined $ set $=%A)&echo !$!"
254 byte
実行結果
c0062295_19155944.gif

私にはこれが限界だったが、さすがはプロの方は更に100Byteほども小さく仕上げていた。(コードは他人様のですので実行結果だけ表示)
c0062295_19234540.gif

FizzBuzz問題についての、Wikipediaの解説

もう一日の熟考結果
[PR]
by bucmacoto | 2018-03-08 00:47 | &Tips;&code; | Trackback | Comments(0)
cmd.exe 砂場遊び /3.5~番外地後片付け
2018年 03月 07日
先の ワンライナー番外地 の投稿でキャレット使いまくりが恥ずかしいと書いた。

ふと文字列扱いのネタを思い出して引っ張り出してみた。
こちらは1月にXP上で変数の挙動テストした時のスクリーンショット。
c0062295_21053456.gif
なんだか分かりにくいが中ほど過ぎで、for /f "delims=" %c in (" 123 " 456 " 789 ") do echo %~c となっているところを見てほしい。引用符の最初と最後だけが外れるように見えている。

そういえば、cmd/exe へのパラメータ渡しは、引用符であればキャレット外せるのだと気付いた。

cmd/? でヘルプを読むと、次の部分の2番目に該当させればよさげである。
/C または /K が指定されている場合、スイッチの後の残りのコマンド ラインが
コマンド ラインとして処理されます。次のルールが引用符 (") の処理に使われます:
  1. 次のすべての条件に一致する場合、コマンド ラインの引用符が有効になり
    ます:

    - /S スイッチがない
    - 引用符が 1 組ある
    - 引用符の中に特殊文字がない
    (特殊文字は &<>()@^| です)
    - 引用符の中に 1 つ以上のスペースがある
    - 引用符の中の文字列が、実行可能ファイルの名前である

  2. 最初の文字が引用符であるにも関わらず上の条件に一致しない場合は、最初
    の引用符とコマンド ラインの最後の引用符が削除され、最後の引用符の後
    のテキストが有効になります。
そうして、以下のように修正した。(パス数も99を上限に)
cmd/q/v:on/k "prompt $g &for /L %i in (101,1,199) do set i=%i&for /F "delims=;" %p in ("!PATH!") do set $path!i:~1!=%p&set path=!PATH:%p;=!&if not defined PATH set $path&set/a i-=100>&2&exit !i!"
(この実行環境はWindows 7)
c0062295_15264611.gif


これで更に連結すると、パス文字分割ワンライナープログラムが一丁あがり
c0062295_15340265.gif

cmd/q/v:on/k "prompt $g &for /L %i in (101,1,199) do set i=%i&for /F "delims=;" %p in ("!PATH!") do set $path!i:~1!=%p&set path=!PATH:%p;=!&if not defined PATH set $path&set/a i-=100>&2&exit !i!">tmp.txt&(for /F "delims=" %A in (tmp.txt) do set %A)&del tmp.txt

で、やってみて気付いたのだが、ヘルプの中で、「最初と最後の引用符」みたいに書いてあったが、上記の場合にはちゃんとcmd.exe へ渡すパラメータの外で、連結されていた。(ちゃんと動いている)
引用符直後にリダイレクト記号があるからなのかわからないが、しばらく砂場遊びを離れてから後で見直すと、戸惑うかもしれない。

※追記※ 上記を 遅延環境変数展開(CMD.exe /v:on)下で実行してはなりません。(バッチ流用時には要注意です)
 理由 = !PATH! の文字が引用符内で展開されてコマンド(cmd/q/v:on/k)に渡されてしまいます。
(この現象確認は、Windows XP SP3 においてなされました)


xp上で動くchoice コマンドもどきは改善途上だが、少しだけ進化
[PR]
by bucmacoto | 2018-03-07 18:15 | &Tips;&code; | Trackback(1) | Comments(0)
砂場遊び /ワンライナー番外地
2018年 03月 04日
エラーレベル(%errorlevel%)をセットするのに cmd/c exit <数字> とやると、任意の数のエラーレベルを戻り値として返すことができると気づいた。

そこで、パス文字列を 擬似配列変数に分解し、エラーレベルとしてパス文字のフォルダ数を返し、パスの擬似配列リストを標準出力するワンライナーを書いてみた。
(windows 10 と XP で動作確認)

キャレットエスケープ使いまくりなのが恥ずかしい。
cmd/q/v:on/k prompt ^^^> ^&path=%path%;^&path=!path:;;=;!^&for /L %i in (1001,1,1999) do set/a $i=%i^>nul^&set $i=!$i:~1!^&for /F "delims=;" %p in ("!PATH!") do set $PATH!$i!=%p^&set path=!PATH:%p;=!^&if not defined PATH set/a $c=%i-1000^>nul^&set $PATH^&exit !$c!
 ↑ これは1行なのでメモ帳的には下の感じ。
cmd/q/v:on/k prompt ^^^> ^&path=%path%;^&path=!path:;;=;!^&for /L %i in (1001,1,1023) do set/a $i=%i^>nul^&set $i=!$i:~1!^&for /F "delims=;" %p in ("!PATH!") do set $PATH!$i!=%p^&set path=!PATH:%p;=!^&if not defined PATH set/a $c=%i-1000^>nul^&set $PATH^&exit !$c!


more /ちょこっと解説
[PR]
by bucmacoto | 2018-03-04 04:58 | &Tips;&code; | Trackback(7) | Comments(0)
砂場遊び /3.4 (ちょこっと後片付)
2018年 03月 04日
実は先日投稿のソースにはバグがある。(翌日にちょこっと手直し済み)
ワンライナー側ではなく、それを呼び出して >$msgbox.vbs としているところが不具合だ。

秒数が1桁だとポップアップしないので、スクリーンショットを撮るときは 09 のような形式で無理やりに動作させていた。
これは、リダイレクト記号の直前に1桁の数字があると、それを入出力デバイス番号とみなすという仕様による。
コマンドやパラメータ間を空白で区切るのがDOS系OSの基本流儀。なのではあるがこのリダイレクト記号直前のデバイス番号については、挙動がおかしいことに気づいた後しばらくして理解した。(ちなみに リダイレクトインの直前デバイス番号は 0< に、リダイレクトアウトの標準出力は 1> 、標準エラー出力は 2> になる)

そしてもう一つ、こちらはごくちいさなパフォーマンス劣化に過ぎないので実害はごくわずか。
vbscript(ファイル=$msgbox.vbs)を実行させるために、cscript.exe から実行させていたが、これは wscript.exe から呼び出す方が、僅かに早いようだ。
(なんとなくCUI=文字操作側のcscriptの方がキビキビ動きそうなものだが、GUIを操作するwscriptオブジェクトだからそちらが本職なのだと憶えよう)

上半分はwscript(9秒のタイムアウトで9.75秒)、
下半分はcscript(9.80秒)。
c0062295_21055058.gif


一方で、手直し前のコードだとタイムアウト設定のつもりの 秒数=9(第4引数) が、(ありはしない)デバイス番号と解釈されているらしく消えている。
そのため秒数設定が空文字となって、ノータイムで戻っている = 戻り値(エラーレベル)が、0 となっていることでまともに動いていないことがわかる。
上半分はcscript(ノータイムの戻りで1.72秒)、
下半分はwscript(1.70秒)。まあほとんど誤差に近いが、上下逆にしても同じ速さ順だから、違いがあるのは間違いではないようだ。
c0062295_21092064.gif


ところで、このリダイレクト記号直前の数字だが、1> は標準出力先を、2> は標準エラー出力先を切り替えるときに使用すると説明される。
となると、0> ではどうなるか、3> や 4> ではどうなるかを確かめずにはいられないのが人情ではないだろうか?
(まあ、痴的好奇心と言われてしまえば、これはそうなのかも)


連続する変数を代入する場合に重宝するのが、for /L の文だが、この(変数代入をする)for や (条件分岐判断をする)if 文は、果たしてコマンドだと言えるのだろうか?
確かに、30年も前のMSDOSリファレンスガイドにもコマンド一覧に載ってはいた。けれども、次の二点で他の内部コマンドとは毛色が異なっている印象がある。
  • for/L とか if/I とかのように、直後に(空白を挟まずに)/スイッチのパラメータをつけるとエラーとなる。
    (set/a とか dir/s などではスイッチ直前が空白でなくとも動作する)
  • call if とか、call for のような、callで呼び出して実行することはできない。
    (call echo.〜〜 や、call set 〜=〜〜 のような実行方法が、ほとんどのコマンドで可能なのとは対照的)
そんなことを確認するのも含めて、遅延展開を有効にした cmd.exe 下で実験してみた。
(ほんのすこし前に気づいたのですが、cmd/v:on/k prompt ^^^> ^&title enabledelayedexpansion<リターン>とやると、遅延展開を有効=バッチ内で setlocal enabledelayedexpansion を宣言中と同じように !変数! の形式が使用できて、プロンプトとコマンド窓タイトルでどのような設定状況か混乱しない砂場遊びが可能なのですね)

more リダイレクト記号直前後の数字
[PR]
by bucmacoto | 2018-03-04 04:03 | &Tips;&code; | Trackback | Comments(0)
砂場遊び /3(やっぱワンライナーが好き)
2018年 02月 20日
年末作成のバッチ修正で苦労した。
プログラミング言語が使える人なら苦もない修正だろうが、バッチのみ(せいぜい WSH ≒ VBScript 止まり)で対応させようとすると結構行数が増えていく。

修正箇所はなんのことはない。CHOICEコマンドでの選択枝で分岐する部分。
作ってしまってから気づいたのだが、Windows XPなどには CHOICE.exe が付属していなかった。
そこで、バッチのみで割と汎用的な選択ルーチンを作ろうとして、行数が長いサブルーチンとなってしまった。(詳細は more 欄追記します)
まあなんとからしきものはできたものの、CHOICE.exe では可能なタイムアウトでの自動実行処理がどうしても実装できなかった。
そこでいっそのこと、正月にコメント欄で頂戴した助言に従って、VBScript のワンライナーをバッチから吐き出す形にしてみた。そしてこの件は解決をみた。

そうそう。その過程で気付いたことをひとつ。(結構、重要)
バッチのラベルって8文字までだと思い込んでいたしネットにもそのような上級者からの助言があったのだが、バッチの砂場で検証したらそうではないらしい。(少なくとも XP以降は大丈夫)
つまり、[:TST$parameters.test] などという長かったり拡張子あったりでもきちんと区別して動くようだ。
(ついでだが、ラベル直後に &~~~ とコメントも入れておける。これは exit/b &~~ や goto ~&~~ と同じだった)

さて、タイムアウト付きポップアップのサブルーチンはこんな感じ。
:TST$popup はワンライナーを吐き出す(表示する)サブルーチンを呼びだして、.vbsファイルに吐き出させてそれを実行する。(実行前後の時刻表示 、実行後エラーレベル表示 、 終了コードをvbsからの引き継ぎ値にしている)

:DSP$popup は VBScript が コロン[:]で区切ることが可能だそうなので、それを利用したワンライナー

横長表示が嫌いな方には申し訳ございません...
:TST$popup
echo off&call :DSP$popup %1 %2 %3 %4 >$msgbox.vbs&type $msgbox.vbs
echo.%time%&cscript //nologo $msgbox.vbs
echo %time%&echo.errolevel=%errorlevel%&del $msgbox.vbs&exit/b %errorlevel%

:DSP$popup&title / message / vbYesNo+vbQuestion+vbDefaultButton2 / 秒数
echo Dim intRet:Set objShell=CreateObject("Wscript.Shell"):intRet=objShell.Popup("%~2",%4,"%~1",%3):Wscript.Quit(intRet)
exit/b

このサブルーチンを直接呼び出すための仕掛けは 温故か懐古か発掘か /3 で書いた冒頭2行です。(mvandlog.bat の冒頭)

次のコマンド入力で実行してみます。
mvandlog !/! TST$popup メッセージ実験 "リターンを二番目のボタンに設定"+vbCRLF+" OK:ハイ の戻り値=6"+vbCRLF+" NO:いいえ 戻り値=7"+vbCRLF+" タイムアウトでは時間切れ=-1" vbQuestion+vbYesNo+vbDefaultButton2 09

実行してみてポップアップ表示中の結果はこちら。(文字サイズ大きめだったため画像サイズがブログ表示にフィットできず見にくくて申し訳ない)
c0062295_20145216.gif


イエスを選んでバッチから戻った直後に次のコマンドでエラーレベル確認した。
if not errorlevel 0 (echo.時間切れでした) else if errorlevel 7 (echo.いいえを押しました) else echo.はいを押して errorlevel=%errorlevel% でした
c0062295_20183999.gif
無事に砂場のテストは完了できた。

More
[PR]
by bucmacoto | 2018-02-20 21:22 | &Tips;&code; | Trackback(7) | Comments(0)
温故か懐古か発掘か /3
2018年 01月 24日
@if not "%~1/"=="!/!/" (goto start) else shift&shift&set exec$sub=%2&rem only sub exec[for debug and use tool]
set exec$sub>&2&set exec$sub=&goto %0&rem ←このgoto以降は戻ってこない
set exec$sub>&2&set exec$sub=&call :%0 %1 %2 %3 %4 %5 %6 %7&goto eof&rem ←こちら戻って来たのを終わらせてる
:start

:TST$mad
@rem for /f "tokens=1-30 delims=;" %%9 in ("%path%") do echo.%%~9&echo.%%~:&echo.%%~^;&echo.%%~^<&echo.%%~^=&echo.%%~^>&echo.%%~^?&echo.%%~@&echo.%%~A
for /f "tokens=1-8 delims=;" %%Z in ("%path%") do echo.%%~Z&echo.%%~[&echo.%%~^\&echo.%%~]&echo.%%~^^&echo.%%~^_&echo.%%~^`&echo.%%~a
for /f "tokens=1-4 delims=;" %%z in ("%path%") do echo.%%~{&echo.%%~^|&echo.%%~}&echo.%%~^~
exit/b
といったテスト用サブルーチンでも確かめたから、キャレット付きにすれば英数字(大文字小文字)〜一部記号までを1文字変数で使用可能なのは、コマンドライン限定ではない。
赤囲み Windows XP    緑囲み windows 10
c0062295_21250059.png
c0062295_22321712.png

上記の文字範囲が、連続して変数にできる文字。(漢字変数は文字コードが連続していてもダメケースを確認)
マルチバイトの記号領域はいけるかもだけど、確認検証は他人にお任せしたい。
単バイトの一部文字は特殊な意味・役割をもっているのだが、キャレット付きで変数として使用可能にしているという結論に。

for / f "tokenes=...." で、最大 tokens が31なのが残念。一気に長〜い展開できるなら、使い所があるかもです。


windows XP / 10 での動作確認結果
[PR]
by bucmacoto | 2018-01-24 13:37 | &Tips;&code; | Trackback | Comments(0)
しつこく cmd.exe の変数挙動いぢり
2018年 01月 06日
昔のDOSを含めある種の環境変数には特別な地位が与えられ、かなり固有の特権を有している。
特に、内部コマンド(path , prompt , date , time など)で扱えるようになっているものは、ローカル変数扱いに過ぎず親環境には反映できない。
コマンドの挙動も特有化していて、パラメータなしで入力しただけだと

path ⇒ 変数 %PATH% を表示、パラメータつけると追加
prompt ⇒ 変数 %PROMPT% をクリア(← set prompt=&rem と同じ)、パラメータつけると set PROMPT=パラメータ
date / time ⇒ 変数 %DATE% / %TIME% の設定(ただし管理者権限でないと変更不能)

という具合です。

ここで %date% %time% の両変数は、変数としては存在しているのに set コマンドで表示も消去もできないです。

> if defined date for %C in ("echo.変数dateはあるようです消してみます" "set date=" "echo.再確認してみます" "set date" "call echo.変数 %DATE% =%date%となっています") do %~C

上のワンライナーをコマンドラインで入力した結果が下記
> echo.変数dateはあるようです消してみます
変数dateはあるようです消してみます

> set date=

> echo.再確認してみます
再確認してみます

> set date
環境変数 date が定義されていません

> call echo.変数 %DATE% =2018/01/06となっています
変数 %DATE% =2018/01/06となっています
引用符つきでもしっかり展開されるのはどうみても健在ですね。

その一方でキャレット [^] はエスケープにならないと判っていてもつい頼るので、次の例示
今度はバッチで実行.
引用扱いが、コマンド出力だったりバッチ内容の方だったりするのは、ご容赦を

type temp.bat
@rem if defined は 変数 %date% が存在しているという意味で、必ず trueです。一行を縦並べに書くために使ってみました。
if defined date (
echo 単純引用は変数が消えます ⇒ "%d^ate%"
echo キャレットまみれも無駄 ⇒ %t^i^m^e: =0%
echo 二重で %d^ate%=%%d^ate%%
echo ダブル %% だと %tim^e%=%%tim^e: =0%%

rem for の変数の形で引用符に入れて実行してみます

for %%c in (
 "echo 引用符内でも単純引用はやり消えるかな ⇒ %da^te%"
 "echo キャレットまみれはどうだろう =%t^i^m^e: =0%"
 "echo キャレットなし二重はこう %%date:/=%%"
 "echo 普通にダブルでもいける %%time: =0%%"
 ) do %%~c
) else rem  ここはスペース開けるために利用しています

rem 引用符がないと、うまくいくっぽいけれど、キャレットの意味はないなぁ
for %%c in ("%%d^ate:/=%%" %%^date%% %%time%%) do echo %%~c

@echo バッチのなかから echo %%date%% と書くのと、コマンドラインで打つのでは結果が違うんですね。
echo %%date%%
echo.echo %%date%%>>test.bat & call test.bat &del test.bat

@rem パイプやリダイレクトも for /f でのコマンド結果に対する(テンポラリから)読み出していると考えて腑に落ちる。一時ファイルというのは暗黙的に作られているらしい。
@echo でもこれで、call で展開される理由づけになったか
echo %%date%%
call echo %%date%%


実行結果   (投稿後、二重引用に気付き削除修正済)
> if defined date (
echo 単純引用は変数が消えます ⇒ ""
echo キャレットまみれも無駄 ⇒ =0
echo 二重で %date%=%date%
echo ダブル % だと %time%=%time: =0%
rem for の変数の形で引用符に入れて実行してみます
for %c in ("echo 引用符内でも単純引用はやり消えるかな ⇒ " "echo キャレットまみれはどうだろう = =0" "echo キャレットなし二重はこう %date:/=%" "echo 普通にダブルでもいける %time: =0%") do %~c
)
単純引用は変数が消えます ⇒ ""
キャレットまみれも無駄 ⇒ =0
二重で %date%=%date%
ダブル % だと %time%=%time: =0%

> echo 引用符内でも単純引用はやり消えるかな ⇒
引用符内でも単純引用はやり消えるかな ⇒

> echo キャレットまみれはどうだろう = =0
キャレットまみれはどうだろう = =0

> echo キャレットなし二重はこう %date:/=%
キャレットなし二重はこう %date:/=%

> echo 普通にダブルでもいける %time: =0%
普通にダブルでもいける %time: =0%

> rem 引用符がないと、うまくいくっぽいけれど、キャレットの意味はないなぁ

> for %c in ("%d^ate:/=%" %date% %time%) do echo %~c

> echo %d^ate:/=%
%d^ate:/=%

> echo %date%
%date%

> echo %time%
%time%
バッチのなかから echo %date% と書くのと、コマンドラインで打つのでは結果が違うんですね。

> echo %date%
%date%

> echo.echo %date% 1>>test.bat & call test.bat & del test.bat

> echo 2018/01/06
2018/01/06
でもこれで、call で展開される理由づけになったか

> echo %date%
%date%

> call echo %date%
2018/01/06


More 1押し
[PR]
by bucmacoto | 2018-01-06 23:21 | &Tips;&code; | Trackback | Comments(0)