パッケージマネージャー(yum/dnf vs apt)、設定ファイルのパス(/etc/sysconfig/ vs /etc/default/)、サービス管理の細かい差異が原因で、1本のスクリプトが複数のディストリビューションをまたがって動かないケースはよくあります。
この記事では、
/etc/os-release の ID 変数と ID_LIKE 変数を使ってOSファミリーを判定し、処理を分岐させる設計を解説します。RHEL 9系~Rocky Linux・AlmaLinux、Ubuntu 22.04 LTS~Debian 12 を想定環境として説明します。この記事のポイント
・/etc/os-release の ID 変数が最もポータブルな判定手段で、RHEL 7・Ubuntu 14.04以降に対応している
・ID_LIKE 変数を使えば AlmaLinux・Rocky Linux などの派生ディストリにも正確に対応できる
・パッケージマネージャーと設定パスの差分はwrapper関数1つで吸収し、呼び出し元のロジックを統一できる
・/etc/os-release がない旧環境や Alpine Linux・WSL2 へのフォールバックも設計に含める
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜシェルスクリプトにOS判定が必要なのか
シェルスクリプトをポータブルに書こうとしても、Linuxディストリビューション間には次の3種類の違いが必ず生じます。パッケージマネージャーの違い
RHEL系(RHEL・Rocky Linux・AlmaLinux)は
dnf(RHEL 8以降)または yum(RHEL 7)を使います。一方、Debian系(Debian・Ubuntu)は apt です。同じ「Nginxをインストールする」という処理でも、コマンドが完全に異なります。設定ファイルパスの違い
RHEL系の多くは環境変数ファイルを
/etc/sysconfig/ 以下に置きます。Debian系では /etc/default/ が対応する場所です。たとえば、Nginxの起動オプションを追記したいとき、パスをハードコードすると片方の環境で失敗します。ディストリビューション固有の挙動
RHEL系では
selinuxenabled コマンドの有無を確認しておく必要があるケースがあります。Debian系ではそのコマンドが存在しないことが多く、チェック処理のエラーになります。こうした差異を1本のスクリプト内でOS判定によって吸収できれば、「RHEL用」「Ubuntu用」と別々にスクリプトを管理する手間がなくなります。
OS・ディストリビューションを判定できる情報源
複数の方法があります。それぞれの特徴と推奨度を整理します。1. /etc/os-release(推奨)
freedesktop.org が定めた標準ファイルです。RHEL 7以降、Ubuntu 14.04以降、Debian 8以降、Alpine Linux 3.x以降でサポートされています。# ファイルの内容例(Rocky Linux 9) NAME="Rocky Linux" VERSION="9.4 (Blue Onyx)" ID=rocky ID_LIKE="rhel centos fedora" VERSION_ID="9.4" PLATFORM_ID="platform:el9" PRETTY_NAME="Rocky Linux 9.4 (Blue Onyx)"
ID にはディストリビューションの識別子、ID_LIKE にはそのベースとなるディストリビューション群が記述されます。これを使うのが最も確実な方法です。2. lsb_release コマンド(非推奨)
以前はよく使われていましたが、最小インストール環境や軽量コンテナイメージには含まれていないことが多く、依存先として頼るのは避けるべきです。スクリプトの先頭でcommand -v lsb_release >/dev/null 2>&1 || true のような存在確認が必要になり、コードが複雑になります。3. 個別の判定ファイル(旧来の方法)
RHEL/CentOSでは/etc/redhat-release、Debianでは /etc/debian_version が存在します。RHEL 6以前など、/etc/os-release が存在しない旧環境のフォールバックとして使います。4. uname -s(OS種別のみ)
Linux・macOS・FreeBSDなど「OSカーネルの種別」を返しますが、ディストリビューションを区別する情報は含まれません。移植性を考えてmacOSも対象に含める場合のみ使います。基本的な判定関数を実装する
/etc/os-release を使った判定関数を実装します。1. os-releaseを読み込んでIDを取得する
source 命令でファイルを読み込むと変数が一括でシェルに展開されます。# /etc/os-releaseを読み込む(ファイルが存在しない場合はスキップ) if [ -f /etc/os-release ]; then . /etc/os-release fi # $IDの値を確認する echo "OS ID: ${ID}" echo "ID_LIKE: ${ID_LIKE}"
OS ID: rocky ID_LIKE: rhel centos fedora
2. OSファミリーを返す関数を実装する
ID と ID_LIKE を組み合わせて「RHEL系」「Debian系」「その他」を返す関数を作ります。#!/bin/bash # OSファミリーを返す関数 # 戻り値(標準出力): "rhel" | "debian" | "alpine" | "unknown" detect_os_family() { local id="" local id_like="" if [ -f /etc/os-release ]; then # サブシェルでsourceして変数を取得 id=$(. /etc/os-release && echo "${ID}") id_like=$(. /etc/os-release && echo "${ID_LIKE}") fi # ID_LIKEにrhel/centos/fedoraが含まれればRHEL系 case "${id_like}" in *rhel*|*centos*|*fedora*) echo "rhel" return ;; esac # IDで直接判定 case "${id}" in rhel|centos|fedora|rocky|almalinux) echo "rhel" ;; debian|ubuntu|linuxmint|pop) echo "debian" ;; alpine) echo "alpine" ;; *) # フォールバック: 旧来のファイルで判定 if [ -f /etc/redhat-release ]; then echo "rhel" elif [ -f /etc/debian_version ]; then echo "debian" else echo "unknown" fi ;; esac }
ID_LIKE を先にチェックすることが重要です。Rocky Linux や AlmaLinux の ID は rocky / almalinux であり、rhel そのものではありません。ID_LIKE には "rhel centos fedora" が入っているため、ここで先に判定することで確実にRHEL系として扱えます。3. 判定結果を変数に格納して後続処理で使う
os_family=$(detect_os_family) case "${os_family}" in rhel) echo "RHEL系ディストリビューションです" ;; debian) echo "Debian系ディストリビューションです" ;; alpine) echo "Alpine Linuxです" ;; *) echo "未対応のOSです(処理を中止します)" exit 1 ;; esac
パッケージマネージャーの差分を吸収するwrapperパターン
OS判定の最も実用的な使い方は、パッケージマネージャーの違いを隠蔽するwrapper関数を作ることです。シェルスクリプトによる自動化の設計パターンはシェルスクリプト実践講座でも詳しく扱っています。
# パッケージインストールのwrapper関数 pkg_install() { local pkg="$1" local family family=$(detect_os_family) case "${family}" in rhel) dnf install -y "${pkg}" 2>/dev/null || yum install -y "${pkg}" ;; debian) apt-get install -y "${pkg}" ;; alpine) apk add --no-cache "${pkg}" ;; *) echo "エラー: 未対応のOSです" >&2 return 1 ;; esac } # パッケージ削除のwrapper関数 pkg_remove() { local pkg="$1" local family family=$(detect_os_family) case "${family}" in rhel) dnf remove -y "${pkg}" 2>/dev/null || yum remove -y "${pkg}" ;; debian) apt-get remove -y "${pkg}" ;; alpine) apk del "${pkg}" ;; esac } # 呼び出し例(OSを意識しない記述ができる) pkg_install nginx pkg_install vim
pkg_install nginx と書けばよく、OSの違いをまったく意識しません。スクリプトの主処理部分がシンプルに保てます。同様に、設定ファイルのパスを返す関数も作れます。
# OS別の設定ディレクトリパスを返す関数 get_sysconf_dir() { local family family=$(detect_os_family) case "${family}" in rhel) echo "/etc/sysconfig" ;; debian) echo "/etc/default" ;; *) echo "/etc/default" ;; esac } # 使用例 sysconf_dir=$(get_sysconf_dir) echo "設定ファイルパス: ${sysconf_dir}/nginx"
コンテナ・Alpine Linux・WSL2への対応
実運用では仮想マシン以外の環境も考慮する必要があります。Alpine Linuxへの対応
Alpine Linux は/etc/os-release に ID=alpine を持ちます。前述の detect_os_family 関数でそのまま判定できます。パッケージマネージャーは apk です。# Alpine Linuxの/etc/os-releaseの例 ID=alpine VERSION_ID=3.19.0 PRETTY_NAME="Alpine Linux v3.19"
Dockerコンテナ環境の検出
コンテナ内と物理サーバーで処理を切り替えたい場合は、/.dockerenv ファイルの有無を確認します。# コンテナ内かどうかを判定する関数 is_container() { if [ -f /.dockerenv ]; then return 0 # コンテナ内 fi # cgroup情報でも確認できる if grep -qE '(docker|lxc|kubepods)' /proc/1/cgroup 2>/dev/null; then return 0 fi return 1 # コンテナ外 } # 使用例 if is_container; then echo "コンテナ環境: syslog送信先を変更します" fi
WSL2の検出
WSL2 は/proc/version に "microsoft" という文字列を含みます。# WSL2かどうかを判定する関数 is_wsl() { if grep -qiE 'microsoft' /proc/version 2>/dev/null; then return 0 # WSL2 fi return 1 } if is_wsl; then echo "WSL2環境: Windows側ファイルシステムとの連携処理を実行します" fi
トラブルシュートとフォールバック設計
「/etc/os-releaseが存在しない」
RHEL 6以前、CentOS 6以前など旧来の環境では/etc/os-release が存在しません。このケースは /etc/redhat-release や /etc/debian_version を確認します。# /etc/os-releaseがない旧環境向けフォールバック _detect_legacy_os() { if [ -f /etc/redhat-release ]; then echo "rhel" elif [ -f /etc/debian_version ]; then echo "debian" elif [ -f /etc/alpine-release ]; then echo "alpine" else echo "unknown" fi }
「sourceしたらスクリプトの変数を上書きされた」
source /etc/os-release を直接実行すると、スクリプト内の変数名と衝突する可能性があります。たとえば NAME 変数を別の目的で使っていた場合、/etc/os-release の NAME で上書きされます。前述の実装例のように、サブシェル
$( . /etc/os-release && echo "${ID}" ) 内で実行することで親シェルの変数を汚染せずに値を取得できます。「ID変数が空になる」
/etc/os-release が存在してもディストリビューションによっては ID が定義されていないケースがあります。関数内で空チェックを入れ、空の場合はフォールバックへ進む設計にします。id=$(. /etc/os-release 2>/dev/null && echo "${ID}") if [ -z "${id}" ]; then # IDが空の場合はフォールバック id=$(_detect_legacy_os) fi
まとめ:ディストリビューション別の判定パターン早見テーブル
主要ディストリビューションのID 値・ID_LIKE 値と、対応するパッケージマネージャーを整理します。| ディストリビューション | ID | ID_LIKE | パッケージマネージャー | 設定ディレクトリ |
|---|---|---|---|---|
| RHEL 9 | rhel |
fedora |
dnf |
/etc/sysconfig/ |
| Rocky Linux 9 | rocky |
rhel centos fedora |
dnf |
/etc/sysconfig/ |
| AlmaLinux 9 | almalinux |
rhel centos fedora |
dnf |
/etc/sysconfig/ |
| CentOS 7 | centos |
rhel fedora |
yum |
/etc/sysconfig/ |
| Fedora | fedora |
(なし) | dnf |
/etc/sysconfig/ |
| Ubuntu 24.04 | ubuntu |
debian |
apt |
/etc/default/ |
| Debian 12 | debian |
(なし) | apt |
/etc/default/ |
| Alpine Linux 3.x | alpine |
(なし) | apk |
/etc/ |
OS判定の実装ポイントをまとめます。
・
/etc/os-release を source(ドット演算子)で読み込み、$ID と $ID_LIKE を使って判定する・
ID_LIKE を先にチェックすることで、Rocky LinuxやAlmaLinuxなどの派生ディストリを確実にRHEL系として扱える・パッケージマネージャーの呼び出しをwrapper関数に隠蔽し、呼び出し元のロジックを統一する
・
source はサブシェル内で実行して親シェルの変数を保護する・
/etc/os-release がない旧環境はフォールバック関数で補完する
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:シェルスクリプトで標準入力とファイル引数を統一的に処理する設計|パイプ渡しとファイル指定の両対応をシンプルに実装する方法
- この記事の属するカテゴリ:シェルスクリプトへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます