`var=''; test -n $var` はどう評価される? - 拡張 POSIX シェルスクリプト Advent Calendar 2013 - ダメ出し Blog

2013-12-16(Mon) [sh][shell] [更新履歴]

拡張 POSIX シェルスクリプト Advent Calendar 2013、16日目の記事です。

ネタ枯渇と締切の危機の中、@hirose31 さん からネタを提供していただきました。 ありがとうございます! ありがとうございます! ありがとうございます! ありがとうございます! ありがとうございます! ありがとうございます! ありがとうございます! (コピペ)

ネタは一応まだ残っているのですが、いかんせん書くのに時間がかかりそうなものばかり。 そこで今日は頂いたネタでお茶を濁したいと思います。 test コマンドにはいくつか罠がありますが、 その一つの紹介です。

$var が空文字列のとき [ -n $var ] はどのように評価されるか?

https://twitter.com/hirose31/status/412528403787415552

@satoh_fumiyasu 小ネタ思いつきました。 testで-nじゃなくて-zを使うべき→V=””; [ -n $V ] && echo T || echo F がTになる理由。ってクォートしろ、[[使えって話でもあるんですけどw

シェル好きには堪らない釣り針ですね! こんなの簡単!!

  1. [ コマンドの -n オプションは文字列を引数に取り、 文字列の長さが 0 でなければ真となる。
  2. シェルは [ -n $V ] を変数展開して [ -n ] にする。
  3. シェルは [ コマンドに引数 -n だけを指定して実行する。
  4. [ コマンドは引数が足りないのでエラー報告する。

確認しましょう。

$ V=""; [ -n $V ] && echo T || echo F
T

あれ?! エラーにならないどころか、結果は真?! 俺の知らないシェル依存の何かがあるのか?

$ /bin/dash -c 'V=""; [ -n $V ] && echo T || echo F'
T
$ /bin/bash -c 'V=""; [ -n $V ] && echo T || echo F'
T
$ /bin/ksh -c 'V=""; [ -n $V ] && echo T || echo F'
T
$ /bin/mksh -c 'V=""; [ -n $V ] && echo T || echo F'
T
$ /bin/zsh -c 'V=""; [ -n $V ] && echo T || echo F'
T
$ V=""; /usr/bin/test -n $V && echo T || echo F
T

\(^o^)/ワカラン

【長考中】( ´・ω) (´・ω・) (・ω・`) (ω・` )【30秒経過】

\(^o^)/ワカタ

$ V=""; [ -z ] && echo T || echo F
T
$ V=""; [ -f ] && echo T || echo F
T
$ V=""; [ -o ] && echo T || echo F
T
$ V=""; [ -urrryyyy ] && echo T || echo F
T
$ V=""; [ -ほげ ] && echo T || echo F
T

わかりますか?

答はそう、[ は引数が一つしかないとき、最初の引数に依らず [ 文字列 ] と評価するためです。[ 文字列 ][ -n 文字列 ] と等価です。 [ -n -n ] であるため真になるということですね。

[[ はどうか?

昨日紹介したように [[ 〜 ]] ではワード分割は発生しない ため、[[ -n $V ]] はそのまま「[[ コマンドに引数 -n, $V (の変数展開結果), ]] を指定して実行」 となり、 「$V の長さが 0 でなければ真 (0 か未定義なら偽)」という期待通りの動作になります。

また、[[ -n ]] のように -n オプションの引数を指定しない場合は bash: unexpected argument ']]' to conditional unary operator のようにこれも期待通りエラーとなります。

また、[[ コマンド [ とは違い、[[ 文字列 ]] という構文はない(不正でエラーとなる)であり、 [[ -n 文字列 ]] を使わなくてはならない仕様です。 (いくつかの OS の各種シェルのマニュアルを確認しましたが、 この仕様が明示されている記述は見つけられませんでした) (@t_tetosuki さんのご指摘により、誤りとわかったため削除)

環境やシェル種別、バージョンの依存度は?

手元にある環境で調べてみたところ、以下の test コマンドは V=""; [ -n $V ] がエラーになることを確認しました。

エラーになるかエラーにならず真になるかは環境依存のようですね。

まとめ

不明確で非直感的な動き・仕様の [, test は使ってはいけません。 明確で直感的な動きをする [[ を使いましょう。


ところで、12月25日はクリスマスな上に、 OSS 界隈で地味に活躍されているふみやすさんの誕生日ですね!
http://www.amazon.co.jp/registry/wishlist/27M7TV8CEEF6G?sort=priority

逆に、あなたの書いた OSS や Blog や Advent Calendar が気に入ったら何か送りたく なってしまうかもしれないので、プロフィールや Web サイトに あなたの Amazon 欲しいものリストの URL を貼っておいてくださいね!


私が勤める OSSTech っていう某弊社で社員募集しているようです。 人材紹介会社を介さなければ、入社後に 20万円のボーナス! 「ふみやすっていう人に紹介された」と言ってもらえると私にもボーナス!! → https://www.osstech.co.jp/recruit/


よろしければ、これまで参加した/参加予定のほかの Advent Calendar もどうぞ。