$1 が 1 でない場合でも、 'if [ $1="1" ]' 分岐が常に選択されるのはなぜですか。

$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" を渡さないため混乱します。

どうなりますか?

答え1

スペースを使用すると問題を解決できます。

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

答え2

[最初に明確なのはtest、または引数の間にスペースを提供する必要があることです[[

if [ "$1" = 1 ];

Bashでは、[[ ]]トークン化、パス名拡張などの条件式に不要な操作を実行しないため、usingを使用することをお勧めします。二重引用符で囲む必要もありません。==より読みやすい演算子を使用することもできます。

if [[ $1 == 1 ]];

*追加された説明:2番目のオペランドにも変数が含まれている場合は、引用符で囲む必要があります。これは、認識された文字(など)が含まれていると?パターンマッチングの影響を受ける可能性があるためです。使用されている場合、他の形式(など)を使用する[]と、拡張されたワイルドカードまたはパターンマッチングが可能になります。パターンとしても認識されます。バラよりshopt -s extglob@()!()パターンマッチング

<同じ演算子の場合、>2番目の引数を引用しないと、他の結果が出るバグが発生したため、まだ必要になることがあります。

最初のオペランドは何も適用されません。

また、次の簡単なバリエーションを検討してください。

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-

答え3

なぜこれが起こるのかという質問に答えるために[testPOSIXで文書化:

次のリストでは、$ 1、$ 2、$ 3、および$ 4はテストするパラメータを示しています。

[...]

パラメータ1つ:

$1 が null でない場合は true(0) を返し、そうでない場合は false を返します。

2=1null以外の1つのパラメータを渡したため、正常に終了しましたtest

他の投稿と同様に(そして住宅検査)は、同等性を比較するために3つの引数とを渡す必要があることを示します2=1

答え4

一つだけおすすめしたい持ち運べるしかし、それはよりクリーンなオプションでもあります。すごいいいえUniversal(ユニバーサルは必要ありませんが、なぜシェルスクリプトを書くのですか?)

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

$action(賢者のための注意:はい、インライン引用符が不要であることをcase "$action" in知っていますが、将来の読者がこれを覚える必要がないように引用文をそこに置く方が良いと思います。)

関連情報