0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Bash入門】シェルスクリプト初心者が最初に覚えたい基本構文15選

0
Posted at

はじめに

シェルスクリプトを学習していると、次のようなコードを見かけます。

#!/usr/bin/env bash

set -euo pipefail

while [[ $# -gt 0 ]]; do
  case "$1" in
    --help)
      echo "ヘルプを表示します"
      shift
      ;;
    *)
      echo "不明な引数: $1"
      exit 1
      ;;
  esac
done

初めて見ると、$#、$1、shift、esac など、何をしているのか分からない構文が多いと思います。

しかし、一つずつ理解していけば、それほど複雑な処理ではありません。

この記事では、シェルスクリプトを読む・書くために最初に覚えたいBashの基本構文を、サンプルコードと実行結果を交えて解説します。

※この記事はBashを対象としています。POSIX shや他のシェルでは動作が異なる場合があります。


1. シェルスクリプトとは

シェルスクリプトとは、シェルで実行するコマンドをファイルにまとめたものです。

例えば、Linuxで次のコマンドを実行するとします。

pwd
ls
echo "処理が完了しました"

これらを毎回手動で入力する代わりに、sample.sh というファイルにまとめます。

#!/usr/bin/env bash

pwd
ls
echo "処理が完了しました"

次のコマンドで実行できます。

bash sample.sh

これにより、複数のコマンドをまとめて実行できます。

シェルスクリプトは、ファイル操作、バックアップ、アプリケーションの起動、AWSなどのクラウド環境の操作を自動化するときにも使用されます。


2. #!/usr/bin/env bash とは

シェルスクリプトの先頭によく書かれているのが、次の記述です。

#!/usr/bin/env bash

これは Shebang(シバン) と呼ばれます。

スクリプトを直接実行するときに、どのインタープリターを使用するか指定するものです。

/usr/bin/env bash は、env を通じて PATH から bash を探して起動します。

例えば、次のスクリプトを作成します。

#!/usr/bin/env bash

echo "Hello Bash"

実行権限を付与します。

chmod +x sample.sh

直接実行します。

./sample.sh

実行結果:

Hello Bash

なお、bash sample.sh と実行した場合は、明示的にBashを指定しているため、Shebangはインタープリターの選択には使われません。


3. 変数

Bashでは、変数を使用して値を保持できます。

NAME="Taro"

echo "$NAME"

実行結果:

Taro

変数を定義するときは、= の前後にスペースを入れないようにします。

# 正しい
NAME="Taro"

# 間違い
NAME = "Taro"

変数を参照するときは $ を使用します。

また、次のように波括弧を使用することもできます。

NAME="Taro"

echo "${NAME}"

実行結果:

Taro

${NAME} は変数名の範囲を明確にしたい場合にも便利です。

NAME="Taro"

echo "${NAME}_backup"

実行結果:

Taro_backup

変数を参照するときは、意図しない単語分割やファイル名展開を避けるため、基本的に "$NAME" のようにダブルクォートで囲むことをおすすめします。


4. $1 / $2:位置パラメータ

Bashでは、スクリプトに渡された引数を $1、$2、$3 などで取得できます。

例えば、次のスクリプトを作成します。

#!/usr/bin/env bash

echo "1番目: $1"
echo "2番目: $2"
echo "3番目: $3"

実行:

bash sample.sh apple banana orange

実行結果:

1番目: apple
2番目: banana
3番目: orange

それぞれの対応は次のとおりです。

記述 取得する値
$1 apple
$2 banana
$3 orange

これらを位置パラメータと呼びます。

なお、$0 は通常、スクリプトの起動に使用された名前やパスを表します。

10番目以降の引数を取得する場合は、${10}、${11} のように記述します。


5. $#:引数の個数

$# は、渡された引数の個数を取得します。

#!/usr/bin/env bash

echo "引数の個数: $#"

実行:

bash sample.sh apple banana orange

実行結果:

引数の個数: 3

引数が3つあるため、3 が表示されます。

引数が存在するかどうかの判定にも使用できます。

