シェルスクリプト

シェルスクリプト (英語: shell script) は主にオペレーティングシステムのシェルまたはコマンドラインインタプリタから実行可能なコマンドの一連の流れをファイルにして再利用できるようにしたものである。狭義では、Unixシェルで用いられるスクリプト言語を指す。シェルスクリプトは他のコマンドを組み合わせるためのグルー型のドメイン固有言語とみなされることもある。シェルスクリプトで書かれる典型的処理としては、ファイル操作、プログラム実行、テキストの印刷などがある。 シェルスクリプト用インタプリタの多くはコマンドラインインタフェースも兼ねており、各種Unixシェル、Windows PowerShell、MS-DOSのCOMMAND.COMなどがある。他にAppleScriptやグラフィカルな Windows Script Host (WScript.exe) などもあり、コマンドラインインタフェース抜きでコンピューティング環境にスクリプト機能を加えている。 この項目では、Unixシェルのシェルスクリプトについて記載する。 その最も基本的な形式として、シェルスクリプトはシステムコマンドに特別な環境設定、コマンドオプション、後処理などを自動的に適用する形で新たなコマンドのバリエーションを提供するが、そのスクリプトを普通のUNIXのコマンドとして利用することもできる。 例としてlsコマンドのバリエーションを作るスクリプトを示す。コマンドオプションを事前に提供しており、これを例えば l という短い名前のファイルとして /home/username/bin/l などに置くのが一般的である。 #!/bin/sh LC_COLLATE=C ls -FCas "$@" ここで、1行目はシバンであり、スクリプトの残りの部分を実行するのに使用するインタプリタが "/bin/sh" であることを示している。2行目ではlsコマンドのオプションを指定しており、ファイル形式のインジケータを表示すること、1行に1ファイルの形式で表示すること、省略せずに全ファイルを表示すること、ファイルサイズをブロック数で表示することを指定している。LC_COLLATE=C は文字の照合順序の指定であり、"$@" は l コマンドに渡されたパラメータの全てをそのまま ls コマンドに渡すことを意味する。したがって、ls の通常のコマンドオプションや構文がそのまま使える。 これを使えば、単に l と入力するだけでよく使うファイル一覧表示形式が得られる。 次の例のシェルスクリプトは、カレントディレクトリの全ファイルおよびディレクトリの一覧を表示するショートカットとして使える。 #!/bin/sh clear ls -l -a こちらも先頭行は一般的な #!/bin/sh である。次に clear というコマンドでディスプレイをクリアする。その次の行でこのスクリプトのメインの機能を実行する。ls -l -a というコマンド行は、このスクリプトを実行したときのカレントディレクトリにあるファイルとディレクトリの一覧を表示する。lsコマンドのオプションを変更すれば、ユーザーが必要な表示をさせることができる。 シェルスクリプトを使えば、コマンドラインインタフェースで人手で入力していたコマンド列を自動的に実行でき、一連のコマンドを連続的に実行できる。例えば、あるディレクトリにC言語のソースファイルが3つあるとき、4つのコマンドを人手で入力してビルドする代わりに、次のようなCシェルのスクリプトを作成して、 名称を build としてそのディレクトリに置けば、ビルドを自動実行できる。 #!/bin/csh echo compiling... cc -c foo.c cc -c bar.c cc -c qux.c cc -o myprog foo.o bar.o qux.o echo done. このようなスクリプトを用意しておけば、ユーザーがソースファイルを編集し、その途中で ./build を実行すれば、更新された実行ファイルを生成・評価し、編集に戻ることもできる。ただし1980年代以降、このようなスクリプトは make などの専用ユーティリティに置換されている。 簡単なバッチ処理は孤立したタスクでは珍しくないが、シェルの持つループ機能、評価機能、変数などを使えば、より柔軟なスクリプトを書くことができる。次の例はJPEG画像をPNG画像に変換するbashスクリプトで、画像ファイル名はコマンド行で提供し、ワイルドカードも使用できる。そのためファイル名をスクリプト内に羅列する必要はない。このスクリプトは例えば /home/username/bin/jpg2png といったファイル名で置いておく。 #!/bin/bash for jpg in "$@" ; do # 指定されたファイル名を $jpg として参照 png="${jpg%.jpg}.png" # .jpg を .png に置換することでPNG用ファイル名を生成 echo converting "$jpg" ... # ステータス情報を表示 if convert "$jpg" jpg.to.png ; then # Linuxで一般的な convert というプログラムを使って、フォーマットを変換する mv jpg.to.png "$png" # 成功したら、出力ファイルを正しいファイル名に移動する else # 失敗したらエラーを表示してスクリプトを終了させる echo 'error: failed output saved in "jpg.to.png".' 1>&2 exit 1 fi # "if" の終り done # "for" ループの終り echo all conversions successful # 完了を表示 exit 0 jpg2png コマンドを使えば、例えば jpg2png *.jpg とすることでディレクトリ内の全JPEG画像を変換できる。 シバンとはテキストファイル形式の実行ファイルの一行目に書く#!で始まる命令のことである。シバンそのものはシェルスクリプトとは無関係のオペレーティングシステムの機能である。シェルスクリプトを実行するシェルはオペレーティングシステムから見るとスクリプト言語の一つとして扱われている。すなわち、execシステムコールに実行ファイル名を渡すと、シバンをカーネルが解釈し、シバンで指定された実行ファイルに元々渡されたスクリプトファイルとパラメータが渡され実行される。初期の Unix が登場したころはこのような機能はなかった。もちろんPerlやPythonなどのスクリプト言語のプログラムもシバンを介して実行することができ、シェルスクリプトを含めどういった言語で実装されているかに関わらずに使うことができる。 標準的なコマンドと同様、シェルスクリプトのファイル名には拡張子をつけないことが多い。これはコマンドがどのような言語で実装されているかはユーザーが気にすることではないからである。しかし、動作中のシェルにシェルスクリプトを読み込ませる場合（例えば、sh の “. ”コマンドや csh の source コマンド）は、該当のファイルがシェルスクリプトである必要があるため拡張子をつけることが推奨される。 現代の多くのシェルは手続き型プログラミング言語にある基本的な制御フロー構造などに似た豊富な機能を備えている。制御構造、変数、コメント、配列、サブルーチンなどである。それらを使えばかなり洗練されたアプリケーションをシェルスクリプトで書くことも可能である。しかし、シェル言語の多くはデータ型システム、クラス、スレッド、複雑な数学的計算といった高水準言語に見られる機能をほとんどサポートしていない。また、コンパイラや性能重視のインタプリタで実装された汎用言語に比べれば実行速度が遅い。 シェルスクリプトで扱うには大きい、あるいは複雑で適切ではないタスクを扱うために、awkやPerlなどのスクリプト言語が開発されてきた。ただしそのような用途には高水準言語もあり、高水準言語とスクリプト言語のそれぞれがどういう用途に適しているかは議論のテーマともなってきた。一般にスクリプト言語はインタプリタとして実装される。 スクリプト言語はプログラミング言語と比較して簡単に記述できるため、Proof of Concept実装に適している。ソフトウェア開発の初期段階でシェルスクリプトを使い、そののち Perl、Pythonなどの高機能なスクリプト言語やC言語やRustなどのコンパイラ言語に書き換えていく手法を用いることがある。 同じプログラムを書く場合、他のプログラミング言語よりもシェルスクリプトの方が短く書けることが多い。OS標準コマンド以外を含む豊富なコマンドを容易に利用できるからである。既存のプログラム群を順次実行したり、コマンドの実行結果やファイルの有無などの条件判断を伴って実行する場合などはシェルスクリプトが有効で、限定的ながらパイプライン処理、バックグラウンドジョブ、xargsコマンドの-Pオプションなどを利用すれば、並列処理、並行計算を容易に行うことができる。また、スクリプト言語全般に当てはまることではあるがコンパイルが不要という点も利点の一つである。 一方でシェルスクリプトは中規模以上のプログラムには向いていない。言語仕様やデバッグ機能や例外処理が貧弱で致命的なエラーを引き起こしやすい。例えばrm -rf */をrm -rf * /と打ち間違える有名なミスが存在する。空白が余分に1つ入っただけで、あるディレクトリ以下を削除するつもりだったものがルートディレクトリ以下を削除するという意味になる。またcp や mv、出力をリダイレクトする>を間違って使用すると意図せずファイルを上書きしてしまうことがある。また、UNIXには1文字しか違わないコマンド名が多く存在するため、さらにうっかりミスの危険性が増す。例えば、cp、cd、dd、df（英語版） などである。この中でもcpとの取り違えでは、誤ったパスにファイルをばら撒き、コピー先のファイル構成を破壊して復旧作業に多大な時間が必要となったり、セキュリティ事故に発展する可能性がある。 もう1つの短所は、実行速度の遅さとほぼ全てのコマンド実行の呼び出しで新たなプロセスを生成する必要がある点である。実際にデータを処理するコマンドはC言語などの高速な言語で実装されているため処理速度自体はあまり問題にならない。しかし、シェルスクリプトから多数のコマンドを呼び出す場合、その呼び出しコストによって桁違いの遅さになることが多い。処理するデータが多い場合は一括してコマンドに渡して処理し、処理するデータが少ない場合はシェル自身が持っている機能（変数展開など）を使って処理することで、実行速度の遅さをある程度回避できる。しかしそのような工夫をするぐらいなら他のスクリプト言語やコンパイラ言語を使った方が簡単である。 また、プラットフォーム間の互換性問題もある。シェルスクリプトからよく利用されるUNIXコマンドは歴史的な理由でプラットフォーム間で完全な互換性がないため、そのようなコマンドに依存するシェルスクリプトは移植性が低くなりがちである。Perlの作者ラリー・ウォールの有名な言葉として「シェルスクリプトを移植するより、シェルそのものを移植する方が簡単だ」というものがある。エリック・レイモンドは「The Art of UNIX Programming」で「複雑なシェルスクリプトは、移植性問題を抱えていることが多い。それはシェル自身に問題があるというよりも、コンポーネントとして他のプログラムが存在することが前提となっていることに起因するものだ。Bourne ShellやKorn Shellのクローンは、Unix以外のオペレーティングシステムにも散発的に移植されているが、シェルプログラムは（現実的にいって）Unix以外のオペレーティングシステムには移植性がない。」とシェルスクリプトの移植性の低さを指摘している。 また、複雑なスクリプトを書こうとするとシェルスクリプト言語自身の制限にぶちあたることが多い。そのため回避策を施すことでコード品質が悪化し、シェル自体を拡張することで上述の互換性問題を引き起こすことになる。 一部スクリプト言語を使った際の多くの短所は、言語の文法上の欠陥や実装上の欠陥が原因である。