システム管理者のためのBashスクリプト入門:他のシェルより優れている5つの理由
Bashシェルは唯一のシェルではありませんが、間違いなく最も強力なシェルのひとつです。単なるコマンドの羅列以上の、本格的なアプリケーションや自動化処理を開発したいシステム管理者にとって、Bashは人気の選択肢となっています。もちろん他のシェルにも優れた用途はたくさんあります(筆者はGitフックにはTcshをデフォルトで使っています)。しかし、本格的なスクリプティングとなると、Bashが最も手軽で確実な選択です。その理由を詳しく見ていきましょう。
1. 関数(Functions)
Bashスクリプトで関数を作成するには、functionキーワードを使用します。
function foo {
# ここにコードを書く
}
関数はプログラマーにとって非常に有用です。コードの重複を減らせるからです。重複が減ればメンテナンスの手間も軽減されます。値を変更する必要があるときも、使用箇所すべてを探し回るのではなく、関数内の1か所を修正するだけで済みます。
他のシェルとの比較
関数は多くのシェルで比較的よくサポートされていますが、Tcshだけは顕著な例外です。
- Kornシェル: Bashと同じ構文を使用
- Zsh: Bashと同じ構文を使用
- Fish: 独自のfunctionコマンドで関数の作成・操作を行う
- Tcsh: gotoステートメントを使えば関数がない問題を回避できる場合もありますが、最適な選択とは言えません
2. リダイレクト(Redirection)
入出力のリダイレクトは、あらゆるプログラミング言語やスクリプト言語における標準的な機能であり、シェルを使っている限り自然に行われるものです。テキストを入力すれば、結果が出力される。ただし、シェルによって機能の堅牢さには差があり、Bashは想像できるほぼすべてのオプションを提供してくれます。
基本はシンプルです。出力のリダイレクトには>記号を、入力のリダイレクトには<記号を使用します。さらに、特定の種類の入出力には特別な指定があります。たとえば、エラーメッセージはstderrというストリームに出力され、リダイレクト時には2>として指定します。次のコマンドは、エラーメッセージをoutput.logファイルに振り分ける例です。
$ ls /void 2> output.log
他のシェルとの比較
リダイレクトはシェル操作における大きな利便性ですが、主要なシェルすべてが何らかの形でサポートしている一方で、すべての機能がすべてのシェルで使えるわけではありません。
- Kornシェル: Bashと同じ構文を使用
- Zsh: Bashと同じ構文を使用
- Fish: 部分的にサポート
- Tcsh: 部分的にサポート
3. sourceコマンド
シェルでファイルをsourceすると、そのファイルが現在のシェル環境に読み込まれます。ほとんどのシェルはこれを適切に処理しますが、利便性を高める追加機能を備えているシェルもあります。
たとえばBashでsourceを使うと、まずカレントディレクトリから参照先のファイルを検索します。見つからなければ、今度はPATHを検索します。これは小さな特典のように思えますが、非常に便利です。共通して使う関数をドライブ上の一元的な場所に保存しておけば、自分の環境を統合開発環境(IDE)のように扱えるからです。関数がどこに保存されているか気にする必要はありません。ローカル版の/usr/includeのような場所にあるとわかっていれば、どのディレクトリからsourceしても、Bashが自動的に見つけてくれるのです。
他のシェルとの比較
sourceコマンド、あるいは省略形(POSIX的に正しい)の.表記を使用した際に、カレントディレクトリとPATHの両方を検索するのは、Bashだけです。
4. キーバインド(Key bindings)
多くのシェルでは、シェルとの対話方法をカスタマイズできます。テキストベースのインターフェースとの対話は、まずタイピングから始まりますよね。Bashでは、現在のキーマップを次のコマンドで確認できます。
$ bind -V | grep keymap
キーマップを変更する(たとえばEmacsからViへ切り替える)には、次のようにします。
$ bind 'set keymap vi'
他のシェルとの比較
キーバインドの組み込みプリセットを提供しているのは、BashとZshのみです。
5. 履歴(History)
Bashは、あらゆるシェルの中で最も堅牢なコマンド履歴インターフェースを持っています。Bash版のhistoryは、逆順検索、クイック呼び出し、履歴の編集(行番号によるエントリ削除を含む)など、多彩な機能を備えています。他のすべてのシェルの機能を合計すればBashに匹敵しますが、単体でこれに並ぶシェルはありません(概ねBashを模倣しているZshでさえもです)。
履歴の操作
historyコマンドは、過去のコマンドと対話する唯一の手段ではありません。Bashには、シェル履歴からコマンドを呼び出したり、さらには修正したりできる豊富な省略記法が用意されています。
たとえば、直前のコマンドをもう一度実行したい場合(history | tail -n1の出力。先頭に半角スペースを付けて、historyコマンド自体が履歴に残らないようにしています)、Bashでは!!と入力するだけです。
$ wc -w luarocks.xml
1284 luarocks.xml
$ !!
1284 luarocks.xml
直前のコマンドの一部を別の文字列に置き換えて実行したい場合は、文字列置換を使います。
$ wc -w luarocks.xml
1284 luarocks.xml
$ ^-w^-l
$ wc -l luarocks.xml
214 luarocks.xml
このようなショートカットは他にも多数あり、すべてBashのmanページやinfoページに記載されています。確かに、これらのトリックの多くは、Bashとのやり取りが反復的で単調になりすぎていて、一見ランダムに見えるキーの組み合わせこそが役立つ、上級ユーザー向けのものです。Zshは事実上Bashを模倣していますが、このような効率の最大化を実現しているのはBashだけです。
他のシェルとの比較
Bashの履歴コマンドは他のどのシェルにも匹敵しません(Zshが最も近いですが、行番号による削除など一部のオプションが欠けています)。
6. 連想配列(Associative arrays)
ほとんどのシェルは、インデックス付き配列の作成・操作・照会に対応しています。平たく言えば、インデックス付き配列とは「番号が付けられた項目のリスト」のことです。この項目リストと割り当てられた番号は、ひとつの変数にまとめられるため、コードの中で簡単に「持ち運び」できます。
しかしBashは、連想配列を作成する機能も備えており、これを通常の配列と同じように扱えます。連想配列を使うと、番号付きの値ではなく、「キーと値のペア」からなるリストを作成できます。
連想配列の素晴らしいところは、キーを自由に設定できる点です。
$ declare -A userdata
$ userdata[name]=seth
$ userdata[pass]=8eab07eb620533b083f241ec4e6b9724
$ userdata[login]=`date --utc +%s`
任意のキーを照会できます。
$ echo "${userdata[name]}"
seth
$ echo "${userdata[login]}"
1583362192
配列に期待される一般的な操作のほとんどが利用可能です。
他のシェルとの比較
連想配列を完全にサポートしているのはBashだけです(ここでもZshが最も近いですが、キー一覧を取得する関数がありません)。
シェルスクリプティングにはBashを選ぼう
Bashの便利な機能の一部はPOSIX規格に準拠していません。理論上は、Bashがインストールされていないシステムでは期待どおりに動作しないBashスクリプトを書いてしまう可能性があるということです。このような状態は、スクリプトが「移植性(ポータビリティ)がない」と呼ばれます。
しかし実際には、Bashはフリーかつオープンソースのソフトウェアなので、Linux、BSD、OpenIndiana、Windows、macOSなど、誰でもどんなプラットフォームでもインストールできます。また、Bashをインストールしても、それをデフォルトシェルにする必要はなく、意識的に起動する必要すらありません。優れたシェルスクリプトは、冒頭のshebang行(例:#!/bin/bash)で使用するシェルを明示するからです。
迷ったときは、ドキュメントの中でBashをシェルスクリプトの依存関係として明記しましょう(それが唯一の依存関係であっても)。そうすることで、ユーザーに対して「これは汎用のシェルスクリプトではなく、Bashスクリプトである」と伝えることができます。
Bashには多くの便利な機能があり、筆者の経験では、それらのメリットは「ユーザーがBashをインストールしているかどうか」という懸念をはるかに上回ります。PythonやJava、その他のソフトウェアと同じく、依存関係が存在することは珍しいことではありません。もしBashが気に入っていて、その省略記法やショートカットが役立つと感じているなら、自分の可能性を狭める必要はありません。
-
Bashシェル関数入門:実用的な6つのサンプル例で学ぶチュートリアル
Bashシェル関数とは、複数のUNIX/Linuxコマンドをひとつの名前でグループ化し、後からまとめて実行できるようにする仕組みです。Bashシェル関数は通常のUnixコマンドと同じように呼び出すことができ、現在のシェルコンテキスト内で実行されるため、解釈用の新しいプロセスを生成する必要がありません。bashのエイリアス(alias)と関数はどちらも、長いコマンドや複雑なコマンドに対するショートカットを定義するのに役立ちます。しかし、エイリアスでは制御フローや引数の扱いなど、関数ならではの高度な処理を行うことはできません。本記事では、その違いも含めて詳しく解説していきます。この記事は、進行中の
-
実用的な5つの例で学ぶBashスクリプト入門チュートリアル
Unix SedやUnix Awkシリーズと同様に、本シリーズではBashスクリプティングに関する記事を複数回にわたって公開します。実用的なサンプルを交えながら、Bashスクリプティングのテクニックを幅広く解説していきます。 シェルとは、ユーザーが入力したコマンドを解釈して実行するプログラムです。コマンドはユーザーが直接入力するか、「シェルスクリプト」と呼ばれるファイルから読み込まれます。 ユーザーからの入力を直接読み取る場合、そのシェルは対話型(インタラクティブ)シェルと呼ばれます。 一方、ファイルからコマンドを読み込んで実行する場合は非対話型(ノンインタラクティブ)シェルと呼ばれます。この