if [[ $# -eq 0 ]]; then
  echo "引数がありません"
fi

ここで使用している -eq は、数値が等しいかを比較する演算子です。


6. $@:すべての引数

$@ は、渡されたすべての位置パラメータを表します。

#!/usr/bin/env bash

echo "$@"

実行:

bash sample.sh apple banana orange

実行結果:

apple banana orange

特に重要なのが "$@" という書き方です。

ダブルクォートで囲まれた "$@" は、各引数を独立した要素として保持します。

例えば、次のコードを見てみましょう。

#!/usr/bin/env bash

for arg in "$@"; do
  echo "引数: $arg"
done

実行:

bash sample.sh apple "red banana" orange

実行結果:

引数: apple
引数: red banana
引数: orange

"red banana" は、スペースを含んでいても1つの引数として扱われます。

引数を順番に処理する場合は、基本的に "$@" を使用すると安全です。


7. shift:引数を左にずらす

shift は、位置パラメータを左にずらすためのBashの組み込みコマンドです。

例えば、次のように引数を渡したとします。

bash sample.sh apple banana orange

最初の状態:

$1 = apple
$2 = banana
$3 = orange
$# = 3

ここで shift を実行すると、次のようになります。

$1 = banana
$2 = orange
$# = 2

最初の引数 apple が位置パラメータから取り除かれ、残りの引数が1つずつ左に移動します。

実際に確認してみましょう。

#!/usr/bin/env bash

echo "shift前: $1"

shift

echo "shift後: $1"

実行:

bash sample.sh apple banana orange

実行結果:

shift前: apple
shift後: banana

また、shift 2 と記述すると2つ分ずらせます。

shift は、複数の引数を順番に処理するときによく使用されます。


8. ${VAR:-default}:デフォルト値

Bashでは、変数が未設定、または空文字列の場合にデフォルト値を使用できます。

NAME="${1:-Guest}"

echo "$NAME"

引数なしで実行:

bash sample.sh

実行結果:

Guest

引数ありで実行:

bash sample.sh Taro

実行結果:

Taro

${1:-Guest} は、次のような意味です。

  • $1 に空でない値がある場合は、その値を使用する
  • $1 が未設定または空文字列の場合は、Guest を使用する

なお、${VAR:-default} は変数そのものにデフォルト値を代入するわけではありません。

値を代入したい場合は、別の構文である ${VAR:=default} を使用できます。

また、${VAR-default} のようにコロンを省略すると、未設定の場合だけデフォルト値が使用され、空文字列はそのまま扱われます。


9. if / case:条件分岐

if文

if は、条件に応じて処理を分岐させるための構文です。

#!/usr/bin/env bash

AGE=20

if [[ $AGE -ge 18 ]]; then
  echo "18歳以上です"
else
  echo "18歳未満です"
fi

実行結果:

18歳以上です

ここで使用している -ge は、左側の数値が右側以上であるかを比較する演算子です。

よく使用する数値比較演算子は次のとおりです。

演算子 意味
-eq 等しい
-ne 等しくない
-gt より大きい
-ge 以上
-lt より小さい
-le 以下

case文

case は、値に応じて複数の処理を分岐させる構文です。

#!/usr/bin/env bash

case "${1:-}" in
  start)
    echo "開始します"
    ;;
  stop)
    echo "停止します"
    ;;
  *)
    echo "不明なコマンドです"
    ;;
esac

実行:

bash sample.sh start

実行結果:

開始します

case では、値がどのパターンに一致するかによって処理が決まります。

*) は、それまでのどのパターンにも一致しなかった場合に実行されます。

また、;; は、その分岐の処理を終了するための記述です。

最後の esac は、case 文の終わりを表します。


10. while:繰り返し処理

while は、条件が成立している間、処理を繰り返す構文です。

#!/usr/bin/env bash

COUNT=1

while [[ $COUNT -le 3 ]]; do
  echo "$COUNT"
  COUNT=$((COUNT + 1))
done

実行結果:

1
2
3

$((COUNT + 1)) は、Bashの算術展開です。

変数 COUNT に1を加算しています。

whileとshiftを組み合わせる

次のようなコードは、シェルスクリプトでよく使用されます。

#!/usr/bin/env bash

