Dashスクリプト入門:Bashとの違いと互換性のための書き方
「シェルスクリプトを書いてみたけど、自分のPCでは動くのにサーバーだと動かない…」そんな経験はありませんか?もしかしたら、それはDashとBashの違いが原因かもしれません。
この記事では、多くのLinuxシステム(特にDebianやUbuntu)で標準シェルとして使われている/bin/shの実体、Dashの基本的な書き方を解説します。初心者の方でもコピペするだけで「動く!」を体験できるよう、具体的なコード例を豊富に用意しました。Bashとの互換性の注意点もまとめているので、より堅牢でポータブルなスクリプトを書くスキルが身につきます。
そもそもDashって何?Bashとどう違うの?
普段、私たちが何気なく「シェルスクリプト」と呼んでいるものは、多くの場合Bash (Bourne Again SHell) を指しています。Bashは非常に高機能で、多くの便利な記法が使えます。
一方でDash (Debian Almquist SHell) は、POSIX標準に準拠した、よりシンプルで軽量なシェルです。動作が非常に高速なため、DebianやUbuntuなどのOSでは、システムの起動スクリプトや標準のシェル(/bin/sh)として採用されています。
つまり、#!/bin/sh と書いたスクリプトは、あなたのPC(Bash環境)では動いても、サーバー(Dash環境)ではエラーになる可能性があるのです。これを防ぐには、Dashでも解釈できるPOSIX準拠の書き方を覚えるのが一番の近道です。
Dashスクリプトの基本的な書き方【コピペOK】
まずは、Dashの基本的な文法に触れてみましょう。どのコードも#!/bin/shで始まるので、そのままファイルに保存して実行できます。
1. Hello World! - 文字列の出力
お約束の「Hello World」です。文字列の出力にはechoコマンドを使います。これはBashと全く同じです。
#!/bin/sh
# スクリプトの先頭には、どのシェルで実行するかを指定する「シェバン」を書きます
echo "Hello, Dash World!"
2. 変数の使い方
変数を定義するときは=の左右にスペースを入れないのがルールです。利用するときは変数名の前に$をつけます。これもBashと同じですね。
#!/bin/sh
GREETING="こんにちは"
TARGET="世界"
echo "${GREETING}, ${TARGET}!" # 変数名を明確にするため{}で囲むのがおすすめです
3. if文による条件分岐
条件分岐にはif文を使います。条件式は[ ... ]で囲みます。ここが最初の注意点で、Bashでよく見る[[ ... ]]はDashでは使えません(後ほど詳しく解説します)。
#!/bin/sh
USER_NAME="Alice"
# [ ] の前後には必ずスペースが必要です
if [ "$USER_NAME" = "Alice" ]; then
echo "ようこそ、Aliceさん。"
else
echo "あなたは誰ですか?"
fi
4. forループによる繰り返し
スペースで区切られたリストを使って、シンプルなforループを書いてみましょう。
#!/bin/sh
# スペース区切りの文字列をループで処理
for fruit in apple banana cherry
do
echo "I like ${fruit}."
done
【最重要】Bashとの互換性で気をつけるべき点
ここからが本題です。Bashでは当たり前に使える便利な機能も、Dashではエラーになることがあります。代表的な例を見ていきましょう。
違い1:配列 (Array)
Dashは配列をサポートしていません。 これは非常に大きな違いです。
❌ Bashの書き方(Dashでは動かない)
#!/bin/sh
# このコードはDashではエラーになります!
fruits=("apple" "banana" "cherry")
echo "最初の果物は ${fruits[0]} です。"
⭕ Dashでの代替策
スペース区切りの文字列や、引数リスト ($@) を使って同じような処理を実現します。
#!/bin/sh
# POSIX準拠のループ処理
fruit_list="apple banana cherry"
for fruit in $fruit_list
do
echo "処理中の果物: $fruit"
done
違い2:条件式 [[ ... ]] vs [ ... ]
Dashは[[ ... ]]を解釈できません。 条件式は必ず[ ... ](testコマンドの糖衣構文)を使いましょう。
❌ Bashの書き方(Dashでは動かない)[[は、文字列のマッチングや論理演算子(&&, ||)が使えて便利ですが、Bash独自の拡張機能です。
#!/bin/sh
# このコードはDashではエラーになります!
COUNT=10
if [[ "$USER" = "root" && $COUNT -gt 5 ]]; then
echo "条件に一致しました。"
fi
⭕ Dashでの書き方 (POSIX準拠)[ ... ]を使い、論理積は-a、論理和は-oで表現します。
#!/bin/sh
# 互換性を意識した書き方
COUNT=10
# 複数の条件を組み合わせる場合は -a (AND) や -o (OR) を使います
if [ "$USER" = "root" -a $COUNT -gt 5 ]; then
echo "条件に一致しました。"
fi
違い3:関数宣言 function キーワード
Dashではfunctionキーワードは使えません。 関数は 関数名() { ... } の形式で宣言します。
❌ Bashの書き方(Dashでは動かない)
#!/bin/sh
# このコードはDashではエラーになります!
function say_hello() {
echo "Hello from function!"
}
say_hello
⭕ Dashでの書き方 (POSIX準拠)
こちらが古くからある標準的な書き方で、もちろんBashでも動作します。
#!/bin/sh
# POSIX準拠の関数宣言
say_hello() {
echo "こんにちは!関数から呼び出されました。"
}
# 関数の呼び出し
say_hello
違い4:プロセス置換 <()
コマンドの実行結果をファイルのように扱えるプロセス置換<()は、Bashの機能です。
❌ Bashの書き方(Dashでは動かない)
#!/bin/sh
# このコードはDashではエラーになります!
# commコマンドは2つのソート済みファイルを比較する
comm <(ls -1 dir1) <(ls -1 dir2)
⭕ Dashでの代替策
一度テンポラリファイルに保存するか、パイプラインをうまく使って処理します。
#!/bin/sh
# 一時ファイルを使う方法
TMP1="/tmp/dir1_list"
TMP2="/tmp/dir2_list"
ls -1 dir1 > "$TMP1"
ls -1 dir2 > "$TMP2"
comm "$TMP1" "$TMP2"
# 後片付けを忘れずに
rm "$TMP1" "$TMP2"
違い5:ブレース展開 {1..10}
連続した数値や文字列を生成するブレース展開はBashの機能です。
❌ Bashの書き方(Dashでは動かない)
#!/bin/sh
# このコードはDashでは {1..5} という文字列をそのまま出力します
echo "ファイル{1..5}.txtを作成します"
touch file_{1..5}.txt
⭕ Dashでの代替策
連番の生成には、古くからあるseqコマンドを使いましょう。
#!/bin/sh
# seqコマンドとforループを組み合わせる
echo "1から5までの連番ファイルを作成します"
for i in $(seq 1 5)
do
touch "file_${i}.txt"
echo "${i}番目のファイルを作成しました。"
done
応用例:Dashで書く実用的なスクリプト
最後に、これまでの知識を活かして、少し実用的なスクリプトを書いてみましょう。カレントディレクトリにある.logファイルを、日付付きのバックアップディレクトリにまとめて移動させるスクリプトです。もちろんDash互換です。
#!/bin/sh
# 今日の日付を YYYY-MM-DD 形式で取得
# dateコマンドのフォーマット指定はPOSIXで標準化されているので安全
BACKUP_DIR="backup_$(date +%Y-%m-%d)"
# バックアップディレクトリがなければ作成
if [ ! -d "$BACKUP_DIR" ]; then
echo "バックアップディレクトリ ${BACKUP_DIR} を作成します。"
mkdir "$BACKUP_DIR"
fi
# .log ファイルをループで処理
# *.log のワイルドカード展開はシェルが持つ標準機能
for file in *.log
do
# ファイルが存在し、かつ通常のファイルであるかを確認
if [ -f "$file" ]; then
echo "${file} を ${BACKUP_DIR}/ へ移動します。"
mv "$file" "$BACKUP_DIR/"
fi
done
echo "バックアップが完了しました。"
まとめ
DashとBashの違いを意識し、POSIX準拠の書き方を心がけることで、スクリプトの互換性は劇的に向上します。最初は少し不便に感じるかもしれませんが、この一手間が、環境の違いによる予期せぬエラーを防ぎ、あなたを「できるプログラマー」に一歩近づけてくれます。
今回紹介した内容は基本的なものですが、非常に重要です。ぜひ、お手元の環境で#!/bin/shと書いたスクリプトを動かしてみてください。そして、そのスクリプトがどこでも動く安心感を体験してください!