web-dev-qa-db-ja.com

Windows用UbuntuでシェルスクリプトのIf / Elseがエラーをスローする

次に、簡単なシェルスクリプトの例を示します。

#!/usr/bin/env bash
if [ 1 == 1 ]; then
echo "Something"
fi

これを実行すると

sh ./test.sh

私は得る:

./test.sh: 4: ./test.sh: Syntax error: "fi" unexpected (expecting "then")

私がそこにいる「その後」を認識していないようです。理由についての手がかりはありますか?これは、一部が重要な場合のWindows用Ubuntuです。

一般にシェルスクリプトを実行することは問題ではなく、if/elseに苦労しているようです。

2
Matt Coady

ここには2つの問題があります。まず、 シェバンライン を呼び出してbashを使用している間に、スクリプトをsh script.shとして実行しています。 Shebang行はすでにこのスクリプトのインタープリターを指定しています。したがって、スクリプトを実行可能にするだけです。

chmod a+x ./test.sh

そして、それを実行します:

./test.sh

または、bashで明示的に実行します。

bash ./test.sh

これにより、bashの代わりにshで実行されます。これは、より限定的なシェルであり、==を理解しません。これが2番目の問題と、なぜ失敗するのかを示しています。奇妙なことに、=ではなく、eqまたは==が必要です。 POSIXシェルスクリプト(shはPOSIX準拠のシェル)で数値の等価性をテストする演算子は-eqであり、stringの等価性をテストする演算子は=および==はnもの。

したがって、スクリプトをshで実行する場合は、次のように変更します。

if [ 1 = 1 ]; then
    echo "Something"
fi

string1string1と同じであることをチェックするか、数値的に比較するには、次を使用します:

if [ 1 -eq 1 ]; then
    echo "Something"
fi

さらに、別の問題があります。 dashの後に\rを追加した場合、thenで表示される特定のエラーのみを再現できます。これは、WindowsとLinuxの間を移動する際の古典的な問題です。 Linuxは\nを行末文字として使用しますが、Windowsはその行を\r\nで終了します。この\r文字はユーザーには見えませんが、そこにあり、スクリプトを混乱させます。したがって、次の方法で削除できます。

sed -i 's/\r//' ./test.sh

将来的には、適切なテキストエディターを使用してスクリプトを記述します。 WSLシステムで動作するもの、または少なくとも行末に\rを追加しないように構成できるもの。

6
terdon

シンプルにしましょう:

  • shはUbuntuのbashではありません(リファレンスについては this を参照してください)。

  • [testコマンドと同じコマンドです。算術比較では==をサポートしていません。代わりに-eqを使用してください。これは POSIX標準 で指定されたユーティリティの1つであるため、bashdashの両方で機能します。

    if [ 1 -eq 1 ];
    then
        echo "it works!"
    fi
    
  • bashには、算術展開と呼ばれる((があります。これは==をサポートします。この機能はksh Shellから借用されているようです。

    $ var=25 bash -c 'if((var==5)); then echo Y;else echo "N";fi'      
    N
    $ var=25 bash -c 'if((var==25)); then echo Y;else echo "N";fi' 
    Y
    
  • dashif (())をサポートしていませんが、いずれのシェルでも$((は、 POSIX標準 で指定されているため機能します。両方のシェルに共通の機能のみを使用して、比較に==を使用しながら、次のようなことができます。

    
    $ var=25 dash -c 'if [ $((var==25)) -eq 1 ];then echo Y; else echo N;fi'
    Y
    $ var=25 dash -c 'if [ $((var==5)) -eq 1 ];then echo Y; else echo N;fi' 
    N
    
  • ifステートメントをすべて捨てて、代わりにcaseを使用できます(戻り状態はシェルの動作ではなくCに準拠していることに注意してください。つまり、trueが1でfalseが0です。 :

    $ dash -c 'case $((1==1)) in 1) echo "equal";; 0) echo "not equal";;esac'                                                                                                
    equal
    $ dash -c 'case $((1==2)) in 1) echo "equal";; 0) echo "not equal";;esac'                                                                                                
    not equal
    
  • または、(ab)算術展開を使用してこっそりすることができます:

    $ var=25 dash -c '_0(){ false;}; _1(){ true;}; if "_$((var==25))" ; then echo Y; else echo N; fi'
    
2