Bashプログラミング
 Computer >> コンピューター >  >> プログラミング >> Bashプログラミング

Bashを極める!システム管理者のための実践テクニック集

どんな職業にも、その道の達人が最も頻繁に使う道具があります。多くのシステム管理者(sysadmin)にとって、それはシェルです。Linuxやその他のUnix系システムの大半では、デフォルトのシェルとしてBashが採用されています。

Bashは1980年代後半に生まれた、かなり歴史のあるプログラムですが、その基盤となっているのはさらに古いシェル、たとえばCシェル(csh)などであり、それらはBashより10年以上前から存在しています。シェルという概念自体が非常に古いため、システム管理者の仕事を格段に楽にしてくれる「秘伝の知識」が世の中に数多く蓄積されています。

今回は、その基本となるテクニックをいくつか見ていきましょう。

rootでの誤操作を防ぐ

誰でも一度は、意図せずrootでコマンドを実行してしまい、何らかの問題を引き起こした経験があるのではないでしょうか?(手を挙げます)

おそらく多くの方が、一度は「あの人」になったことがあるはずです。痛い思い出ですね。ここでは、同じ轍を二度と踏まないための、とても簡単なテクニックを紹介します。

エイリアスを活用する

まず、mvrmといったコマンドに対し、mv -irm -iを指すエイリアスを設定しましょう。こうすることで、たとえrm -f /bootのような危険なコマンドを実行しても、少なくとも確認を求められるようになります。Red Hat Enterprise Linuxでは、rootアカウントを使用する場合、これらのエイリアスがデフォルトで設定されています。

一般ユーザーアカウントでも同じ設定を使いたい場合は、ホームディレクトリにある.bashrcファイルに次の2行を追加するだけです(sudo環境でも機能します)。

alias mv='mv -i'
alias rm='rm -i'

rootプロンプトを目立たせる

事故を防ぐもう一つの方法は、rootアカウントを使用しているときに常に意識できるようにすることです。私は普段、日常業務用のプロンプトとは明確に異なるデザインをrootプロンプトに適用しています。

rootのホームディレクトリにある.bashrcに以下を追加すると、黒地に赤文字のrootプロンプトになり、「慎重に操作すべき状態」であることが一目瞭然になります。

export PS1="\[$(tput bold)$(tput setab 0)$(tput setaf 1)\]\u@\h:\w # \[$(tput sgr0)\]"

なお本来は、rootでのログイン自体を極力避け、ほとんどのコマンドをsudo経由で実行するのが望ましいのですが、これはまた別の話です。

rootアカウント使用時の「意図しない副作用」を防ぐための小技をいくつか紹介したところで、次は日々の業務で役立つBashの便利な機能を見ていきましょう。

コマンド履歴を自在に操る

Bashで上矢印キーを押すと、過去に実行したコマンドを確認・再利用できますよね。これは、コマンドがホームディレクトリの.bash_historyファイルに保存されているためです。この履歴ファイルには、非常に便利な設定やコマンドが多数用意されています。

まず、historyと入力すれば最近のコマンド履歴全体を表示でき、history 30と入力すれば直近30件に絞り込めます。しかしこれだけでは平凡です。実は、Bashが何をどのように保存するかは、もっと細かく制御できます。

たとえば、以下を.bashrcに追加すると、先頭が半角スペースで始まるコマンドは履歴に保存されなくなります。

HISTCONTROL=ignorespace

これは、コマンドライン引数として平文のパスワードを渡す必要がある場合などに便利です。(平文パスワードの利用は本来避けるべきですが、現実にはまだ存在します。)

また、頻繁に実行するコマンドを履歴に残したくない場合は、次のようにします。

HISTCONTROL=ignorespace:erasedups

この設定では、コマンドを実行するたびに履歴ファイル内の同じコマンドの古い記録が削除され、最後に実行したものだけが履歴リストに残ります。

個人的にお気に入りの設定がHISTTIMEFORMATです。これを設定すると、履歴のすべてのエントリにタイムスタンプが付与されます。たとえば私は次のように設定しています。

HISTTIMEFORMAT="%F %T  "

history 5と入力すると、次のような見やすい情報が得られます。

1009  2018-06-11 22:34:38  cat /etc/hosts
1010  2018-06-11 22:34:40  echo $foo
1011  2018-06-11 22:34:42  echo $bar
1012  2018-06-11 22:34:44  ssh myhost
1013  2018-06-11 22:34:55  vim .bashrc

これにより、コマンド履歴をさかのぼって、「2日前に自宅ラボへのSSHトンネルを張るのに使ったあのコマンド」を簡単に見つけられるようになります。(何度忘れても、また調べ直すことになりますが…)

Bashスクリプト作成のベストプラクティス11カ条

最後に、Bashスクリプトを書く際のベスト(少なくともグッドな)プラクティス、私のトップ11を紹介します。

11. コメントは惜しまず書く

Bashスクリプトは複雑になりがちですが、コメントのコストはほぼゼロです。「コメントを付けるべきか迷ったら」付けましょう。週明けに出社して、先週金曜日に何をしようとしていたのか解読するために時間を費やすようなら、コメントを付け忘れていた証拠です。

10. 変数名は波括弧で囲む

すべての変数名を${myvariable}のように波括弧で囲みましょう。これを習慣にすれば、${variable}_suffixのような記述が可能になり、スクリプト全体の一貫性も向上します。

9. バッククォートではなく$()を使う

式を評価するときはバッククォートを使わず、$()構文を使いましょう。つまり:

for file in $(ls); do

こちらではなく:

for file in `ls`; do

