目次
- コマンドプロンプト・プログラミング 序
- コマンドプロンプト・プログラミング 々
- コマンドプロンプト・プログラミング 宴
- コマンドプロンプト・プログラミング 二次会 <- ★
- コマンドプロンプト・プログラミング 三次会
- コマンドプロンプト・プログラミング 壮行会
この二次会は予想に反して nkf に辿り着かなかった。
パイプの挙動
コマンドプロンプトと nix シェルはパイプの挙動が違う。
command | command
とあったとき、
コマンドプロンプトは前段コマンドの処理が終わってから後段に移る。
nix シェルの場合は、前後のコマンドが並列に動く。
nix には yes
コマンドがある。
これは引数無しで呼ぶと無限に y
を出力し続ける。
$ yes | head -n 3
y
y
y
yes
は OS からシグナルを受け取り終了する。
これぞ並列の力なり。
コマンドプロンプトではこうはいかない。
ユーティリティ
lua-rap:Lua ラッパー・テンプレート
同ディレクトリ内の自己同名 *.lua
を実行する。
@echo off
lua.exe "%~dpn0.lua" %*
if %errorLevel% neq 0 ( exit /b 1 )
exit /b
パイプからの行数分呼ばれる可能性があるので、
エラー値をちゃんと確認しておこう。
take:パイプからの出力行を得る
local limit = tonumber(arg[1]) or math.huge
if limit < 0 then error("param1 out of range") end
local i = 1
for line in io.lines() do
if i > limit then os.exit() end
print(line)
i = i + 1
end
@echo off
:loop
echo y
goto :loop
前回までの環境が整っているとする。
しかし、下記は実行しない方がいい。
> type bin\lua-rap.cmd > bin\take.cmd
> yes | take 3
y
y
y
プロセスが、存在しないパイプに書き込もうとしました。
プロセスが、存在しないパイプに書き込もうとしました。
プロセスが、存在しないパイプに書き込もうとしました。
・
・
・
(実行してしまったならば Ctrl+C
を押しっぱで抜けるまで待つ)
Win はこの様にシグナルを yes
に飛ばさない。
これは PowerShell も同様である。
したがって、パイプ前段に無限ループするものを持ってきてはならない。
また、膨大な行を出力するコマンドが来るのも非効率である。
> words 1 2 3 4 5 | take
1
2
3
4
5
> words 1 2 3 4 5 | take 3
1
2
3
drop:パイプからの出力行を捨てる
local limit = tonumber(arg[1]) or os.exit()
if limit < 0 then error("param1 out of range") end
local i = 1
for line in io.lines() do
if i > limit then print(line) end
i = i + 1
end
> type bin\lua-rap.cmd > bin\drop.cmd
> words 1 2 3 4 5 | drop
> words 1 2 3 4 5 | drop 3
4
5
lua-rap の改良
lua-rap.cmd
を単に lua-rap.txt
と拡張子を変え、
ラッパーの雛形としてとっておく。
そして新たに、その雛形を利用する lua-rap
を書く。
@echo off
if "%~1" == "" (
echo error: param1 does not exist.
exit /b 1
)
setLocal
set pwd=%~dp0
set pwd=%pwd:~0,-1%
set template=%pwd%\%~n0.txt
if not exist "%template%" (
echo error: %~n0.txt does not exist.
exit /b 2
)
type "%template%" > "%pwd%\%~n1.cmd"
endLocal
exit /b
lua-rap name
とすると、
lua-rap.txt
の内容を持った name.cmd
ファイルが出来上がる。
> lua-rap drop
> words a b c d e f | drop 3
d
e
f
これで type
を使わなくて済む。
出力行の結合
これは簡単であるが、演算子の優先順位には注意。
> puts 1 & words 2 3
1
2
3
> words a b c & words 1 2 3 | take 1
a
b
c
1
> (words 1 & words 2 3) | take 1
1
|
の方が &
よりも優先順位が高い。
map:出力行に対するマッピング
整数をインクリメントする incr
があるとすると、
> incr 99
100
> words 0 1 2 | map incr
1
2
3
こう動く。
if #arg == 0 then error("param1 does not exist") end
for line in io.lines() do
local cmd = string.format("%s %s", arg[1], line)
if not os.execute(cmd) then
error(string.format("%s execution error", arg[1]))
end
end
command | map incr
とすると、パイプからの行数分
os.execute("incr <line>")
にて incr
が起動される。
incr:整数インクリメント
if #arg == 0 then error("param1 does not exist") end
local n = tonumber(arg[1])
if math.type(n) ~= "integer" then
error("param1 must be an integer")
end
print(n + 1)
> lua-rap map & lua-rap incr
> words 0 1 2 | map incr
1
2
3
> words 0 1 2 | map incr | map incr
2
3
4
> words 0 1 a 3 4 | map incr
1
2
lua.exe: C:\usb\bin\incr.lua:6: param1 must be an integer
stack traceback:
[C]: in function 'error'
C:\usb\bin\incr.lua:6: in main chunk
[C]: in ?
lua.exe: C:\usb\bin\map.lua:7: incr execution error
stack traceback:
[C]: in function 'error'
C:\usb\bin\map.lua:7: in main chunk
[C]: in ?
int:整数フィルタ
if #arg == 0 then error("param1 does not exist") end
local n = tonumber(arg[1])
if math.type(n) == "integer" then print(n) end
> lua-rap int
> int a & int 1.1 & int 123 & int "789"
123
789
> words 1 a 2 b 3 c | map int
1
2
3
文字列操作への動機
例えば、ラッパー・テンプレート lua-rap.txt
を更新したとする。
それに伴って *.lua
のラッパーも更新する必要がある。
> dir /b bin\*.lua
drop.lua
incr.lua
int.lua
map.lua
puts.lua
take.lua
words.lua
> lua-rap words
> words drop incr int map puts take | map lua-rap
words
で手打ちするのが面倒だ。
が、今回はこの辺で手を打とう。
閉会
この後どんなコマンドが欲しくなるだろう?
「文字列を受け取って拡張子を除く」コマンドを作り map
に渡す?
シェルという視点からすれば悪くない気もする。
だが私は、もっと一般的なものが欲しい。
- 文字列を一文字ずつ分割
- 出力行を反転
一文字ずつ分割するには CP932 を処理しないとならない。
それを受け取るであろう Lua には UTF-8 で渡さないとならない。
また、反転処理には最低でも配列が欲しい。
環境変数を配列に見立てて操作はしたくない。
と考えていると、いつも聴こえてくる。
「糠に釘」
何が糠なのか?コマンドプロンプト?
違う!Windows だ!
「○○言語最高!」
「そう、Windows 以外ではね」
MSYS2 が頭をよぎるが、大きくてヤダ。
BusyBox が頭をよぎるが、やっぱドットファイルがヤダ。
Lua で コマンドプロンプト自体をラップするかぁ。。。
嗚呼、ほんと、二次会って嫌い。
無駄に長くなる。
Mac 喰いたくなってきたから帰りまーす。