`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万円のボーナス! 「ふみやすっていう人に紹介された」と言ってもらえると私にもボーナス!! → http://www.osstech.co.jp/company/recruit

バツイチでアラフォーでほとんど諦めていますが、新たなパートナーも募集中です。 ちゃんと愛情表現してくれて精神的に自立している女性がいいです。お友達から始めましょう。 → https://twitter.com/satoh_fumiyasu


よろしければ、過去の Advent Calendar もどうぞ。