Bash

Linux で標準的に使われるシェル。macOS にも同梱され、コマンド操作と自動化の中心になる

シェルインフラ
Bash」の技術書を見る →

Bash とは

Bash (バッシュ) は、Linux をはじめとする多くの UNIX 系 OS で標準的に使われてきたシェルだ。シェルとは、利用者が打ち込んだコマンドを解釈して OS に伝える窓口にあたる。Bash はターミナルでの操作を担うとともに、コマンドをまとめて自動実行するシェルスクリプトの実行環境としても広く使われている。

シェルとしての役割

機能内容
コマンド実行ファイル操作やプログラム起動
パイプコマンドの出力を次の入力につなぐ
変数・制御構文スクリプトでの条件分岐・繰り返し
履歴・補完過去のコマンド呼び出しや入力補助

特に、複数のコマンドをパイプでつないで処理を組み立てる発想は、UNIX 由来の強力な考え方を体現している。

なぜ重要か

サーバーの操作、開発環境のセットアップ、ファイルの一括処理、自動化スクリプトなど、IT の現場では Bash でのコマンド操作が日常的に発生する。GUI では手間のかかる作業も、コマンドを組み合わせれば一瞬で終わることが多い。Bash に習熟することは、作業効率を大きく左右する基礎スキルになる。

POSIX シェルとの関係

Bash は POSIX が定めるシェル仕様の実装に独自の拡張を重ねたシェルだ。--posix を付けて起動するか set -o posix を実行すると、既定の挙動が POSIX と食い違う部分を標準寄りに寄せる POSIX モードになる。sh という名前で起動された場合も、起動ファイルを読んだ後に POSIX モードへ入る。

ここに移植性の落とし穴がある。POSIX モードは標準と異なる挙動を合わせるだけで、Bash 独自の機能を禁止するわけではない。そのため macOS の /bin/sh (中身は Bash) では、#!/bin/sh と書いたスクリプトでも配列や [[ ... ]] がそのまま通ってしまう。同じスクリプトを Debian 系へ持っていくと /bin/sh は dash を指しているため文法エラーで落ちる。「手元で動いたから POSIX 準拠」とは考えず、Bash 拡張を使うなら #!/usr/bin/env bash と明示するのが確実だ。

macOS に入っている Bash は 3.2 系

macOS 10.15 以降、既定のログインシェルは zsh になっている。Bash も /bin/bash として同梱されているが、版は 3.2.57 で据え置きだ。連想配列 (declare -A) は Bash 4.0 で入った機能なので、同梱の Bash では次のように失敗する。

$ /bin/bash -c 'declare -A m'
/bin/bash: line 0: declare: -A: invalid option

手元の Mac で書いたスクリプトが Linux サーバーでは動く (あるいはその逆) という食い違いは、多くがこの版差に由来する。新しい Bash が必要なら Homebrew などで別に入れ、シバンでそちらを指す。

set -euo pipefail の効果と限界

スクリプト先頭の定番 set -euo pipefail は 3 つの別々の設定をまとめたものだ。

設定効果
-eパイプラインが非ゼロ終了したらシェルを終える
-u未設定の変数を展開したらエラーにする (特殊パラメータの @* は対象外)
-o pipefailパイプの返り値を、非ゼロで終わった最も右のコマンドの値にする (全て成功なら 0)

pipefail を付けなければ false | true の終了状態は 0 になり、途中の失敗は見えない。ここまでは効果の話だが、限界の方が実務では重要になる。-e はマニュアルが列挙するとおり、while / until の条件部、if の条件部、&&|| で連結したうち最後の && / || の後ろ以外、パイプラインの最後以外、! で反転したものでは発動しない。つまり「失敗したら止まる」ではなく「素の位置で失敗したら止まる」だ。

実際に踏みやすいのは次の 3 つ。

  • 関数を f && echo ok の左辺で呼ぶと、その関数の本体でも -e が効かない。-e が無視される文脈で実行される関数の中身は -e の影響を受けないとマニュアルに明記されている。
  • local v=$(false) は失敗を隠す。終了状態が local (declare) 自身のものになるため -e が発動しない。宣言と代入を分けて書く。
  • -u を付けると、Bash 3.2 では空配列の "${a[@]}" が unbound variable になる。@ 添字の配列を対象外とする挙動は後の版で入ったもので、macOS の同梱 Bash では引っかかる。

スクリプトの落とし穴

最も踏みやすいのは空白を含むファイル名だ。for f in $(ls) のようにコマンドの出力を単語分割させると My File.txt が 2 つに割れる。ファイル列挙は for f in * とグロブで書き、変数は必ず "$f" と引用する。find の結果を渡すときは find . -name '*.txt' -print0 | xargs -0 ... のように NUL 区切りにする。

こうした罠は目で追うより機械に見つけさせる方が速い。shellcheck のような静的検査を通せば、引用漏れや -e が効かない書き方の多くはコミット前に指摘される。それでも Bash は制御構造やデータ構造が貧弱で、分岐とデータ加工が増えるほど読みにくくなる。数百行を超えたあたりが Python など本格的な言語へ移す目安になる。強力なコマンドを扱う以上、削除など破壊的な操作の前に対象を echo で確認する習慣も欠かせない。

この記事は役に立ちましたか?

関連用語

関連する記事