前者はネスト可能で読みやすく、システム管理者コミュニティ全体も喜びます。バッククォートは使いません。

8. 一貫性を保つ

一貫性は重要です。ひとつのスタイルを選んだら、スクリプト全体を通してそれを貫きましょう。もちろん、私としてはバッククォートより$()構文を、変数は波括弧で囲むスタイルをお勧めします。インデントはタブではなくスペース2つか4つが好みですが、仮に「間違った」やり方を選ぶとしても、それを一貫して行うことが大切です。

7. 適切なshebangを使う

Bashスクリプトには適切なshebangを指定しましょう。Bashでのみ実行することを前提にスクリプトを書くなら、私は#!/usr/bin/bashを使うのが一般的です。#!/bin/sh#!/usr/bin/shは使わないでください。スクリプトは実行されますが、互換モードで動作し、意図しない副作用が発生する可能性があります。(もちろん、互換モードが目的なら別です。)

6. 文字列比較では変数を引用符で囲む

if文で文字列を比較する際は、変数を引用符で囲むのが安全です。変数が空の場合、次のようなコードはエラーになります。

if [ ${myvar} == "foo" ]; then
  echo "bar"
fi

一方、次のように引用符で囲めばfalseと評価されるだけで済みます。

if [ "${myvar}" == "foo" ]; then
  echo "bar"
fi

また、変数の中身が不明な場合(ユーザー入力を解析するときなど)、特殊文字の意図しない解釈を防ぎ、空白を含んでいても1つの語として扱われるよう、必ず引用符で囲みましょう。

5. 比較には==を使う

これは好みの問題かもしれませんが、私はBashで文字列を比較する場合でも、ダブルイコール(==)を使います。一貫性の問題です。文字列比較に限ればシングルイコールでも動作しますが、シングルイコールを見ると脳が即座に「代入演算子だ!」と反応してしまうのです。

4. 適切な終了コードを返す

スクリプトが処理に失敗した場合は、ユーザーに失敗メッセージ(できれば問題の解決方法も添えて)を表示し、非ゼロの終了コードを返しましょう。

# 処理失敗
echo "Process has failed to complete, you need to manually restart the whatchamacallit"
exit 1

こうしておけば、別のスクリプトからこのスクリプトを呼び出し、正常完了をプログラム的に検証することが容易になります。

3. 変数のデフォルト値や未定義チェックを活用する

Bashの組み込み機構を使って、変数に妥当なデフォルト値を設定したり、定義されているはずの変数が未定義の場合にエラーを出したりできます。

# $myvarの値をredhatに設定し、'redhat'と出力する
echo ${myvar:=redhat}
# $myvarが未定義の場合、'The variable myvar is undefined, dear reader'というエラーを出す
${myvar:?The variable myvar is undefined, dear reader}

2. 関数内ではlocalキーワードを使う

特に大きなスクリプトを書く場合、特に他の人と共同で作業する場合は、関数内で変数を定義するときにlocalキーワードの使用を検討しましょう。localキーワードはローカル変数、つまりその関数内でのみ参照可能な変数を作成します。これにより、変数名の衝突リスクを最小限に抑えられます。

1. スクリプトの行は長くしすぎない

システム管理者なら誰でも時々必要になるのが、コンソール上でのデバッグです。データセンターの物理コンソールでも、仮想化プラットフォーム経由の仮想コンソールでも同じです。そのような環境でスクリプトをデバッグすることになったとき、この教訓を思い出せば自分を褒めたくなるはずです。スクリプトの行を長くしすぎてはいけません!

多くのシステムでは、コンソールのデフォルト幅は今なお80文字です。コンソール上で非常に長い行を含むスクリプトをデバッグしなければならなくなると、悲惨な思いをすることになります。そもそも80文字以内の短い行で構成されたスクリプトは、通常のエディタでも読んで理解するのがはるかに容易なのです。

私は本当にBashが大好きです。それについて何時間でも語れますし、仲間の愛好家たちと素晴らしいテクニックを交換するのも楽しみの一つです。あなたのお気に入りのテクニックも、ぜひコメント欄で教えてください!

  1. 毎日の作業で本当に役立つBash履歴ショートカット7選

    多くのBash履歴ショートカットのガイドは、利用可能なショートカットを網羅的にリストアップしています。しかし問題なのは、私がショートカットを一度使ってみても、すべてのパターンを試そうとして頭が混乱し、その後日常業務に戻ると完全に忘れてしまうことでした。結局残っていたのは、Bashを使い始めた頃に覚えた有名な!!のテクニックだけでした。 つまり、ほとんどのショートカットは記憶に定着しなかったのです。 この記事では、私が毎日実際に使っているショートカットを紹介します。内容の一部は私の著書『Learn Bash the Hard Way』(プレビューを読むことで詳細を確認できます)に基づいています。

  2. 【完全ガイド】Macで検索履歴・閲覧履歴を消去する方法

    MacでSafariやGoogle Chrome、Firefoxなどのブラウザを使ってWebページを閲覧すると、検索ボックスの入力履歴や訪問したページの履歴など、あらゆる活動の足跡が残ります。さらに、ブラウザはキャッシュ、Cookie、画像なども読み込んで保存しており、これにより次回同じページへアクセスした際の読み込み速度が向上します。しかし、プライバシー保護のために他人に閲覧したサイトを知られたくない、機密データへのアクセスを防ぎたい、Macのストレージ容量を空けたいといった理由から、検索履歴や閲覧履歴を残したくない場合もあるでしょう。この記事では、Macで閲覧履歴・検索履歴を消去する方法を