これはOSXで完全に動作します
#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
echo "${chars[i]}"
done
しかし、Ubuntuで実行すると、次のエラーが発生します。
ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected
問題を解決できないようです。助言がありますか?
これはOSXで完全に動作します
#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
echo "${chars[i]}"
done
しかし、Ubuntuで実行すると、次のエラーが発生します。
ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected
問題を解決できないようです。助言がありますか?
回答:
おそらく、スクリプトを次のように実行しています。
sh ForLoopAlphabetTest.sh
Ubuntuでは、sh
にシンボリックリンクされていますdash
ます; dash
配列の概念がありません、あなたは構文エラーを取得しています(
。
スクリプトはで完全に動作するbash
ため、次のように実行すると問題ありませんbash
引数。
bash ForLoopAlphabetTest.sh
これでbash
、スクリプトにシェバンができたので、スクリプトを実行可能にすることができます(chmod u+x ForLoopAlphabetTest.sh
)にして、次のように。
/path/to/ForLoopAlphabetTest.sh
またはスクリプトのディレクトリから:
./ForLoopAlphabetTest.sh
また、スクリプトにはブレース展開{a..z}
とCスタイルのfor
構造が含まれていることに注意してください。for (( ... ))
これらは、dash
; でもサポートされていません。したがって、移植性が目標の場合は、POSIX sh
構文のみを検討する必要があります。
/bin/sh
Unixライクなオペレーティングシステムで実行できる移植可能なスクリプトを作成する場合、配列を使用することはできません。Bash(および他のいくつかのシェル)は非常に便利であり、常により移植性の高いコードに簡単に置き換えることができないため、それらを追加しました。ただし、特にスクリプトの場合は、bash固有の機能を使用せずに問題なく実行できます。その方法に興味がありますか?
sh
スクリプトは、すべてのBourneスタイルのシェルでは提供されないBashシェルの3つの機能を使用します。以下のようheemaylは言う、あなたは単にでそのスクリプトを実行することができますbash
代わりにsh
。あなたのhashbangの一番上(のライン#!/bin/bash
)を指定しbash
ていますが場合にのみ有効であり、実行として、スクリプトをheemaylを説明。スクリプトの名前をに渡すと、が自動的に呼び出されるのsh
でsh
はなくbash
、単にスクリプトが実行されます。これは、スクリプトが実際に実行されると、hashbang行は効果がないためです。
完全にポータブルな記述が必要な場合、他の選択肢Bashの機能に依存しないスクリプトスクリプトを変更して、スクリプトを使用せずに機能するようにすることもできます。使用するBash機能は次のとおりです。
( {a..z} )
割り当てた括弧で囲まれた式chars
は、配列を作成し、${chars[i]}
し、ループに表示されるが、その中にインデックスを付けます。{a..z}
は、に展開されa b c d e f g h i j k l m n o p q r s t u v w x y z
ます。ただし、これはBourneスタイルのシェルの普遍的な(または標準化された)機能ではなく、Dashはサポートしていません。for
ループ構文。それ自体がBashに固有ではない算術展開に基づいていますが(POSIXに準拠していない非常に古いシェルもいくつかありませんが)、Cスタイルのfor
ループは Bash主義であり、他のシェル。Bashは、特にUbuntuのようなGNU / Linuxシステムで広く利用可能です。また、(ご存知のように)macOSや他の多くのシステムでも利用可能です。Bash固有の機能をどれだけ使用しているかを考慮すると、単にスクリプトを実行するときにそれらを使用し、Bash(または使用している機能をサポートする他のシェル)を使用していることを確認するだけです。
ただし、必要に応じて、移植可能な構成要素に置き換えることができます。配列とCスタイルのfor
ループは簡単に交換できます。中括弧を展開せずに(およびスクリプト内でハードコーディングせずに)一連の文字を生成することは、少し注意が必要な部分です。
まず、小文字のラテン文字をすべて印刷するスクリプトを次に示します。
#!/bin/sh
for i in $(seq 97 122); do
printf "\\$(printf %o $i)\n"
done
seq
コマンドは、数値シーケンスを生成します。コマンド置換を$(
)
実行するため、の出力に置き換えられます。これらは、文字コードのための経由。$(seq 97 122)
seq 97 122
a
z
printf
コマンドは、文字コードを文字に変換できます(たとえば、printf '\141'
printsのa
後に改行が続きます)が、コードは8進数でなければならseq
ず、出力は10進数のみでなければなりません。したがってprintf
、2回使用しました。内側はprintf %o $i
10進数(で提供されるseq
)を8 進数に変換し、外側のコマンドに置き換えられprintf
ます。(16進数を使用することも可能ですが、単純ではなく、移植性が低いようです。)printf
\
8進数が続くコードをそのコードの文字\n
として、および改行として解釈します。ただし、シェル\
はエスケープ文字としても使用します。A \
の前に$
防ぐことができます$
から発生する拡張原因(この場合は、コマンド置換を)、私は別でそれを逃れてきたので、私は、それを防ぐためにしたくありません\
。それが理由です\\
。\
前の2つ目n
はエスケープする必要はありません。なぜなら\$
、\n
二重引用符で囲まれた文字列ではシェル特別な意味がないからです。'
)もシェルクォート構文の重要な部分であることに注意してください。私はたまたまそのスクリプトでそれらを使用していません。これは、ほとんどの Unixライクシステムに移植可能であり、使用するBourneスタイルのシェルに依存しません。ただし、いくつかのUnixライクなシステムはseq
デフォルトではインストールされていません(jot
デフォルトではほとんどのGNU / Linuxシステムにインストールされていない代わりに使用する傾向があります)。expr
必要に応じて、ループを使用するか算術置換を使用して、移植性をさらに高めることができます。
#!/bin/sh
i=97
while [ $i -le 122 ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
それは使用while
して-loopを指示するときのみ、ループを継続する範囲です。[
$i
スクリプトはアルファベット全体を印刷するのではなく、変数n
を定義して最初の$n
小文字を印刷します。Bash固有の機能に依存せず、Dashで動作するが、必要なスクリプトのバージョンはseq
次のとおりです。
#!/bin/sh
n=3 start=97
for i in $(seq $start $((start + n - 1))); do
printf "\\$(printf %o $i)\n"
done
の値を調整する n
スクリプトのように印刷される文字数が変わります。
必要のないバージョンはseq
次のとおりです。
#!/bin/sh
n=3 i=97 stop=$((i + n))
while [ $i -lt $stop ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
そこに、印刷されるべき最後の文字の文字コードよりも$stop
1つ高いので、コマンド-lt
では-le
(より小さいか)ではなく(より小さい)を使用します[
。(を作成stop=$((i + n - 1))
して使用することもできたでしょう[ $i -le $stop ]
)。