シェルスクリプト

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動先: 案内検索

シェルスクリプトは、オペレーティングシステムシェルまたはコマンドラインインタプリタ向けに書かれたスクリプトである。シェルは単純なドメイン固有言語の一種と見なされることが多い[1]。シェルスクリプトで書かれる典型的処理としては、ファイル操作、プログラム実行、テキストの印刷などがある。

シェルスクリプト用インタプリタの多くはコマンドラインインタフェースも兼ねており、各種UnixシェルWindows PowerShellMS-DOSCOMMAND.COMなどがある。他にAppleScriptやグラフィカルな Windows Script Host (WScript.exe) などもあり、コマンドラインインタフェース抜きでコンピューティング環境にスクリプト機能を加えている。他のシェルスクリプト向けのプログラミング言語としては、テンプレート:仮リンクJCLなどがある。

機能

ショートカット

その最も基本的な形式として、シェルスクリプトはシステムコマンドに特別な環境設定、コマンドオプション、後処理などを自動的に適用する形で新たなコマンドのバリエーションを提供するが、そのスクリプトを普通の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システムコールにシェルスクリプトのファイル名を渡すと、シバンカーネルが解釈し、そこに指定されたファイルを実行ファイル、元々渡されたスクリプトファイルをその実行ファイルへのパラメータとして実行させる。初期の Bourne Shell が登場したころはこのような機能はなかった。このため現代のシェルスクリプトはシステムコマンドと同じ足場に立っているだけでなく、実際に多くのシステムコマンドがシェルスクリプトで実装されている(さらに言えば、PerlPythonなどの言語で書かれたスクリプトも同様の機構で実行できる)。そのため、スクリプトも通常のシステムユーティリテイと同様に終了ステータスとして成功か失敗かを返すようになっていき、どういった言語で実装されているかに関わらず大規模なソフトウェアのコンポーネントとして使えるようになっていった。

標準的なシステムコマンドと同様、シェルスクリプトのファイル名には拡張子をつけないことが多い。しかし、動作中のシェルにシェルスクリプトを読み込ませる特別な機構(例えば、sh の “. ”コマンドや cshsource コマンド)を使う場合は、その限りではない

プログラミング

現代の多くのシェルは汎用プログラミング言語に見られるような豊富な機能を備えている。制御構造、変数、コメント、配列、サブルーチンなどである。それらを使えばかなり洗練されたアプリケーションをシェルスクリプトで書くことも可能である。しかし、シェル言語の多くはデータ型システム、クラス、スレッド、複雑な数学的計算といった高水準言語に見られる機能をほとんどサポートしていない。また、コンパイラや性能重視のインタプリタで実装された汎用言語に比べれば性能が低い。

他のスクリプト言語

テンプレート:Main シェルスクリプトで扱うには大きいあるいは複雑すぎるタスクを扱うため、もっと強力な様々なスクリプト言語が開発されてきた。ただしそのような用途には高水準言語もあり、高水準言語とスクリプト言語のそれぞれがどういう用途に適しているかは議論のテーマともなってきた。一般にスクリプト言語はインタプリタとして実装される。

開発ライフサイクル

ソフトウェア開発の初期段階でシェルスクリプトを使い、それらを徐々に PerlPythonC言語などに書き換えていくことがある。シバンによって実装の詳細がスクリプト内に隠蔽され、ファイル名を見ても拡張子などから実装が判明しないため、他の言語へのシームレスな再実装が可能である。

長所と短所

同じプログラムを書く場合、他のプログラミング言語よりもシェルスクリプトの方が早いことが多い。プログラムが容易でファイル操作機能が豊富で、素早く起動でき、対話的にデバッグできるためである。既存のプログラム群を順次実行する場合や何らかの条件判断を伴って実行する場合などはシェルスクリプトが有効で、中規模のシェルスクリプトではコンパイルが不要という点も利点の一つである。インタプリタ実行なのでデバッグ用コードを挿入するのも容易であり、バグも見つけやすい。また、複数のプログラムを限定的な形ながら並列実行することもできる。

一方でシェルスクリプトは手痛いエラーを引き起こしやすい。例えばUNIXコミュニティには rm -rf */rm -rf * / と打ち間違えるといううっかりミスの伝説が存在する。空白がよぶんに1つ入っただけで、あるディレクトリ配下を全て消去するつもりだったものが、ルートディレクトリ配下全体を消去する指示になってしまった例である。同様に出力をリダイレクトする > を間違って使用することで、cpmv も危険な武器になってしまう(ファイルの中身を上書きしてしまう)。また、UNIXには1文字しか違わないコマンド名が多く存在するため、さらにうっかりミスの危険性が増す。例えば、cpcdddテンプレート:仮リンク などである。

もう1つの短所は、実行速度の遅さとほぼ全てのシェルコマンド実行で新たなプロセスを生成する必要がある点である。多くのスクリプトの仕事はフィルタコマンド群をパイプで繋ぐことであり、性能問題はあまり関係ない。しかし、複雑なスクリプトはコンパイル方式の言語で実装した場合に比べて桁違いの遅さになることが多い。

また、プラットフォーム間の互換性問題もある。Perlの作者ラリー・ウォールの有名な言葉として「シェルスクリプトを移植するより、シェルそのものを移植する方が簡単だ」というものがある。

また、複雑なスクリプトを書こうとするとシェルスクリプト言語自身の制限にぶちあたることが多い。そのため回避策を施すことでコード品質が悪化し、シェル自体を拡張することで上述の互換性問題を引き起こすことになる[2]

一部スクリプト言語を使った際の多くの短所は、言語の文法上の欠陥や実装上の欠陥が原因である。

脚注

テンプレート:Reflist

関連項目

外部リンク

  • テンプレート:Citation
  • "Csh Programming Considered Harmful"