_シェル_とは、コマンドを入力してオペレーティングシステムを操作するためのプログラムです。 シェルにはグラフィカルなものもあり、マウスやトラックパッドで操作するデスクトップ環境もシェルの一種です。 ここではコマンドラインインターフェースに焦点を当てます。コマンドラインインターフェースでは、コマンドを入力すると、その結果が(たいていは)ターミナルに表示されます。
コマンドラインシェルにはいろいろな種類があります。 Windowsにはcmdとpowershellがあります。 MacとLinuxには「Unixシェル」と呼ばれるシェルがあり、こちらも多くの種類があります。 Macのデフォルトのシェルはzshです。 cshはC言語に似た構文を持つ古いシェルです。 bashは、特にLinuxで、最もよく使われるシェルの1つです。
最も初期のシェルの1つがBourneシェル、shです。 やがてこのシェルは標準化され、Unix系のシステムには必ず用意されるようになりました。 多くのシェルはBourneシェルを基にしており、対話機能やプログラミング機能を追加で備えていることがよくあります。
BashはBourneシェルを基にしています。 これが、Bashが「Bourne-again shell」と呼ばれる理由です。 多くのLinuxシステムではデフォルトのシェルになっています。 Bashには、対話シェルとして快適に使えるようにする機能がたくさんあります。たとえば、
ここで扱うのは、bashを対話シェルとして使う方法ではありません。 bashを使ってプログラムを書けるようになることが目的です。 外部ツールを呼び出すことは制限されていませんが、_bashだけ_を使って問題を解くことに重点を置きます。
Bashは、入力(通常はターミナルかファイル)からコマンドを読み取ります。 読み取った入力の各行は、_コマンド_として扱われます。つまり、実行すべき命令です。 (複数行にまたがるコマンドなど、いくつかの高度なケースについては後で説明します。)
Bashは各行を、空白文字(スペースとタブ)で区切られた単語に分割します。 行の最初の単語が、実行する_コマンド_の名前です。 残りの単語はすべて、そのコマンドへの_引数_になります(オプション、ファイル名など)。
次の例では、実際に実行できる対話的なコマンドをいくつか紹介します。
#は_コメント_の始まりを表します。
コメントに続く単語はシェルには無視され、コメントは読むためだけのものです。$はシェルのプロンプトを表します。
コマンドをコピーするときは、プロンプトは含めないでください。# Note: It is always a good idea to run `ls` before `rm`
# to make sure you know what you are deleting.
$ rm * # Remove all files in the current directory.
# Make sure you run this in a temp directory or you can lose files!
$ ls # List files in the current directory (no output, no files).
$ touch a b c # Create files 'a', 'b' and 'c'.
$ ls # List files again; this time the outputs: 'a', 'b' and 'c'.
a b c
rmは、渡されたファイルを削除するアプリケーションです。
*はグロブ文字で、カレントディレクトリのすべてのファイルに一致する「ワイルドカード」パターンです。
グロブについては後で詳しく説明します。
aとbの間にはスペースがいくつかあり、bとcの間には1つしかないことに気づきましたか? 引数の間の空白の量は関係ありません!
たとえば、次のようになります。
$ echo This is a test.
This is a test.
$ echo This is a test.
This is a test.
echoは、引数を標準出力(ここではターミナル)に出力するコマンドです。
Bashはコマンドを5つの単語に分割し、4つの引数でechoコマンドを呼び出します。
2つ目の例の余分なスペースは何の違いも生みません。
echoは、もとの単語の間にスペースがいくつあったかを知りません。
余分な空白をそのまま残したい場合は、文全体を1つの引数として渡す必要があります。 それには引用符を使います。
$ echo "This is a test."
This is a test.
引用符は、その中にあるものをすべてまとめて_1つの引数_にします。
その引数はThis is a test.で、そこにあるスペースもそのまま含まれます。
引用符自体は引数の一部ではないことに注意してください。bashは、その1つの引数をechoに渡す前に引用符を取り除きます。
引数は、コマンド名との間、そして引数どうしの間を空白で区切ります。 これは覚えておくことが大切です。 たとえば、次の例は間違っています。
$ [-f file]
bash: [-f: command not found
これは、fileという名前のファイルが存在するかどうかを調べるつもりで書いたものです。
[と]のまわりの空白は関係ないと思うかもしれませんが、[は実はコマンドで、その最後の引数が]である必要があります。
([コマンドについては後で詳しく説明します。)
そのため、[と-f、]とfileをそれぞれ区切る必要があります。
そうしないと、bashは[-fという名前のコマンドを、file]という1つの引数で実行しようとしていると解釈します。
正しいコマンドは、すべての引数を空白で区切ります。
$ [ -f file ]
(この動作に戸惑う人はたくさんいます。[...]は「ただの構文」で、スペースは省略できると思っているのです。しかし[はコマンドであり、空白が必要です。)
ファイル名に空白やその他の特殊文字が含まれる場合も、同じように引用符で囲みます。
$ [ -f "my file" ]
Bashは、いくつかの種類のコマンドを扱えます。エイリアス、関数、組み込みコマンド、キーワード、実行ファイルです。 これらはすべて_コマンド_であり、どれも非常によく似た動作をします。
デフォルトでは、エイリアスは対話シェルでのみ有効で、シェルスクリプトでは有効になりません。 ここでは扱いません。
関数はシェルのコマンドを含んでいて、小さなスクリプトとよく似た働きをします。引数を受け取ったり、ローカル変数を作ったりもできます。 関数を呼び出すと、その中のコマンドが実行されます。 関数については後で詳しく扱います。
Bashには、cd(ディレクトリを移動する)、echo(出力を書き出す)など、あらかじめ組み込まれた基本的なコマンドがあります。
これらは、すでに用意されている関数のようなものだと考えられます。
キーワードは組み込みコマンドに似ていますが、主な違いは、キーワードが実際にはbashの構文であり、特別な規則で解析されることです。
たとえば、[はbashの組み込みコマンドですが、[[はbashのキーワードです。どちらもさまざまな条件を判定するために使われます。
ただし[[キーワードには追加の機能があります。
たとえば、正規表現の判定を行う=~演算子が使えます。
$ [[ "hello world" =~ e.*o ]] # returns a "success" status
$ [ "hello world" =~ e.*o ]
bash: [: =~: binary operator expected
文字列を比較する演算子<と>は、キーワードがコードを違うように解析するもう1つの例です。
[[...]]の中では、これらの演算子に特別な配慮は必要ありません。
$ if [[ "$USER" < "n" ]]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
しかし[...]の中では、シェルがこれらをリダイレクト演算子として解析しないように、引用符で囲むかエスケープする必要があります。
$ if [ "$USER" < "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => bash: n: No such file or directory
$ if [ "$USER" \< "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
bashが実行できる最後の種類のコマンドは_実行ファイル_です。
これは、コンピューターのファイルシステム上にある、実行できるファイル(プログラム)のことです。
(実行ファイルは_外部コマンド_や_アプリケーション_と呼ばれることもあります。)
実行ファイルは、通常は名前を入力するだけで呼び出せます。ファイルへの完全なパスはたいてい必要ありません。
これは、bashがあらかじめ定義された変数を使って、よく使われる実行ファイルの場所を探せるようにしているからです。
その変数はPATHと呼ばれます。
コロンで区切られたディレクトリ名の集まりです(例:/usr/bin:/bin)。
ファイルパスを指定せずにコマンド(たとえばmyprogramやls)を指定すると(それがエイリアス、関数、組み込みコマンド、キーワードのいずれでもなければ)、bashはPATHにあるディレクトリを順に探します。
ディレクトリは左から右へ順に調べられ、指定された名前の実行ファイルがあるかどうかが確認されます。
実行ファイルが既知のパスの外にある場合は、その実行ファイルのパスを指定する必要があります。
カレントディレクトリにあるmyprogramという実行ファイルなら./myprogram、/opt/somedirectoryディレクトリにあるなら/opt/somedirectory/myprogramと書きます。
ヒント:typeコマンドを使うと、そのコマンドの詳細を調べられます。
$ type rm
rm is hashed (/bin/rm)
# or it might return: `rm is /bin/rm`
$ type cd
cd is a shell builtin
_スクリプト_とは、ファイルに保存された一連のコマンドです。 Bashはファイルを読み込み、コマンドを順番に処理します。 現在のコマンドが終わってから、次のコマンドに進みます。 例外は、コマンドが非同期(バックグラウンド)で実行されるように指定されている場合です。 このケースはまだあまり気にしなくて大丈夫です。どのように動くかは後で学びます。
このガイドにあるコマンドラインの例は、どれもスクリプトでも使えます。
スクリプトを作るのは簡単です。 まず新しいファイルを作り、最初の行にこれを書きます。
#!/bin/bash
この行は_インタプリターディレクティブ_と呼ばれます(_ハッシュバン_や**シバン**とも呼ばれます)。
これは、そのファイルをコマンドの実行ファイルとして使うときに、/bin/bashをインタプリターとして使うことを指定します。
OSのカーネルが_非バイナリファイル_を実行するとき、カーネルはそのファイルの最初の行を読みます。
その行が#!で始まっていれば、カーネルはその行を使って、ファイルを渡す先のインタプリターを決めます。
(ほかにも有効な方法があります。後述します。)
#!はファイルの先頭に、その前にスペースや空行を置かずに書く必要があります。
スクリプトのコマンドは、この行の下に1行ずつ書いていきます。
ヒント:#!/bin/bashの代わりに、次のようにも書けます。
#!/usr/bin/env bash
envは、最初の引数で指定された名前(この場合は「bash」)の実行ファイルを$PATHから探します。
この技法の詳しい説明や、普通の#!/bin/bashとの違いについては、StackOverflowのこちらの質問を参照してください。
インターネット上のスクリプトや例で、インタプリターとして/bin/shを使っているものに惑わされないでください。
shはbashではありません!
bash自体は「sh互換」のシェルで、ほとんどのshスクリプトを実行でき、同じ構文を多く備えています。
しかし、その逆は当てはまりません。bashの一部の機能はshでは壊れたり、予期しない動作を引き起こしたりします。
実際のところ、単一のshというシェルは存在しません。
POSIX-shの構文をサポートするシェルの実装はたくさんあります。
OSによっては、shを実行しても、実際にはbash、ash、dash、あるいは別のシェルが動いていることがあります。
スクリプトファイルを作成したら、次のようにして実行できます。
$ bash myscript
この例では、コマンドbashを実行し、_引数_として渡したスクリプトを読み込むように指示しています。
このように実行する場合、#!の行は単なる_コメント_です。
別の方法として、スクリプトに実行権限を与えることもできます。 この方法なら、bashを手動で呼び出さなくても、ほかの実行ファイルと同じようにスクリプトを実行できます。
$ chmod +x myscript # Mark the file as executable.
$ ./myscript # Now, myscript can be executed directly
# instead of having to specify bash.
この方法でコマンドを実行すると、オペレーティングシステム(OS)が#!の行を使って、スクリプトを実行するインタプリターを決めます。
このレッスンの内容は、Bash GuideのCommands and Argumentsを基にしています。