Linuxのシェル(Bash)に関する世界観2(シェルスクリプト)です。
シェルスクリプトは、簡単な定型処理やバッチ処理、そしてコマンド行のメモに使えます。
たとえば、ホームページのデータを更新して、gitコミットし、lftpでサーバーに送信し、rsyncで同期し、tarでアーカイブしてコピーすると言った具合です。
行をたくさん書く必要は必ずしも無く、コマンドのヒストリーをカーソルキーで参照するように、「数行だけ書かれたシェルスクリプト」を作って、それをbashから実行すれば、まるでWindowsでショートカットアイコンをダブルクリックするように、ひとつのアクションでコマンドを実行できます。シェルでは「./hoge.sh」と実行するか、GNOMEなどではダブルクリックで実行できます。
lftpについては専用のスクリプト記法があるのでそれを参照すれば良いでしょう。lftpを参照のこと。他のUNIXコマンドについてはただそのコマンドを一行に書けばそれで実行できます。変数や条件分岐、繰り返しや関数呼び出しなどもできます。特に引数を指定してそれを変数として使えるようにスクリプトを書けば、状況によって別の処理を実行させられます。
また、システムの内部では、bashやX11の初期化スクリプトや、デーモンやサービスを適切に実行させるRCスクリプト(SysV initの場合)でシェルスクリプトが使われているほか、automake/autoconfでもソフトウェアのコンパイル・インストールの際にシステムの構成をチェックして移植性を高めるためにシェルスクリプトが多用されています。またGentoo LinuxのPortageはPythonで書かれていますが、それぞれのパッケージのビルドやインストール情報を記述したebuildファイルはbashで書かれています。
ファイルのバックアップなどは、きちんと端末で入力するとさまざまなコマンドを覚えなければなりませんが、面倒ならシェルスクリプトに一度書いてしまいましょう。何度でも手軽に使えます。シェルスクリプトの利用は強制ではないので、使いたいコマンドだけ好きに使ってください。
UNIXシステム管理(システム情報と設定)やinitとデーモンやUNIXシステム管理(cron)なども参照のこと。
シェルスクリプトは、上手く使いましょう。
たとえば、大事なファイルをUSBハードディスクにrsyncで同期しているとします。この時、対話型シェルでrsyncをかけると、コマンド行を間違えるかもしれません。ファイルが多いと、大変なことになります。
こうした時に、「間違えることのないようにシェルスクリプトに記述して、それを実行する」といったことができます。
シェルスクリプトは、シェルのスクリプトです。よって、シェルの中でコマンドで実行できることは、シェルスクリプトに書いて実行できます。「シェルにおけるコマンド実行は、シェルスクリプトに書くのと同じ」であるため、「シェルで自分が行うことは全てスクリプトに書ける」と言えます。
たとえば、メールの送信は、sendmailコマンドをシェルスクリプトに記述することで可能です。
応用すれば、「何かのタイミングでメールを送信する」といったことも可能です。たとえばcronデーモンでスクリプトを定期実行したり、サーバーのCGIからエラーが発生した時にスクリプトを実行して、異常を感知した時にメールを管理者に送信する、といった用途が考えられます。
UNIXには、「システムの力はプログラム単体ではなく、複数の小さなプログラムの関係から生まれる」という基本哲学があります。
そして、シェルスクリプトはまさに、このUNIX哲学の体現です。
すなわち、ひとつのプログラムだけを使うのではなく、プログラムそれぞれをシェルスクリプトの中で、「どのように組み合わせるか」ということを記述し、その組み合わせられた結果として、より柔軟に、複雑に、高度に、必要な場合に適した実行内容を実行するのです。
このようにすることは、自動化といった点からも強力です。すべてのコマンド入力を、手作業で入力する必要はありません。シェルスクリプトにすべてのコマンド列を事前に書いておいて、そのスクリプトを自動的に、たとえばcronから定期的に実行することができるのです。
シェルスクリプト以外にもviやemacsからはそれをひとつの環境であるかのように、UNIXコマンドをターミナルのように実行することができます。基本的に、自動化する場合はシェルスクリプト、インタラクティブに操作・編集して使う場合はviやemacs、という使い分けをするといいでしょう。
変数の例:
#!/bin/bash HOGE=Assy echo $HOGE
シェルに引数を与えた場合、$1, $2, $3のように引数の値にアクセスできる。
関数の例:
HOGE=Assy function hoge() { echo "My name is $HOGE." }
functionは省略可能。
HOGE=Assy hoge() { echo "My name is $HOGE." }
"~"とダブルクォーテーションでくくった場合は$の変数展開がされる。'~'とシングルクォーテーションでくくった場合は変数展開がされない。
変数HOGEを上手く使うことで、以下のようなシェル入力ができる。
$ HOGE=Assy $ hoge(){ > echo "My name is $HOGE." > } $ hoge My name is Assy. $ HOGE=Schwarz $ hoge My name is Schwarz.
また、関数の引数はスペース(空白)によって引数と引数の間を区切って渡し、関数の中から$1, $2, $3のようにアクセスできる。
#!/bin/bash hoge() { echo "My name is $1, and your name is $2." } hoge Assy Schwarz
以下は参考文献。
BashはCシェルと違い独特の記法がある。
たとえば、if文やwhile文は
if [ 式 ]; then ~ fi
や
while [ 式 ]; do ~ done
のようにブラケットを使い、case文は閉じ括弧を使って
case $HOGE in "f") echo Foo;; "b") echo Bar;; esac
のように用いる。
比較式も特殊であり、
数値1 -eq 数値2
のように独自の短縮されたキーワードを使う。
ifブロックを閉じるのはfi、caseブロックを閉じるのはesac、for/whileブロックはdo~doneで記述するなど、訳が分からない。
まるでジョークのようだが、むしろbashで操作中にコマンドのような形で制御を行いたい、という視点から見ると逆にこれが良いのかもしれない。
コマンドでls -aを打つ感覚で、
if [ $HOGE = $FUGA ]; then echo "Hoge and Fuga is same." fi
とか、
if [ $HOGE -eq 0 ]; then echo "Don't use zero." fi
のように、「コマンドラインのコマンド行ライクなスクリプト記法」であると言える。
以下はBashによるシェルスクリプトの活用例。制御文、ファイル読み込み、while read line、演算や比較、関数などについての方法が参照できる。
コマンド置換は$()あるいは``で行える。また、二重かっこ(())は数学的な演算に使える。
標準出力はecho、標準入力はreadで行える。パイプやリダイレクトも使用できる。
ちなみに、二重ブラケット[[]]は文字列の比較に使える。
Bashのようなシェルスクリプトは、連続した処理を実行するために使えます。
ですが、それはあくまで、途中で失敗しなければです。
たとえば、僕は先日、少しずつ増分で増えていく同名の複数のファイルについて、ひとつの場所にコピーしながらgitでコミットし続ける、ということを行いました。
これは、シェルスクリプトが書ける人ならば、全部自動でできたかもしれません。
ですが、途中の処理で失敗してしまうのが怖かったため、僕はこの作業を手動でファイルマネージャから行いました。
今書くとしたら、以下のようになるかもしれません。
TEXTS=/home/assy/hoges GIT=/home/assy/git_hoge FILE=hoge.txt copy_git() { cd $TEXTS cd 2023.$1.$2 cp $FILE $GIT cd $GIT git commit -m "2023.$1.$2" } copy_git 01 01 copy_git 01 02 copy_git 01 03 copy_git 01 04 # 以下続く
ですが、これが本当にきちんと動くかどうかは不安なところです。
シェルスクリプトを書くということは、本当はとても怖い作業です。慣れない人間にはさせるべきではないと思います。
2023.04.06
かつてツイッターで山田ハヤオ (@Hayao0819)さんに教えてもらったこととして、Bashでは、二重のスラッシュ//はひとつのスラッシュ/と解釈されます。
そのため、
ASSYHOME=/home/assy/ cd $ASSYHOME/hoge/
のようなコードが動作します。cd /home/assy//hoge/が問題なく解釈されて/home/assy/hoge/にcdできます。
2023.09.22
実際、Bashのシェルスクリプトを実行する際に、「壊れるかもしれないから怖い」と思う人には、最近は仮想マシンという優れ物があります。
すなわち、壊れるかもしれないことをやる前に、テスト用の仮想マシンを作ってそのテスト環境でまず試して、壊れた時は仮想マシンごと消して新しい仮想マシンを作るということができます。
なので、壊れるかもしれないことをやる前に、仮想マシンを作ってその環境の中で行い、もし成功した時はその中から必要なデータを取り出してそれを使うようにすると、比較的安全だと思います。
後日注記:Dockerをわざわざ使わなくても、処理するデータあるいは処理前のデータを一時的なディレクトリにコピーしてから処理をするとか、gitのようなバージョン管理システムを使うとか、元のデータをtarでアーカイブしてバックアップするとか、やりようはいくらでもあります。いきなり本番環境のデータを間違えて処理して、システムをぶち壊すという失敗が絶対にないようにしましょう。Windowsを使うのであっても同じで、僕はサクラエディタでgrep置換を行う際に、間違えた時はバックアップから復旧できるようにしています。
2024.05.22
2024.05.25編集
ビル・ジョイによるCに近い記法のCシェルについてはCシェルを参照のこと。