シェルスクリプト

**シェルスクリプト** () とは、主にUnix系オペレーティングシステムにおいて、シェルと呼ばれるコマンド実行環境上で実行されるスクリプト言語である。1970年代のUNIX開発期に成立し、利用者やシステム管理者が複数のコマンドをテキストファイルに記述し、分岐や反復などの制御構文を用いて処理を自動化するために用いられる。

本項ではUNIX系シェルにおけるシェルスクリプトを中心に扱う。

## 機能
### ショートカット
その最も基本的な形式として、シェルスクリプトはシステムコマンドに特別な環境設定、コマンドオプション、後処理などを自動的に適用する形で新たなコマンドのバリエーションを提供するが、そのスクリプトを普通のUNIXのコマンドとして利用することもできる。

例としてlsコマンドのバリエーションを作るスクリプトを示す。コマンドオプションを事前に提供しており、これを例えば l という短い名前のファイルとして /home/*username*/bin/l などに置くのが一般的である。


```sh
#!/bin/sh
LC_COLLATE=C ls -FCas "$@"
```


ここで、1行目はシバンであり、スクリプトの残りの部分を実行するのに使用するインタプリタが "/bin/sh" であることを示している。2行目ではlsコマンドのオプションを指定しており、ファイル形式のインジケータを表示すること、1行に1ファイルの形式で表示すること、省略せずに全ファイルを表示すること、ファイルサイズをブロック数で表示することを指定している。LC_COLLATE=C は文字の照合順序の指定であり、"$@" は l コマンドに渡されたパラメータの全てをそのまま ls コマンドに渡すことを意味する。したがって、ls の通常のコマンドオプションや構文がそのまま使える。

これを使えば、単に l と入力するだけでよく使うファイル一覧表示形式が得られる。

次の例のシェルスクリプトは、カレントディレクトリの全ファイルおよびディレクトリの一覧を表示するショートカットとして使える。


```sh
#!/bin/sh

clear
ls -l -a
```


こちらも先頭行は一般的な #!/bin/sh である。次に clear というコマンドでディスプレイをクリアする。その次の行でこのスクリプトのメインの機能を実行する。ls -l -a というコマンド行は、このスクリプトを実行したときのカレントディレクトリにあるファイルとディレクトリの一覧を表示する。lsコマンドのオプションを変更すれば、ユーザーが必要な表示をさせることができる。

### バッチ処理
シェルスクリプトを使えば、コマンドラインインタフェースで人手で入力していたコマンド列を自動的に実行でき、一連のコマンドを連続的に実行できる。例えば、あるディレクトリにC言語のソースファイルが3つあるとき、4つのコマンドを人手で入力してビルドする代わりに、次のようなCシェルのスクリプトを作成して、 名称を build としてそのディレクトリに置けば、ビルドを自動実行できる。


```csh
#!/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 といったファイル名で置いておく。


```bash
#!/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、Rubyなどの高機能なスクリプト言語や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以外のオペレーティングシステムには移植性がない。」とシェルスクリプトの移植性の低さを指摘している。

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

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

## 脚注

## 関連項目

* グルーコード
* シバン (Unix)
* Unixシェル
* Windows PowerShell
* Windows Script Host
* バッチファイル

## 外部リンク
* ''An Introduction To Shell Programming'' by Greg Goebel
* ''UNIX / Linux shell scripting tutorial'' by Steve Parker
* ''Shell Scripting Primer'' (Apple)
* Free Unix Shell scripting books
* Video Tutorial for beginners