- `set -euo pipefail` と安全な IFS を有効にすると、bash スクリプトは早期に失敗し、終了コードと未定義の変数を処理するときにサイレント エラーを回避できます。
- ERR のトラップや、INT または TERM などの信号を使用すると、障害の記録、制御されたクリーニングの実行、システムの一貫した状態の維持が容易になります。
- レベルとタイムスタンプを備えた集中型ログ システムにより、DevOps および CI/CD 環境でのトレーサビリティ、デバッグ、スクリプト統合が向上します。
- 防御的プログラミング、入力検証、適切なデバッグ手法を組み合わせることで、運用環境での bash スクリプトの信頼性が高まります。

Bash を使用してタスクを自動化すると、常に監視する必要があるシステムと、単に監視するだけで済むシステムとの間に大きな違いが生じます... 自動的に動作し、何か問題が発生すると警告を発します。DevOpsやシステム管理環境では、優れたスクリプトとは単に連鎖させるだけではない。 コマンドサイレントエラーを避け、迅速にデバッグし、明確な痕跡を残すことが重要です。 ログ 何がいつ起こったのかを知るために。
スクリプトを本格的に使い始めると、Bashはデフォルトでかなり寛容であることに気づきます。何か問題が起きても、 スクリプト 通常 空の変数や不完全な結果を使用して、何も起こらなかったかのように続行するバックアップが途中で切れてしまうという恐ろしい話はここから来ているのです(そのため、 Linuxでrsyncを使って同期する)、ルートの構築が不十分なために大量の削除が発生したり、正しく実行されているように見えても途中で失敗したデプロイメントが発生したりするなど、さまざまな問題が発生します。だからこそ、適切な使い方を学ぶことは非常に重要です。 set -euo pipefail、trap、そして適切なログシステム.
BashがDevOpsで重要な理由
システムとDevOpsの日常業務において、Bashは常に存在するツールであり、サーバー上でも Linuxコンテナ、CI/CDマシン、メンテナンススクリプト…その最大の利点は ランタイムや外部ライブラリのインストールに依存しないシステムにすでに含まれているシェルを使用すると、ヘルスチェックから完全なデプロイメントまですべてを自動化できます。
その力には落とし穴がある。スクリプトが下手だと、コマンドが失敗しやすくなり、 脚本の残りの部分は、すべてが完璧に進んだかのように続きます。これに重要なタスク (バックアップ、ログのローテーション、ファイルの削除、スケジュールされた実行) を追加すると、リスクは理論上のものではなく、遅かれ早かれ、気付かないうちに何かが壊れることになります。
そのため、自動化に加えて、最初からそれを組み込むことが非常に重要です。 エラー処理、デバッグ、構造化ログここで、現代の Bash のスクリプトの優れた例すべてで繰り返される 3 つの要素が登場します。 set -euo pipefail、trap、および一貫したログシステム.
実例: 厳密なエラー処理を備えた監視スクリプト
非常に便利なパターンは、主要なシステム情報(CPU、メモリ、ディスク、ネットワーク、プロセス、最近のログなど)を収集し、さらに 安全に失敗し、ログファイルに明確な痕跡を残すこのタイプのスクリプトは、手動で使用したり、cron から使用したり、CI/CD パイプラインをサポートするために使用したりできます。
一般的な考え方 一般的な考え方としては、次のようなスクリプトを用意します。
- 厳格モードが有効 早期に失敗し、矛盾した状態で実行を継続しないようにするには、`set -euo pipefail` を使用します。
- ERRやINT、TERMなどの信号をトラップする 退社前にエラーを記録してクリーンアップします。
- 集中ログ機能 タイムスタンプとレベル (INFO、WARNING、ERROR) を含むファイルをコンソールに書き込みます。
- モジュール式検査 CPU、メモリ、ディスク、ネットワーク、およびシステム ログ。
- 依存関係のオプションのインストール sysstat、lm-sensors、net-tools など、Debian/Ubuntu または RHEL/CentOS/Fedora に適合したもの。
常習犯 このタイプのスクリプトでは、次のようなものがよく見られます。
- 色変数 警告やエラーを強調表示する ターミナル (赤、緑、黄、青、NC)。
- 次のようなルート
/var/log/system-monitor.log一般的なログと/var/log/system-metrics.log定期的なメトリック用。 - 設定可能なしきい値 アラート: たとえば、ALERT_CPU_THRESHOLD=80、ALERT_MEM_THRESHOLD=80、ALERT_DISK_THRESHOLD=85 など。
- Un 監視間隔 MONITOR_INTERVAL は、スクリプトをループで実行する場合に、X 秒ごとにチェックを繰り返します。
その基盤に基づいて、情報を収集するだけでなく、 障害に対して適切に対応し、明確な証拠を残し、より大規模なプロセスに統合できます。.
厳密モードを有効にする: -euo pipefail を設定し、IFS をセキュアにする
Bashの「奇妙な」問題の多くは、シェルがデフォルトで、 コマンドが失敗したり、変数が存在しないことは深刻なこととは見なされません。より安全な動作を強制するために、多くの人が「厳密な Bash モード」と呼ぶモードが頻繁に有効になります。
厳密モードの基本的なコンポーネントは次のとおりです。
- セット-e: は、一部の特殊なコンテキスト (if、while、&&、||…) を除き、コマンドが 0 以外の終了コードを返すとすぐにスクリプトを終了します。
- セット-u o set -o 名詞セット: 定義されていない変数を使用しようとすると、空の文字列として扱われず、エラーが発生します。
- set -o パイプ失敗: パイプラインの終了コードを、最後のコマンドだけでなく、失敗した最初のコマンドのコードにします。
- IFS=$'\n\t': 内部フィールド区切り文字を改行とタブに制限し、ファイルのリストなどのループでスペースが中断されるのを防ぎます。
すべてを組み合わせると、本格的なスクリプトのヘッダーでは非常に一般的なものになります。
set -euo pipefail 安全なヘッドボード
IFS=$'\n\t'
これにより、パイプラインの中間コマンドが失敗しても、最後のコマンドが 0 を返すためスクリプトが続行されるという状況が防止されます。また、次のような状況も防止されます... スペルミスまたは未定義の変数 暗黙的に空の文字列になり、これが間違ったパスや満たされない論理条件の原因となることがよくあります。
set -e の罠: なぜそれが魔法ではなく、スクリプトを壊す可能性があるのか
非常に便利ですが、 set -eは万能薬ではないその動作には微妙なニュアンスがあり、その仕組みを理解していないと驚くかもしれません。例えば、次のような構文があります。
- コマンド内
if,whileountil. - 表現
&&y||フロー制御用。 - 特定の算術展開。
期待されるような「即時退出」は引き起こしません。典型的な例としては、 後置増分による算術拡張:
((contador++))
算術展開は、Bashが終了コードとして解釈する値を返します。後置インクリメントの場合、式が0を返すと成功とみなされますが、それ以外の場合は1を返すことがあります。これは set -e はスクリプトをそこで終了させますループ内のカウンターのような無害なものによって、スクリプトが途中で終了する可能性があります。
対照的に、次のような構文は ((++i)) または ((i+=1)) これらは異なる動作をし、同じ問題を引き起こすことはありません。これは、 set -e を使用する場合は、使用している構成をよく理解しておく必要があります。境界線上のケースが多数あるからです。
そのため、多くの著者や経験豊富な管理者は、 特にテスト中はset -e...これは脆弱性を見つけるための積極的な方法だが、本番環境ではより明示的なアプローチ、つまり手動で出力コードをチェックし、 スクリプトをいつどのように中止するかを正確に決定する.
トラップ: エラーと信号をキャプチャして適切にクリーンアップし、ログに記録します。
コマンド トラップ これは、Bashにおける防御的スクリプトについて語る上で、もう一つの重要な役割を担っています。基本的に、シェルがコマンドを受け取った際に実行したいコマンドや関数を指定できます。 サインまたは特別なイベント (ERR や EXIT など)。
典型的な用途 堅牢なスクリプトにおける trap の一般的な使用法は次のとおりです。
- トラップ 'error_handling_function' ERR: コマンドが失敗したときにエラー処理ロジックを実行します。
- トラップ 'cleaning_function' INT TERM: CTRL+C (SIGINT) や停止要求 (SIGTERM) などのシグナルに反応します。
- トラップ 'finalization_function' EXIT: スクリプトが正常に実行されたかどうかに関係なく、スクリプトの終了時にブロックを実行します。
CTRL+Cを受け取ったときに突然停止するのではなく、関数を呼び出すスクリプトを想像してください。 クリーニング 一時ファイルを削除したり、接続を閉じたり、ログに明確なメッセージを残したりします。
trap 'limpieza' TERM INT 予防処置
function limpieza(){
echo "Ejecutando limpieza, el usuario uso CTRL + C"
# ... lógica de limpieza ...
}
このパターンは、特に長いプロセス(バックアップ、ETL、デプロイメント)で役立ちます。 システムを不整合な状態にせずに制御された方法で中止できること.
トラップERRで捕獲できるものとできないもの
一般的な例 多くのガイドでは、次のようなものを使用することを推奨しています。
trap 'echo "Error en línea $LINENO" >&2' ERR キャプチャライン
回線番号に障害を登録します。問題ありませんが、推奨されます 自分の限界を理解するトラップは次のことができます:
- ERRがトリガーされたときに追加のコードを実行するたとえば、日付、スクリプト、行をログに書き込みます。
- 最後のコマンドの終了コードにアクセスするには
$?. - 知っています $LINENOを含むスクリプト行デバッグに非常に便利です。
なに できません ERRトラップは、既に実行されたコマンドの標準エラー出力(stderr)を「救出」するために使用されます。ただし、事前にファイルまたは記述子にリダイレクトして読み取り可能な状態にしておかない限り、このエラー自体は 単一の値ではなくテキストストリーム.
いくつかのパターンでは、スクリプト全体のstderrを一時ログにリダイレクトし、トラップで、 日付、ファイル、行を含むヘッダーを追加します。 これにより、エラーブロックとスクリプト内の障害発生箇所を関連付けることができます。手間はかかりますが、本番環境で何かがクラッシュした場合、コンテキスト情報があると非常に役立ちます。
Bashでのログ記録:必要なものだけをフォーマットして記録し、混乱を招かない
たとえ最低限の重要タスクであっても自動化する場合は、次の点を知っておく必要があります。 何がいつ起こり、どのような結果になったかそのため、本格的なスクリプトのほとんどすべてには、形式を統一する集中ログ機能が含まれることになります。
典型的な機能 典型的な関数は次のようになります。
log_message() { 統一フォーマット
local level="$1"
local message="$2"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${timestamp} ${message}" | tee -a "$LOG_FILE"
}
例 次のように呼び出すことができます:
- log_message "INFO" "CPUチェックを開始しています"
- log_message "警告" "ディスク使用率が85%を超えています"
- log_message "ERROR" "データベースに接続できませんでした"
常に同じ形式を使用し、日付とレベルを明確に示します。 ティー-a また、メッセージを画面上で表示し、同時にログ ファイルに保存することもできるため、インタラクティブな使用と後の分析の両方に非常に便利です。
もう一つの良い方法は、 アクティビティログ (スクリプトが何をするか)と メトリクスまたは結果ログ (CPU、RAM、ディスクなどの値)を異なるファイルに分割します。例:
- /var/log/システムモニター.log イベントやメッセージ用。
- /var/log/システムメトリックス.log 定期的なステータス記録用。
統合を促進する これにより、人間のメッセージと機械データを混在させることなく、外部ツール、パーサー、さらには観測可能性ソリューションにデータを供給することが容易になります。
システムステータスのチェック: CPU、メモリ、ディスク、ネットワーク、ログ
Linux 環境での Bash の非常に一般的な使用法は、必要なすべての情報を一度に収集する「クイック システム チェック」スクリプトを実装することです。 問題を診断するための基本情報このタイプのスクリプトには通常、複数のブロックが含まれます。
- CPU負荷:着用
mpstat(sysstatパッケージのおかげで)利用可能な場合、またはtopバッチ モードで CPU 使用率の概要を取得します。 - CPUあたりの上位プロセス:と
ps aux --sort=-%cpu | head -n N何が最も多くのリソースを消費しているかを確認します。 - ディスクスペース:と
df -hルートパーティションとアプリケーションに不可欠なボリュームに特に注意を払います。 データベース (方法を見る Linuxでスペースを解放する). - RAMとSWAPメモリ:経由
free -hどれだけ使用されているか、過剰なスワップ使用が行われているかどうかを確認します。 - ネットワーク状態: 既知のIP(通常は8.8.8.8)へのpingで外部接続を確認し、アクティブなインターフェースを一覧表示します。
ip addr showなどのツールを使って ネットキャット (nc/ncat) より高度なテストのために。 - 最近のシステムログ: 最後の線を描く
journalctl -nまたは同等の機能を使用して、スクリプトの実行直前にサービス エラーを検出します。
結果は通常、ログファイルに保存されるか、画面上にフォーマットされた形式で表示され、 システムの健全性のクイックスナップショット 自分で確認したり、監視ツールに統合したりできます。
依存関係の自動インストールと権限チェック
前提条件 多くの管理スクリプトの重要な点は、何か重要なことを実行する前に、次の 2 つの条件が満たされているかどうかを確認することです。
- スクリプトは次のように実行されています ルート (または南経由).
- 必要なツールがインストールされている オペレーティング システムによって異なります。
ルート検証 ルート認証は通常、 $EUID:
check_root() { ルート権限を持っていない場合は失敗します。
if ; then
log_message "ERROR" "Este script necesita privilegios de root para instalar paquetes."
log_message "WARNING" "Por favor, ejecute con sudo."
exit 1
fi
}
これにより、権限不足による後続のエラーを回避し、 最初からユーザーへの明確なメッセージ.
ディストリビューションを検出する 依存関係のインストールは通常、 /etc/os-release 分布ファミリを検出するには:
- En Debian / Ubuntu 使用される
apt-getsysstat、lm-sensors、net-tools などのパッケージをインストールします。 - En CentOS / RHEL / Fedora 引っ張られるだろう
dnfoyum同等のパッケージ (lm_sensors、net-tools など) を使用します。
アラート、cron、ログエクスポート: スクリプトを本番環境に導入する
十分に堅牢なチェックスクリプトや自動化スクリプトができたら、次の論理的なステップは 日常生活に取り入れる:
- メール、Slack、その他のチャネル経由のアラート例えば、ディスク使用率が90%を超えた場合、mailxまたはSlackのWebhookを使用してメールを送信します。典型的なパターンは、df/awk/sedで値を確認し、特定のしきい値を超えた場合に通知をトリガーすることです。
- cronによる定期実行: 次のようなエントリを追加します
0 8 * * * /ruta/a/script.sh毎日特定の時間に実行されるようにします。 - 出力をログファイルにリダイレクトする: 実行する
./script.sh > /var/log/system-check.log 2>&1すべての標準出力とエラー出力を 1 つのファイルに記録する場合。
スクリプトが設計されている場合、 最初の間違いで止まる `set -euo pipefail`のおかげで、これを呼び出すcronまたはCI/CDパイプラインは明らかに ゼロ以外の終了コード 障害が発生した場合、回復フローをトリガーしたり、ビルドを失敗としてマークしたりできるようになります。
Bashでのデバッグ: -x、-v、-ny トレースをファイルに設定する
Bashには他の言語のようなデバッガはありませんが、いくつかの機能を提供しています。 非常に強力なデバッグオプション これを正しく使用すると、「一体なぜこんなことが起きているのか?」と考える時間を何時間も節約できます。
- -x を設定する o set -o xtrace: 実行された各コマンドを、すべてのパラメータが展開された状態で表示します。変数が実際にどのような値を取るかを確認するのに最適です。
- セット-v o set -overbose: 展開前のスクリプト行をそのまま出力します。フローを理解するのに役立ちます。
- -n を設定 o -o noexec を設定する: コードを実行せずに構文を分析します。引用符、中括弧、または角括弧の欠落を検出するのに最適です。
- セット-uすでに述べたように、未定義の変数を使用すると失敗しますが、これは論理エラーのデバッグ中に非常に役立ちます。
さらに、これらのオプションの効果を制限して、 スクリプトの特定の断片 これを set -x / set +x で囲みます。例:
set -x 時間痕跡
# bloque problemático
read -p "Pass Dir name : " D_OBJECT
read -p "Pass File name : " F_OBJECT
set +x
#!/bin/bash リダイレクトトレース
exec 6> salida_debug.log
BASH_XTRACEFD="6"
set -x
# ... resto del script ...
したがって、すべてのデバッグ情報は デバッグ出力.logそして、ターミナルではスクリプトの「通常の」出力のみを引き続き確認できます。
未定義変数のバグの典型的な例
set -u を使わない場合によくある問題は、 存在しない変数 気づかないうちに。オブジェクト名を尋ね、それがファイルかディレクトリかを確認するスクリプトを想像してみてください。しかし、変数名は間違っています。
#!/bin/bash 変数エラー
read -p "Nombre del Objeto : " OBJECT
if ]; then
echo "$OBJECT es un archivo"
elif ]; then
echo "$OBJECT es un directorio"
fi
コモ $OBJECT1 は定義されていませんBash はそれを空の文字列に展開し、-fy -d テストはエラーを返さず、単に条件を満たしていないため、スクリプトはコード 0 で終了します。何も「発生」しませんが、論理的には結果は期待どおりではありません。
代わりにスクリプトを次のように実行した場合 bash -u スクリプト名 またはアクティブ セット-uすぐに「未バインド変数」エラーが発生し、型エラーに直接つながります。 bash -x スクリプト名 または、`set -x` を使用すると、使用されている特定の行と値も確認できるため、デバッグがはるかに簡単になります。
バイトの世界とテクノロジー全般についての情熱的なライター。私は執筆を通じて自分の知識を共有するのが大好きです。このブログでは、ガジェット、ソフトウェア、ハードウェア、技術トレンドなどについて最も興味深いことをすべて紹介します。私の目標は、シンプルで楽しい方法でデジタル世界をナビゲートできるよう支援することです。
