あ、それ包んでください。
今日も、始めて知って悶絶した。な系の話w
shellscript は、
test
コマンド使っての文字列長の評価について。
シェルスクリプト(sh)で、ある変数が空文字列かどうかを調べるには、testコマンドの -n (Nonzero length) または -z (Zero length) を使う……と書いてあるんだけど、コツがいる。
if [ -n $HOGE ]; then
echo "nonzero だよ。"
fi
↑これはダメif [ -n "$HOGE" ]; then
echo "nonzero だよ。"
fi
これならOK。
test
コマンド、-n
やら -z
オプション使って文字列長を正しく評価したいなら、評価対象はクォートするべき*1。とゆ事を学んだ。今日。
特にそれが変数とかの場合。
要は、変数が文字列埋め込みされてないと上手く動かない。何故か?はよくわからない。(シェルに於ける変数の概念はどういうものなのか?)
まったくだっ。
実に地味な話なのもあり、中々情報に辿り着けず。*2
さらにチラチラ見えてきているその原因が、あまりのイージーさ故、すんなり受け入れられなかったのも助け、変な遠回りをしたような...で、2、3 週した感じで上述引用のエントリーで、「あぁ、これでいいんだ」と。
迎えたオチがオチだけに、ちょいと疲れました。
あ。
ちなみにダブルクォートで囲むの "何故か" は、こちらで述べられてました。
""で囲む事によって比較する型が合わなくなる事を防ぐそうだ。
そういうことなのですね。
背景
コマンドの終了ステータスは、0 正常終了 を返すけど、さらに出力がブランクの場合も捕えたいと思ったのです。
シェルスクリプトに当該の処理を追加する。
test コマンドで -n オプションをかまし、出力された答えの文字列長が 0 より大きい場合のみ処理を先に進めるようにしました。
で、試してみると、何とも、想定した振る舞いをしてくれていない様子。
見てみると、出力結果は確かにブランクなので文字列長は 0。
なので test -n
で弾かれるはずなのに、なぜか違う処理の方に入ってしまう。
ターミナルで、チョロチョロ試してみると...
% var="" % echo "var:$var(${#var})" var:(0) % if [ -n $var ]; then echo "not blank"; else echo "blank"; fi not blank % if [ -z $var ]; then echo "blank"; else echo "not blank"; fi blank
ここで、あれっ?!、思い。
% var="xyz" % echo "var:$var(${#var})" var:xyz(3) % if [ -n $var ]; then echo "not blank"; else echo "blank"; fi not blank % if [ -z $var ]; then echo "blank"; else echo "not blank"; fi not blank
またまた、あれっ?!。
-z
オプションは正しく判定できて、-n
は判定できない?
-z
使えってこと?
え? ブランクを検証するには -z
が推奨とかってあったりすんの?
じゃ、-n
って何ってなのかしら?
もう何が何だか分かんない。
つか、そんな仕様だとしてきもてぃわるすぎるんだけど
きもてぃわるいのがそれならそれでハッキリさせたい、と思い地味に調べて見る事にしました。
がこの始まり。
ちゃんと見ると言及しているとこはある
自分が如何に気にしていないか、ちゃんと読んでいないか、ということだったみたいです。
最初は「なかなかないなぁ」と思っていたのですが。
上述冒頭の引用以外で、
以下に関連すると思った情報、書き留めておきたいと思います。何と言うか、自戒をも込めてw。
まずはこちら、クオートすることを一番最初に意識し始めた記述です。;
testコマンド
:(略
※ 第一引数は "で括ること。
(第一引数が[空|未定義]のとき、エラーになるため)via. シェルスクリプトに関する覚え書き
if [ "$1" = "xxx" ]; then echo $1; fi
変数は二重引用符で囲む。下記の例のように変数を二重引用符で囲まない場合、変数に何らかの値が設定されていれば正常に動作するが、変数に値が設定されていないとエラーとなる。
if [ $1 = "xxx" ]; then echo $1; fi
via. UNIX シェル
if [ "$var1" = 'test' ];
: (略
このとき、変数参照をすべて "-" で囲むのがコツである。これは変数が未定義のために展開されないと、test コマンドに対する引数が不足してしまうからである。注意されたい。
文字列比較演算子。
比較対象の文字列はダブルクォーテーションで囲んでおかないと動作しない、と思われる。
if [ -n "${VAL}" ] ; then
:
:
if [ ${VAL} = "Hello" ] ; then
注意:変数を[ ]の判定式で使用する際には、 " "でくくる必要があるので注意せよ。(変数の中身が空の場合、 " "でくくっていないと、比較演算ができないというエラーが表示 されるため)
via. UNIXコマンド
また、空文字じゃないかどうか調べる際に、「-n 変数」という書式になるわけですが、変数をダブルクォーテーションでくくらないといけません。
-z (空文字かどうか)調べるときも同じです。
といったことで、結構、きちんと言及されていたりする。
自身が知らないだけの話 orz
ちなみにコマンド置換もやれる
今回、これが本当にやりたかったことなのですが、
コマンド置換の標準出力を直接ぶつけることだったりします。
一応、ここにメモさせておいてください。;
bash-3.2$ type cd cd is a shell builtin bash-3.2$ type -p cd bash-3.2$ if [ -n "$(type -p cd)" ]; then echo "not blank"; else echo "blank"; fi blank
コマンドがあれば、そのパスを穫る。
なければ、あるいはパスを穫れなければエラーとする、と言った感じ。*3
変数のブランク判定はパラメータ展開でも
最後に、パラメータ展開 Parameter Expansion 使ってよりスマートに描くことも可能であることを知ったのでメモ。
シェルでパラメータの空文字チェックを行う際、今まではif文を使ってこんな感じでやってました。
:(略echo ${aaaa:?"空文字です"}
こうすると変数aaaaに値が入っていない場合は、標準エラー出力に?以降のメッセージが出力されます。
パラメータ展開については、以前、変数のネストに関するエントリーでも触れましたが、
ここでやっと使うことの意味が分かりました(笑
でも上述したような、コマンド置換はだめみたい? ;
bash-3.2$ echo ${$(type -p cd):?"error"} bash: ${$(type -p cd):?"error"}: bad substitution bash-3.2$ echo ${"$(type -p cd)":?"error"} bash: ${"$(type -p cd)":?"error"}: bad substitution
似たようなので、{aaaa:-"空文字です"}
と、-
ハイフンを使う方法がありますが、
今回の目的ですと、?
を使った ${aaaa:?"空文字です"}
になりますかね。
なぜならば、aaaa
が 空白であればvalueを「標準エラー出力」に出力し、シェルスクリプトの実行を終了するから。
ちなみに −
を使った {aaaa:-"空文字です"}
の方は同様のケースでも、終了ステータスは 0 となる。
なるほど。...でいいのかな?(笑
おしまい。