$ 1が1でなくても、 'if [$ 1 =“ 1”]'ブランチが常に選択されるのはなぜですか?


10

次のような「teleport.sh」という名前のシェルスクリプトがあります。

if [ $1="1" ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ $1="2" ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ $1="3" ];
    then
    shift
        mv "$@" ~/lab/Earth
fi

私が実行すると:

sh teleport.sh 2 testfile

これtestfile~/lab/Sunディレクトリに移動されますが、そのスクリプトに1または '1'を渡さなかったため、非常に混乱しています。

ここで何が問題になっていますか?


1
実験室太陽地球テレポートの場合は+1 。ただし、展開は常に二重引用符で囲む必要があります(、、さらに[ 優先する必要があります])。あなたがいない、いくつかのエッジケースがあります持って引用するが、常にそれは傷つけることはありませんやっては。$var$(cmd)`cmd`$(cmd)
nyuszika7h 2014

@ nyuszika7h、二重引用符は「$ var」と「$ cmd」を意味するべきではありませんか?上記の丸括弧の利点は何ですか?
Zen

$(cmd)あるコマンド置換(主に)、と同じ`cmd`mywiki.wooledge.org/CommandSubstitutionおよびmywiki.wooledge.org/BashFAQ/082を
nyuszika7h

回答:


19

スペースを使用すると問題が解決します。

if [ "$1" = 1 ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ "$1" = 2 ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ "$1" = 3 ];
    then
    shift
        mv "$@" ~/lab/Earth
fi

これはきれいですが:

#!/bin/bash

action=$1
shift
files=("$@")
case $action in  
  1) mv -- "${files[@]}" ~/lab/Sun     ;;
  2) mv -- "${files[@]}" ~/lab/Moon    ;;
  3) mv -- "${files[@]}" ~/lab/Earth   ;;
esac

3
はい、スペースは必要ですが、なぜ元の$1="1"構文がまったく「機能する」のか疑問に思っています(真の結果が生成されます)。[/ testビルトインは実際にその式をどのように解釈しますか?
echristopherson、2014

5
@echristophersonスペースtestがない場合、1つの引数(「l値」)のみが表示されます。他の引数(「演算子」と「r値」)がなければ、テストするものは何もないのでtest、「わかりました、ええ、あなたは私に何かを与えたので、それは空虚に真実でなければなりません」とだけ言います。
ビショップ

2
$1="1"される$1ことにより、連結=1
jdh8

ケースを使用する場合、どの条件も満たされないときに実行するアクションを設定するにはどうすればよいですか?

11

最初の明らかなことは[testまたはの引数の間にスペースを入れるべきです[[

if [ "$1" = 1 ];

Bashでは、[[ ]]単語の分割やパス名の展開などの条件式に不要なことをしないため、使用することをお勧めします。二重引用符を引用する必要もありません。より読みやすい演算子==も使用できます。

if [[ $1 == 1 ]];

メモを追加した:第2オペランドはまた、変数を含む場合、引用それはのような認識可能な文字が含まれている場合、それは、パターンマッチングの対象とすることができるように必要であり*?[]、など。場合はグロブ延長またはパターンマッチングを用いて有効になっているshopt -s extglobような、他の形態@()!()などパターンとしても認識されます。パターンマッチングを参照してください。

のような演算子を使用する<>、2番目の引数を引用しないと異なる結果が発生するバグにかつて遭遇したことがあるので、まだ必要な場合があります。

第1オペランドについては、何も適用されません。

この単純なバリエーションも検討してください。

case "$1" in
1)
    mv -- "${@:2}" ~/lab/Sun
    ;;
2)
    mv -- "${@:2}" ~/lab/Moon
    ;;
3)
    mv -- "${@:2}" ~/lab/Earth
    ;;
esac

または凝縮:

case "$1" in
1) mv -- "${@:2}" ~/lab/Sun ;;
2) mv -- "${@:2}" ~/lab/Moon ;;
3) mv -- "${@:2}" ~/lab/Earth ;;
esac

"${@:2}"2オフセットである部分文字列展開または配列メンバー展開の形式です。これにより、2番目の値から拡張が開始されます。これにより、使用する必要がない場合がありますshift

追加された--機能によりmv、ダッシュ(-)で始まるファイル名が無効なオプションとして認識されなくなります。


あなたはそれぞれの場合に壊れるべきではありませんか?
Archemar 2014

2
@Archemar:いいえ、他の多くの言語とは異なり、フォールスルーはありません。
マット2014

2
@Mat、;&代わりにを使用するとフォールスルーが発生します;;(ksh、bash、zshのみ)。しかし、breakそれでもフォールスルーを防ぐわけではなくbreak、ループから抜け出すだけです。
ステファンChazelas

それif[コマンドの引数ではありません。
ステファンChazelas

1
修正:二重引用符は左側にのみ必要です! [[ $foo == $bar ]]パターンマッチングを実行しますが、実行し[[ $foo == "$bar" ]]ません。
nyuszika7h 2014

7

なぜこれが起こっているの質問に答えるために、この振る舞い[通称testされるPOSIXで文書化

次のリストで、$ 1、$ 2、$ 3、および$ 4は、テストのために提示された引数を表します。

[...]

1つの引数:

$ 1がnullでない場合はtrue(0)を終了します。それ以外の場合は、falseを終了します。

2=1nullではない1つの引数を渡しているためtest、成功して終了します。

他の投稿(およびshellcheck)が指摘しているように、等しいかどうかを比較したい場合は、代わりに3つの引数2=およびを渡す必要があり1ます。


3
ある意味では、これは(OPが達成したかったことを実行する1つ以上のレシピを提供するのではなく)質問に回答した唯一の回答であり、シェルは十分に神秘的であり、なぜそれが役立つを知っています。 。
dmckee ---元モデレーターの子猫2014

1

私はポータブルでありながら、よりすっきりした代替品をお勧めします。Bashはユニバーサルではありません(ユニバーサルが必要ない場合、なぜシェルスクリプトを作成するのですか?)

#! /bin/sh
action="$1"
shift
case "$action" in
    1) dest=Sun   ;;
    2) dest=Moon  ;;
    3) dest=Earth ;;
    *) echo "Unrecognized action code '$action' (must be 1, 2, or 3)" >&2; exit 1 ;;
esac
mv -- "$@" ~/lab/"$dest"

(pedantsへの注意:はい、私は前後に引用符を知っている$action上のcase "$action" in不要なラインが、私はそれが将来の読者はそれを覚えておく必要はありませんので、とにかくそこにそれらを置くことが最善であると感じています。)


1
「bashは普遍的ではありません(そして、普遍的である必要がないのに、なぜシェルスクリプトを書いているのですか?」)—これは、bashスクリプトにユースケースがないことを意味しているようです。
ルスラン

@Ruslanはい、それは私の考えた意見です。/bin/sh必要に応じて、移植可能なスクリプトを作成します。それ以外の場合は、シェルほどひどくないスクリプト言語で記述します。基本的なPerlインタプリタは、実際には多くの可能性が高いレガシー独自の環境やカットダウン組み込み環境中に存在するとbashがあるより。
zwol 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.