by @hkoba
@hkoba(えちこば)
今日のスライド: hkoba.github.io → Webナイト宮崎 Vol.22
昨年のWebナイト宮崎 Vol.21:
git clone ... && cd ... chmod g+ws var var/db chown nginx var var/run sqlite3 var/db/myapp.db3 < sql/schema.sql
外部コマンドの呼び出しやファイル操作の、単なる羅列
他にも:CI/CD の中のアクションとか
短いから Shell で…
workDir=_build fileList=*.dat cp --update $fileListtt $workDir; # ←変数名間違い!なのにコマンド実行! ... # なのに停止しない!実行が続いてしまう!
# 変数代入 set workDir _build # ↓glob はワイルドカードにマッチするファイルのリストを返すコマンド set fileList [glob -nocomplain *.dat] # exec で外部コマンド呼び出し({*} はリスト展開) exec cp --update {*}$fileList $workDir
"..."
,
foo bar baz; # コマンド foo に引数 bar baz を渡す set cmd [list foo bar baz]; # 上記の処理を表す list を変数 cmd に入れる {*}$cmd; # 変数に入ったコマンドを実行
つまり:コマンドを操作するコマンドを作りやすい
→ Dry-Run やべき等、undo の仕組みを作りやすい
Dry-Run
べき等
undo
Dry-Run - 試運転(コマンドを表示するだけ。実行はしない。make -n)
make -n
べき等(idempotent) - 目標状態を満たしてない時だけ実行(何回呼んでも安全)
べき等(idempotent)
例: clean-backup.zsh (ここだけ Zsh です)
clean-backup.zsh
Zsh
#!/bin/zsh emulate -L zsh; setopt no_no_match zparseopts -D -K n=o_dryrun; # -n なら配列 o_dryrun に保存 function x {; # `-n` オプションで dry-run にする shell 関数 `x` print "# $argv" if (($#o_dryrun)); then return; fi; # -n ならここでリターン "$argv[@]" || exit $? } #==== 以下、やりたいこと ==== x rm -f *.bak
使い方
./clean-backup.zsh -n # rm -f ファイル… を表示するけど、実行はしない
先の仕組みでは dry-run 化できないもの:リダイレクトとパイプ
# リダイレクト x echo foobar > foo.txt # パイプ x find -size +10M | xargs rm -f
> | は Shell の組み込み構文 → 関数の外側で動いてしまう
>
|
Tcl ではリダイレクトやパイプは言語の構文ではない
exec echo foobar > foo.txt exec find -size +10M | xargs rm -f
exec コマンドが 単なる引数 文字列として > や | を受け取り、それを解釈して実行
package require cmdline array set ::opts [cmdline::getoptions ::argv { {n "dry-run"} }] proc =RUN args { exec -ignorestderr {*}$args 2>@ stderr } proc RUN args { puts "# $args" if {$::opts(n)} return # 最後の手前の引数にリダイレクトが指定されていない時は stdout へリダイレクト if {[lindex $args end-1] ni {">" ">@" ">>"}} { lappend args >@ stdout } =RUN {*}$args }
exec を RUN に変えるだけ: リダイレクトとパイプ も OK
RUN echo foobar > foo.txt RUN find -size +10M | xargs rm -f
変数間違いも、Dry-Run 時点でエラーになる
set fileList [glob *.bak] RUN rm {*}$fileListtt; # ERROR! ここで止まる
動的スコープなのに厳格 ← Shell慣れした人のつまずきポイント
静的検査が無い(実行するまで typo すら見つからない)
記述が長くなりがち
pip, gem, npm みたいなの無いの?(→ tcllib と tcler's wiki で我慢)
tclsh に -e EXPR, -c CMD みたいなオプションが無いのはとても不便
-e EXPR
-c CMD
全てを任せる言語じゃない、けど、雑用言語としては有益
Tcl は コマンド行に対するメタプログラミング が出来る Shell風言語!!?
もちろん Bash 全部の置き換えは無理
けど 仕方なく bash を使っている箇所 の何割かは、置き換えても良いかも?
ご清聴、ありがとうございました〜〜
#
exec
open
<
( )
# Google Cloud の現在のプロジェクトの VM の一覧を取得 set vmList [=RUN gcloud compute instances list --format=value(name)] if {$newVm in $vmList} { puts "VM $newVm は作成済み" } else { RUN gcloud compute instances create $newVm ... }
# @ は順方向のときはそのまま RUN, 逆実行の時は変数 @ に貯めるようなコマンドにしておく @ gcloud compute networks create $vpcName \ --project=$project -- \ --subnet-mode=custom--mtu=1460 ... @ gcloud compute firewall-rules create $vpcName-allow-icmp-ssh \ --project=$project \ -- \ "--description=google cloud 内部からのみ icmp, ssh を許可" \ --network=projects/$project/global/networks/$vpcName ... ... # -R オプションが指定されたときは、逆順に実行 if {$::opts(R)} { puts "=====================" foreach cmd [lreverse [set ::@]] { RUN {*}$cmd } }