web-dev-qa-db-ja.com

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

次のような「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'を渡さなかったため、非常に混乱しています。

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

10
Zen

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

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
19
Karlo

最初の明らかなことは、_[_、testまたは_[[_の引数の間にスペースを入れる必要があることです。

_if [ "$1" = 1 ];
_

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

_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がダッシュ(_-_)で始まるファイル名を無効なオプションとして認識しないようにします。

11
konsolebox

なぜこれが起こっているのかという質問に答えるために、[ aka testのこの動作は POSIXに文書化されています

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

[...]

1つの引数:

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

Nullでない1つの引数2=1を渡しているため、testは正常に終了します。

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

7
that other guy

私はportableをお勧めしたいのですが、もっとすてきな代替手段もあります。 Bashはnotユニバーサルです(そして、ユニバーサルが必要ない場合は、なぜシェルスクリプトを作成しているのですか?)

#! /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"

(注意事項:はい、私は$actioncase "$action" in行は不要ですが、とにかくそこに置くのがベストだと思います。将来の読者が覚えておく必要がないようにするためです。)

1
zwol