while [[ $# -gt 0 ]]; do
  echo "現在の引数: $1"
  shift
done

実行:

bash sample.sh apple banana orange

実行結果:

現在の引数: apple
現在の引数: banana
現在の引数: orange

while [[ $# -gt 0 ]] は、引数の個数が0より大きい間、処理を繰り返すという意味です。

shift によって引数を1つずつ取り除き、最後に引数が0個になるとループが終了します。


11. 関数

Bashでは、複数の処理を関数としてまとめることができます。

#!/usr/bin/env bash

greet() {
  echo "Hello $1"
}

greet "Taro"
greet "Hanako"

実行結果:

Hello Taro
Hello Hanako

greet() が関数の定義です。

greet "Taro" のように記述すると、関数を呼び出せます。

関数の内部では、$1 は関数に渡された1番目の引数を表します。

なお、Bashの関数では return によって終了ステータスを返します。一般的な文字列などのデータを返したい場合は、echo などの標準出力を利用する方法があります。


12. set -euo pipefail:エラーを検出しやすくする

シェルスクリプトの先頭では、次の記述を見かけることがあります。

set -euo pipefail

これは、スクリプトのエラーを検出しやすくするための設定です。

それぞれ分解して説明します。

set -e

コマンドが失敗した場合に、一定の条件下でスクリプトを終了します。

#!/usr/bin/env bash

set -e

false

echo "この行は実行されません"

false は終了ステータス1を返すコマンドです。

この例では、false が失敗するため、後続の echo は実行されません。

ただし、if の条件判定や &&、|| など、一部の文脈では -e による終了が適用されません。

set -u

未定義の変数を参照した場合に、エラーとして扱います。

#!/usr/bin/env bash

set -u

echo "$NAME"

NAME が定義されていなければ、エラーになります。

未定義の変数にデフォルト値を設定したい場合は、${NAME:-Guest} のように記述できます。

set -o pipefail

パイプラインの途中でコマンドが失敗した場合にも、失敗を検出しやすくします。

#!/usr/bin/env bash

set -o pipefail

false | cat

echo "終了ステータス: $?"

実行結果:

終了ステータス: 1

通常、パイプラインの終了ステータスは最後のコマンドによって決まります。

pipefail を有効にすると、失敗したコマンドが存在する場合、最も右側にある失敗コマンドの終了ステータスがパイプライン全体の終了ステータスになります。

なお、set -euo pipefail を指定しただけですべてのエラーを防げるわけではありません。

想定される失敗については、if などを使用して明示的に処理することも重要です。


13. BASH_SOURCE[0] と dirname

シェルスクリプトでは、スクリプト自身が配置されているディレクトリを取得したい場合があります。

例えば、次のようなディレクトリ構成を考えます。

project/
├── scripts/
│   └── sample.sh
└── config/
    └── app.conf

sample.sh から config/app.conf を参照したい場合、スクリプトの場所を基準にパスを作ると便利です。

BASH_SOURCE[0]

BASH_SOURCE は、Bashが提供する配列変数です。

通常、スクリプトのトップレベルでは ${BASH_SOURCE[0]} に、そのスクリプトのパスが格納されています。

echo "${BASH_SOURCE[0]}"

例えば、次のように表示されます。

./scripts/sample.sh

※実際の表示は起動方法によって異なります。

dirname

dirname は、指定されたパスのディレクトリ部分を取得するコマンドです。

dirname "/home/user/project/scripts/sample.sh"

実行結果:

/home/user/project/scripts

組み合わせて使用する

次のコードを見てみましょう。

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

echo "$SCRIPT_DIR"

この処理は、次のような流れです。

  1. ${BASH_SOURCE[0]} でスクリプトのパスを取得する
  2. dirname でディレクトリ部分を取得する
  3. cd でそのディレクトリに移動する
  4. pwd で絶対パスを取得する
  5. $(...) でコマンドの出力を取得し、SCRIPT_DIR に代入する

例えば、次のような結果になります。

/home/user/project/scripts

さらに、プロジェクトのルートディレクトリを取得したい場合は、次のように記述できます。

PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

.. は親ディレクトリを表します。

これにより、スクリプトをどの作業ディレクトリから実行しても、スクリプトの配置場所を基準にファイルを参照しやすくなります。

ただし、この方法はシンボリックリンクの実体パスを自動的に解決するものではありません。


14. bash -n:構文チェック

Bashでは、スクリプトを実行せずに構文を確認できます。

bash -n sample.sh

-n は、コマンドを実行せずに構文を読み込んでチェックするオプションです。

例えば、次のようなスクリプトがあったとします。

#!/usr/bin/env bash

if [[ 1 -eq 1 ]]; then
  echo "Hello"

このコードでは fi が不足しています。

bash -n sample.sh を実行すると、構文エラーが検出されます。

なお、bash -n は構文の確認が目的であり、変数の値や実行時のエラー、処理の正しさまで保証するものではありません。


15. shfmt:コードのフォーマット

shfmt は、シェルスクリプトのコードを整形するためのツールです。

例えば、次のようなコードがあるとします。

if [[ $# -gt 0 ]];then
echo "$1"
fi

shfmt を使用すると、インデントや空白などを統一できます。

shfmt -w sample.sh

整形後:

if [[ $# -gt 0 ]]; then
  echo "$1"
fi

よく使用するオプションは次のとおりです。

オプション 意味
-w ファイルを直接書き換える
-d 整形前後の差分を表示する
-l 整形が必要なファイルを表示する
-i 2 インデントをスペース2つに設定する

例えば、次のコマンドでスペース2つのインデントに統一できます。

shfmt -i 2 -w sample.sh

※shfmt はBashの標準コマンドではないため、別途インストールが必要です。

また、shfmt は主にコード整形を行うツールです。潜在的なバグや危険な書き方の検出には、ShellCheck という静的解析ツールも役立ちます。


16. まとめ

今回は、シェルスクリプト初心者が最初に覚えたいBashの基本構文を紹介しました。

構文・コマンド 役割
#!/usr/bin/env bash Bashで実行するためのShebang
NAME="value" 変数を定義する
$1 / $2 引数を個別に取得する
$# 引数の個数を取得する
"$@" すべての引数を個別に保持する
shift 位置パラメータを左にずらす
${VAR:-default} 未設定または空文字列の場合のデフォルト値
if 条件分岐
case パターンによる条件分岐
while 条件が成立する間繰り返す
function_name() 関数を定義する
set -euo pipefail エラーを検出しやすくする
BASH_SOURCE[0] スクリプトのパスを取得する
dirname パスのディレクトリ部分を取得する
bash -n 構文をチェックする
shfmt コードを整形する

最初からすべての構文を暗記する必要はありません。

実際にシェルスクリプトを書いたり、既存のコードを読んだりしながら、少しずつ覚えていくのがおすすめです。

特に、while、case、shift の組み合わせや、set -euo pipefail、BASH_SOURCE[0] などは、運用・自動化スクリプトを読む際にも役立ちます。

まずは小さなスクリプトを作成して、各構文がどのように動作するのか確認してみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?