BASH Programming - Introduction HOW-TO by Mike G mikkey at dynamo.com.ar Mon Jul 27 11:47:00 ART 2000 日本語訳 千旦裕司 July 2000 この文書は、初中級のシェルスクリプトのプログラムを始める人の手引書とし て書かれています。(タイトルからも分かるように)これは高度な内容を解説し た文書ではありません。わたし自身、シェルプログラムのエキスパートではな いですし、ましてや達人でもありません。これを書こうと思い立ったのは、そ こから自分が多くを学べるだろうと思ったからであり、もしかしたら他人の役 に立つかもしれないと考えたからです。どんなフィードバックも歓迎します。 特に、パッチ形式でいただけると嬉しく思います :) ______________________________________________________________________ 目次 1. イントロダクション 1.1 最新バージョンの入手場所 1.2 必要事項 1.3 この文書の使い方 2. 非常にシンプルなスクリプト 2.1 伝統の Hello World スクリプト 2.2 非常にシンプルなバックアップスクリプト 3. リダイレクトションについて(redirection) 3.1 理論と簡単なリファレンス 3.2 サンプル: stdout を file へ 3.3 サンプル: stderr を file へ 3.4 サンプル: stdout を stderr へ 3.5 サンプル: stderr から stdout へ 3.6 サンプル: stderr と stdout から file へ 4. パイプ (pipe) 4.1 何なのか、どこがいいのか 4.2 サンプル: sed を使った簡単なパイプ処理 4.3 サンプル: 'ls -l *.txt' の代替コマンド 5. 変数 (variable) 5.1 サンプル: 変数を使った Hello World! 5.2 サンプル: 非常にシンプルなバックアップスクリプト(改良版) 5.3 ローカル変数 6. 条件制御(conditionals) 6.1 セオリー 6.2 サンプル: if .. then による基本的な条件制御の例 6.3 サンプル: "if .. then ... else" を使った基本的な条件制御の例 6.4 サンプル: 変数を伴った条件制御 7. for, while, until によるループ 7.1 For のサンプル 7.2 C 言語に似た for 7.3 while のサンプル 7.4 until のサンプル 8. 関数(functions) 8.1 関数のサンプル 8.2 パラメーターを取る関数のサンプル 9. ユーザインターフェイス 9.1 簡単なメニューを作るために select を使う 9.2 コマンドラインを使ってみる 10. その他のスクリプト 10.1 read を使ってユーザの入力を読み込む 10.2 数値の評価 10.3 bash を探す 10.4 プログラムの戻り値を獲得する方法 10.5 コマンド出力を獲得する方法 10.6 複数のソースファイル 11. 参照テーブル 11.1 文字列比較演算子 11.2 文字列比較の実例 11.3 数値演算子 11.4 数値関係演算子 11.5 便利なコマンド 11.5.1 sed (stream editor) 11.5.2 awk (データファイルやテキストの検索と整形処理) 11.5.3 grep (検索パターンに合致する行の出力) 11.5.4 wc (行、語、バイト数を数えます) 11.5.5 sort (テキストファイル内の行の並べ替え) 11.5.6 bc (数値計算用のプログラミング言語) 11.5.7 tput (端末の初期化や terminfo データベースの検索) 12. いろいろなスクリプト 12.1 ディレクトリ上の全ファイルにコマンドを適用する方法 12.2 サンプル: 非常にシンプルなバックアップスクリプト(改良版) 12.3 ファイル名変更スクリプト 12.4 ファイル名変更スクリプト(簡易版) 13. うまくいかない時(デバッグ) 13.1 bash の呼び出し方 14. この文書について 14.1 (無)保証について 14.2 翻訳について 14.3 謝辞 14.4 改訂履歴 14.5 参考ホームページ 14.6 日本語訳について ______________________________________________________________________ 1. イントロダクション 1.1. 最新バージョンの入手場所 http://www.linuxdoc.org/HOWTO/Bash-Prog-Intro-HOWTO.html 1.2. 必要事項 GNU/Linux のコマンドラインに違和感がなく、プログラムについての基本的な 考え方が身についていると理解しやすいと思います。しかし、この冊子はプロ グラミングの技術書ではないので、多くの基本概念について説明を加えて(あ るいは少なくともその努力をして)います。 1.3. この文書の使い方 この文書は、次のような場合に役立つことを念頭に置いて書かれています。 o プログミングがどういうものか知っていて、これからシェルスクリプトの コーディングを始めようとしている場合。 o シェルプログラミングについて漠然としか知らないので、参照できる文献 が欲しい場合。 o 自分で書き始めるにあたって、実際のスクリプトとそのコメントをいくつ か見たい場合。 o DOS/Windows 環境から移行しようとしていて(あるいはしてしまって)、 「バッチ処理」を実現したい場合。 o いわゆる完全に「オタク(nerd)」と呼ばれるひとで、片っ端から HOW-TO 本を読んでいる場合。 2. 非常にシンプルなスクリプト この HOW-TO は、例題に重点を置いて、シェルスクリプトのプログラミングに 関するヒントを、述べようとしています。 この章では、いくつかのテクニックを理解するのに役立つような簡単なスクリ プトを取り上げます。 2.1. 伝統の Hello World スクリプト #!/bin/bash echo Hello World このスクリプトは、二行しかありません。一行目は、ファイルを実行するのに どのプログラムを使うかをシステムに指示しています。 二行目は、このプログラムが実行する唯一の命令が書かれていて、それによっ て端末に "Hello World" と表示されます。 もしかすると、./hello.sh: Command not found. と表示されたかもしれませ ん。おそらく、最初の行の "#!/bin/bash" が正しくないためでしょ う。'whereis bash' と打つか、``「bash を探す(10.3)」''を見るかして、そ の行の書き方を確認してください。 2.2. 非常にシンプルなバックアップスクリプト #!/bin/bash tar -cZf /var/my-backup.tgz /home/me/ このスクリプトでは、ターミナルにメッセージを表示するのではなく、ユーザ のホームディレクトリの内容を圧縮ファイル(tarball)にまとめます。これは 実際の使用を意図したものではありません。もっと便利なバックアップスクリ プトを、この文書で後ほど紹介します。 3. リダイレクトションについて(redirection) 3.1. 理論と簡単なリファレンス bash には、ファイル記述子(file descriptor)として、標準入力 (stdin)、標 準出力(stdout)、そして標準エラー(stderr) の 3 つがあります。 ( std は standard の意味です。) 基本的に以下のことができます。 1. stdout を file にリダイレクト 2. stderr を file にリダイレクト 3. stdout を stderr にリダイレクト 4. stderr を stdout にリダイレクト 5. stderr と stdout を file にリダイレクト 6. stderr と stdout を stdout にリダイレクト 7. stderr と stdout を stderr にリダイレクト (リダイレクト構文では)数字の 1 は stdout を表し、 2 は stderr を表しま す。 *上記の項目それぞれについての簡単なメモ* less コマンドを使うと、 stdout (これはバッファに残ります)と stderr (スクリーンには表示されます が、そのバッファを見ようとすると消去されてしまいます)の両方を見ること ができます。 3.2. サンプル: stdout を file へ 次の場合、プログラムの出力がファイルに書き込まれます。 $ ls -l > ls-l.txt 上記では ls-l.txt というファイルが作成されます。そして、'ls -l' という コマンドを実行した場合にスクリーンに表示される内容が、そのファイルに保 存されます。 3.3. サンプル: stderr を file へ 次の場合、プログラムの stderr の出力がファイルに書き込まれます。 $ grep da * 2> grep-errors.txt 上記では、grep-errors.txt というファイルが作成されます。そして、その ファイルには、'grep da *' の出力のうち、読者が見ようとしているはずの stderr の部分が書き込まれています。 3.4. サンプル: stdout を stderr へ 次の場合、プログラムの stderr 出力が、stdout と同一のファイル記述子に 書き込まれます。 $ grep da * 1>&2 上記では、コマンドの stdout の部分が stderr に送られます。別のやり方が あることに読者は気付いているかもしれません。 3.5. サンプル: stderr から stdout へ 次の場合、プログラムの stderr 出力が、stdout と同一のファイル記述子に 書き込まれます。 $ grep * 2>&1 上記では、コマンドの stderr の部分が stdout に送られます。したがって、 もしパイプを使って less に流せば、通常なら stderr に書き込まれて消えて しまうはずの出力行が、 stdout に送られたことで保持されているのを確認で きます。 3.6. サンプル: stderr と stdout から file へ 次の場合、プログラムの全ての出力がファイルに入れられます。これは、完全 に黙ってコマンドを実行させたいような cron の実行項目を書く場合に適して います。 $ rm -f $(find / -name core) &> /dev/null cron プログラムの実行項目を思い浮かべてほしいのですが、上記は、あらゆ るディレクトリから "core" と呼ばれるファイルをすべて削除するものです。 コマンドの出力を見えなくしてしまう場合は、そのコマンドがどういう処理を するのか充分認識しておかなければいけません。 4. パイプ (pipe) この章では、簡単かつ実用的なパイプの使い方とその有用性について説明しま す。 4.1. 何なのか、どこがいいのか 簡単にいうと、パイプを使えば、プログラムの出力を他のプログラムの入力と して利用できるようになります。 4.2. サンプル: sed を使った簡単なパイプ処理 次の例は簡単なパイプの使い方を示すものです。 $ ls -l | sed -e "s/[aeio]/u/g" 上記では、次のことが起こります。先ずコマンド 'ls -l' が実行され、その 出力が画面に表示される代わりに、sed プログラムに送られ (pipe 処理さ れ)、今度は sed がそれを処理して画面に表示します。 4.3. サンプル: 'ls -l *.txt' の代替コマンド おそらく次は、'ls -l *.txt' とするよりも難しい方法です。これを例題に取 り上げるのは、ファイルをリストアップするときのジレンマを解消するためで はなく、単にパイプの使い方を説明したいからです。 $ ls -l | grep "\.txt$" 上記では、'ls -l' プログラムの出力が grep プログラムに送られ、そこで "\.txt$" の正規表現に合致する行が画面に出力されます。 (訳注: 上記で円 マークが表示される場合、それはバックスラッシュの意味です。) 5. 変数 (variable) 変数を使えないプログラム言語はありません。ただ、bash にはデータ型(data type)がありません。bash の変数は、数字、文字、文字列を格納することがで きます。 変数を宣言する必要はなく、参照すべき変数に値を代入すれば、それだけで作 成できます。 5.1. サンプル: 変数を使った Hello World! #!/bin/bash STR="Hello World!" echo $STR 2 行目で STR という変数を作って、それに "Hello World" という文字列を代 入しています。この変数の「値」は変数の最初に "$" を置くことで取り出せ ます。もし "$" という記号を使わなかったらプログラムの出力は違うものと なり、望んだ結果を得られないということを理解して(あるいは実際にやって みて)ください。 5.2. サンプル: 非常にシンプルなバックアップスクリプト(改良版) #!/bin/bash OF=/var/my-backup-$(date +%Y%m%d).tgz tar -cZf $OF /home/me/ 上記のスクリプトは、今までとは少し違うことを紹介しています。まず二行目 にある変数の作成と代入についてはもう慣れたと思います。では、"$(date +%Y%m%d)" という表現に注目してください。スクリプトを実行すると、丸カッ コのなかのコマンドが実行されて、その出力が取り込まれるようになっていま す。 このスクリプトでは、date コマンドの(+%Y%m%d)という書式スイッチに従っ て、出力されるファイル名が毎日変わることに注意してください。違った書式 を指定することで、これとは異なるファイル名にすることもできます。 それ以外の参考事例として、 o echo ls o echo $(ls) といったものがあります。 5.3. ローカル変数 ローカル変数は、local というキーワードを使って作成することができます。 #!/bin/bash HELLO=Hello function hello { local HELLO=World echo $HELLO } echo $HELLO hello echo $HELLO ローカル変数の使い方については、以上の例で充分かと思います。 6. 条件制御(conditionals) 条件制御を使えば、ある命令を実行をすべきかどうかの決定ができます。そし て、この決定は、式の評価によりなされます。 6.1. セオリー 条件制御には、多くの形式があります。最も基本的な形式は、「if 条件式 then 実行文」 であり、この実行文は条件式の評価が真であるときだけ実行さ れます。" 2 < 1 " というのは偽と評価される条件式であり、" 2 > 1 " とい うのは真と評価される条件式です。 また、条件制御は、「if 条件式 then 実行文1 else 実行文2 」という形式も とります。ここでは、条件式が真のとき実行文 1 が実行され、それ以外のと きは実行文 2 が実行されます。 条件制御にはさらに別の形式もあります。「if 条件式1 then 実行文1 else if 条件式2 実行文 2 else 実行文3 」 です。この形式では、 "else if 条 件式2 then 実行文2 " というのが加わっただけですが、これはもし条件式2 の評価が真ならば実行文2 を実行するものです。その他の部分については、 読者の想像通りです。(これまでの形式をご覧ください) 構文に関してですが、 bash で if 制御構造を使うときの基本は以下のような ものです。 if [条件式] then (if の条件が真だったときの)実行コード fi 6.2. サンプル: if .. then による基本的な条件制御の例 #!/bin/bash if [ "foo" = "foo" ]; then echo expression evaluated as true fi ブレース [ ] 内の条件式が真であった場合、"then" という文字の後に、実行 されるコードが書かれています。そしてその後に、条件制御に基づいて実行さ れるコードの終了を指示する "fi" が置かれています。 6.3. サンプル: "if .. then ... else" を使った基本的な条件制御の例 #!/bin/bash if [ "foo" = "foo" ]; then echo expression evaluated as true else echo expression evaluated as false fi 6.4. サンプル: 変数を伴った条件制御 #!/bin/bash T1="foo" T2="bar" if [ "$T1" = "$T2" ]; then echo expression evaluated as true else echo expression evaluated as false fi 7. for, while, until によるループ この章では、for, while, そして util によるループについて述べます。 bash の for は、他の言語での使い方と若干異なります。基本的には、ある文 字列内の単語をあたまから順に反復処理するものです。 while は、制御式が真の間はコードを実行していて、偽になったとき (あるい は実行コード内に break が明示されている場合)にのみ停止します。 until ループは、制御式が偽と評価されている間はコードが実行されるという 点を除くと、while ループとほぼ同じです。while と until が似ていると 思ったなら、それは正しい考えです。 7.1. For のサンプル #!/bin/bash for i in $( ls ); do echo item: $i done 二行目で、変数 i が宣言されています。これには、$( ls ) によって出力さ れる様々な値が(順番に)代入されていきます。三行目は、必要であればもっと 長くできます。すなわち、四行目の done の前に複数の行が存在してもかまい ません。四行目の done は、変数 $i の値を使用していたコードがそこで終了 したことを示していています。したがって、$i に新しい値を代入することが 可能です。上記は、はほとんど意味がないスクリプトです。for ループを使用 するもっと有益な方法は、以前の例題にあったように、ある種のファイルだけ を選択的に処理する場合に使うことだと思います。 7.2. C 言語に似た for Fiesh さんから以下のような形式のループを付け加えたらどうかと言われまし た。この for ループは、C 言語や Perl の for ループに類似した使われかた をしています。 #!/bin/bash for i in `seq 1 10`; do echo $i done 7.3. while のサンプル #!/bin/bash COUNTER=0 while [ $COUNTER -lt 10 ]; do echo The counter is $COUNTER let COUNTER=COUNTER+1 done 上記のスクリプトは、(C, Pascal, Perl といった)有名な言語の for の制御 構造を真似たものです。 7.4. until のサンプル #!/bin/bash COUNTER=20 until [ $COUNTER -lt 10 ]; do echo COUNTER $COUNTER let COUNTER-=1 done 8. 関数(functions) ほとんどどんなプログラムに当てはまることですが、関数(function)を使え ば、より論理的に一群のコードをグループ化することができます。また、神秘 的ともいえる再帰法(recursion)を使うことも可能になります。 関数の宣言は、 "function my_func { my_code }" と書くだけでできます。 関数の呼び出しは、他のプログラムの呼び出しと同じで、その関数名を書くだ けです。 8.1. 関数のサンプル #!/bin/bash function quit { exit } function hello { echo Hello! } hello quit echo foo 二行目から四行目にかけては、"quit" という関数が記述されています。五行 目から七行目にかけては、"hello" という関数が記述されています。このスク リプトが何をしているか完全に把握できないなら、実際に自分で書いて、実行 してみてください。 関数は、何らかの特定の様式による宣言を必要としないということに注意して ください。 このスクリプトを実行すると、最初に "hello" 関数が呼び出され、次に "quit" 関数が呼び出されます。そしてプログラムは十行目までは実行されな いことに気付くでしょう。 8.2. パラメーターを取る関数のサンプル #!/bin/bash function quit { exit } function e { echo $1 } e Hello e World quit echo foo 上記のプログラムは、前記のプログラムとほぼ同一です。主な違いは、関数 "e" の存在です。この関数は受け取った第一引数を表示します。引数は、関数 の内部にあるときでも、スクリプトに直接渡したときと同じように扱われま す。 9. ユーザインターフェイス 9.1. 簡単なメニューを作るために select を使う #!/bin/bash OPTIONS="Hello Quit" select opt in $OPTIONS; do if [ "$opt" = "Quit" ]; then echo done exit elif [ "$opt" = "Hello" ]; then echo Hello World else clear echo bad option fi done もし上記のスクリプトを実行したなら、テキストベースのメニューというプロ グラマのひとつの夢が実現しているのが分かると思います。また for の制御 構造と似ていることにも気付いたかもしれません。$OPTIONS に代入された語 (word)をひとつずつループさせる代わりに、プロンプトを出力しているという 違いがあるだけだからです。 9.2. コマンドラインを使ってみる #!/bin/bash if [ -z "$1" ]; then echo usage: $0 directory exit fi SRCD=$1 TGTD="/var/backups/" OF=home-$(date +%Y%m%d).tgz tar -cZf $TGTD$OF $SRCD 上記のスクリプトが何をするかは、もうお分かりでしょう。最初の条件制御の 式は、プログラムが引数 ($1)を受け取ったかどうかを調べるものです。そし て、引数を受け取らなかったときは簡単な使用方法についてのメッセージを表 示して終了します。スクリプトの残りの部分は、この時点では明瞭だと思いま す。 10. その他のスクリプト 10.1. read を使ってユーザの入力を読み込む ユーザに何らかの入力を促したいと思う場合がよくあるものです。それを実現 する方法はいくつか存在しますが、以下はそれらのうちのひとつです。 #!/bin/bash echo Please, enter your name read NAME echo "Hi $NAME!" 上記の変形として、read を使って複数の値を受け取ることもできます。次の 例題はそれを明らかにするものです。 #!/bin/bash echo Please, enter your firstname and lastname read FN LN echo "Hi! $LN, $FN !" 10.2. 数値の評価 コマンドライン(あるいはシェル)で、次のように打ってみてください。 $ echo 1 + 1 2 が出力されると期待していたなら、がっかりしたことでしょう。 bash に数 値を計算させたいときは、どうしたらいいのでしょう?解決策は次のようにす ることです。 $ echo $((1 + 1)) 上記では、よりロジカルな出力がなされたことと思います。これは数式を評価 したということです。また、次のような表現でも同じことができます。 $ echo $[ 1 + 1 ] 分数やあるいはもっと複雑な数学的処理が必要になった場合、あるいは数式で 遊びたいときなどは、数式評価のための bc というコマンドが使えます。コマ ンドプロンプト上で "echo $[3/4]" と実行しても、bash は整数でしか答えを 返せないので、結果は 0 となります。しかし、"echo 3/4 | bc -l" と実行す れば、0.75 という正しい答えが返ってきます。 10.3. bash を探す Mike からのメッセージを掲載します。(謝辞を見てください) あなたはいつも #!/bin/bash というのを使っていますが、bash がどこにある のか探す方法を例示したほうがいいと思います。"bash をその場所に置いてし まえばいいのですが、すべてのマシンがそれをしているわけではありません。 ルートディレクトリから、'find ./ -name bash' を実行すれば通常は探し出 せるはずです。 チェックすべきなのは、次のような場所でしょう。 ls -l /bin/bash ls -l /sbin/bash ls -l /usr/lodal/bin/bash ls -l /usr/bin/bash ls -l /usr/sbin/bash ls -l /usr/local/sbin/bash (これ以外のディレクトリはすぐに思い浮かびません... システムが違って も、これまではこの中から見つかる場合がほとんどでした。) それか ら、'which bash' を使ってみるのもいいと思います。 10.4. プログラムの戻り値を獲得する方法 bash では、プログラムの戻り値を $? という特殊な変数に入れておくことが できます。以下は、プログラムの戻り値を獲得する方法を書いたものです。た だし、 /dada というディレクトリは実在しません。(これも Mike からの提案 を載せたものです) #!/bin/bash cd /dada &> /dev/null echo rv: $? cd $(pwd) &> /dev/null echo rv: $? 10.5. コマンド出力を獲得する方法 以下の簡単なスクリプトは、すべてのデータベース上を探して全部のテーブル を表示するというものです( MySQL がインストールされていることが前提で す)。この中の 'mysql' というコマンド部分を変更して、有効なユーザ名とパ スワードが使えるようにしてください。 #!/bin/bash DBS=`mysql -uroot -e"show databases"` for b in $DBS ; do mysql -uroot -e"show tables from $b" done 10.6. 複数のソースファイル コマンドソースとして複数のファイルを使うことができます。 作成予定( TO DO ) 11. 参照テーブル 11.1. 文字列比較演算子 (1) s1 = s2 s1 と s2 が同一 (2) s1 != s2 s1 と s2 が同一でない (3) s1 < s2 作成中 (4) s1 > s2 作成中 (5) -n s1 s1 が null でない(一文字以上を含んでいる) (6) -z s1 s1 が null である 11.2. 文字列比較の実例 ふたつの文字列を比較する #!/bin/bash S1='string' S2='String' if [ $S1=$S2 ]; then echo "S1('$S1') is not equal to S2('$S2')" fi if [ $S1=$S1 ]; then echo "S1('$S1') is equal to S1('$S1')" fi ここで、上記の if [ $1 = $2 ] に関して Andrea Beck が送ってくれたメー ルの一文を引用します。 あまりいい考えとは言えません。もし、$S1 か $S2 のどちらかが空の場合、 パーサエラーになるからです。x$1=x$2 か "$1"="$2" としたほうがよいで しょう。 11.3. 数値演算子 + - * / % (剰余) 11.4. 数値関係演算子 -gt ( > ) -le ( <= ) -ge ( >= ) -eq ( == ) -ne ( != ) C プログラマが使っている演算子についても、丸カッコを付けて対応関係を示 しました。 11.5. 便利なコマンド このセクションは Kees (謝辞を見てください)によってリライトされたもので す。 以下のコマンドのなかには、完全なプログラミング言語を内包しているといえ そうなものもあります。それらのコマンドを取り上げて、基本的用法だけを説 明しよと思います。詳細な解説については、コマンドのマニュアルページをみ てください。 11.5.1. sed (stream editor) sed はノン・インタラクティブなエディタです。カーソルを画面上で動かしな がらファイルに変更を加えるかわりに、編集に関する指示と編集すべきファイ ル名を書き込んだスクリプトを使用します。また、sed は一種のフィルタであ ると考えることもできます。いくつか例を見てみましょう。 $ sed 's/to_be_replaced/replaced/g' /tmp/dummy 上記において、sed は、/tmp/dummy というファイルを読み込んで、文字列 "to_be_replaced" を文字列 "replaced" に置換します。その結果は標準出 力(通常はコンソール)に出力されますが、上記コマンドラインの末尾に(例え ば) '> capture' と付け加えるなら、 "capture" という名前のファイルに sed の出力結果を送ることもできます。 $ sed 12, 18d /tmp/dummy 上記の例で、sed は 12 から 18 行目までを除く全ての行を表示します。この コマンドによって元のファイルに変更が加えられることはありません。 11.5.2. awk (データファイルやテキストの検索と整形処理) AWK プログラミング言語には多くの実装が存在します。(最も有名なインター プリタは、GNU の gawk と「新しい awk」 である mawk です。) AWK の原理 は単純です。パターンを探して、合致したパターン(pattern)全てに対してな んらかの処理(action)を行うということです。 再度、以下のような行を含むダミーファイルを作りました。 test123 test tteesstt $ awk '/test/ {print}' /tmp/dummy test123 test 上記において、AWK が探そうとしたパターン(pattern)は "test" であ り、/tmp/dummy ファイルの行の中から "test" という文字列を探し出したと きに行った処理(action)が画面への表示 "print" です。 $ awk '/test/ {i=i+1} END {print i}' /tmp/dummy 3 数多くのパターンを探そうとしているときは、クオテーション('')で囲まれた 文字列を(例えば) '-f file.awk' といったファイル名で置き換えます。そう すれば、すべてのパターンと処理を 'file.awk' というファイルに書き込ん で、まとめて実行できます。 11.5.3. grep (検索パターンに合致する行の出力) grep コマンドの、あるパターンに合致する行を表示する機能については、こ れまでの章でいくつも見てきたと思います。しかし、grep の機能はそれだけ ではありません。 $ grep "look for this" /var/log/messages -c 12 上記の例は、"look for this" という文字列が /var/log/messages ファイル のなかで 12 回見つかったことを意味しています。 [ OK, この例題はフェイクなんだ。/var/log/messages にちょっと手を加えと いたのさ。 :-)] 11.5.4. wc (行、語、バイト数を数えます) 以下の例題では、出力が期待通りになっていないのが分かると思います。この 例題で使われたダミーファイルには(前に使った内容とは異なる)次のような文 字列が含まれていたからでらす。 bash introduction howto test file $ wc --words --lines --bytes /tmp/dummy 2 5 34 /tmp/dummy wc ではパラメータ指定の順番は自由ですが、それらの出力時の順番はいつも 決まっています。上記のように、行数、語数、バイト数、ファイル名 の順番 になります。 11.5.5. sort (テキストファイル内の行の並べ替え) 今回のダミーファイルの内容は次のようになっています。 b c a $ sort /tmp/dummy 次は、そのアウトプットがどう表示されるかです。 a b c コマンドを使うとすると、これほど簡単にはいかないでしょう。 11.5.6. bc (数値計算用のプログラミング言語) bc はコマンドラインで操作するよくできた計算プログラムです。リダイレク トやパイプからは入力できませんが、ファイルからの入力が可能で、ユーザイ ンターフェイスもあります。以下では、そのコマンドの中からいくつかを実際 にデモンストレーションしてみます。なお、bc 起動時に -q パラメータを使 用しているのは、起動時にウェルカムメッセージを出力させないためです。 $ bc -q 1 == 5 0 5 != 5 0 2 ^ 8 256 sqrt(9) 3 while (i != 9) { i = i + 1; print i } 12345689 quit 11.5.7. tput (端末の初期化や terminfo データベースの検索) tput でなにができるか、簡単に紹介します。 $ tput cup 10 4 上記の例では、プロンプトが (y10,x4) の位置にあらわれます。 $ tput reset 上記では、画面をもとに戻して、(y1,x1) の位置にプロンプトを表示しま す。(y0,x0) がちょうど左上の隅にあたる位置です。 $ tput cols 80 上記では、X 軸方向に文字がいくつ並ぶのかを表示しています。 (少なくとも)上記のようなプログラムについては、使い慣れておくことを強く お薦めします。コマンドライン上の操作で驚くほどの処理をしてくれる小さな プログラムは無数にあります。 [例題のいくつかは、マニュアルページや FAQ から取りました。] 12. いろいろなスクリプト 12.1. ディレクトリ上の全ファイルにコマンドを適用する方法 (作成中) 12.2. サンプル: 非常にシンプルなバックアップスクリプト(改良版) #!/bin/bash SRCD="/home/" TGTD="/var/backups/" OF=home-$(date +%Y%m%d).tgz tar -cZf $TGTD$OF $SRCD 12.3. ファイル名変更スクリプト #!/bin/sh # renna: いくつかのルールに従って、複数のファイルをリネームする # スクリプト: Felix Hudson Jan 2000 # 最初にこのプログラムが取るいくつかの「モード」についてチェックする # 第一引数($1)の条件が一致するなら、プログラムの該当部分を実行して # 終了する # prefix の条件をチェックする if [ $1 = p ] ; then # モード変数($1)と prefix ($2)を取り除く prefix=$2 ; shift ; shift # ファイルが指定されているか簡単にチェックする # 指定されていないときは、存在しないファイルをリネームすることは # できないので、なにもしない if [$1 = ]; then echo "no files given" exit 0 fi # プログラムの引数となるファイルを順にループ処理する # ファイルをひとつずつリネームする for file in $* do mv ${file} $prefix$file done # ここでプログラムを終了する exit 0 fi # suffix をリネームするかチェックする # ここでの残りの部分は上記とおなじ処理なので、そちらを見てほしい if [ $1 = s ]; then suffix=$2 ; shift ; shift if [$1 = ]; then echo "no files given" exit 0 fi for file in $* do mv ${file} $file$suffix done exit 0 fi # 名前の置き換えかどうかをチェックする if [ $1 = r ]; then shift # ユーザが処理の内容を指定しなかったとしてもファイルがダメージを # 受けないように、次のコードを含めた if [ $# -lt 3 ] ; then echo "usage: renna r [expression] [replacement] files... " exit 0 fi # 余計な情報を削除する OLD=$1 ; NEW=$2 ; shift ; shift # この for ループはプログラムに与えられたファイルを順に処理する # sed というプログラムを使って、ファイルをひとつずつリネームする # sed は標準入力を読み込んで、特定の表現を与えられた文字列に置換する # ここでは、標準入力からファイル名を指定して、必要な文字を置換させる for file in $* do new=`echo ${file} | sed s/${OLD}/${NEW}/g` mv ${file} $new done exit 0 fi # ここまで来たとすると、プログラムに引数がなかったということなので、 # 使い方を表示する echo "usage;" echo " renna p [prefix] files.." echo " renna s [suffix] files.." echo " renna r [expression] [replacement] files.." exit 0 # 終了 12.4. ファイル名変更スクリプト(簡易版) #!/bin/bash # renames.sh # basic file renamer criteria=$1 re_match=$2 replace=$3 for i in $( ls *$criteria* ); do src=$i tgt=$(echo $i | sed -e "s/$re_match/$replace/") mv $src $tgt done 13. うまくいかない時(デバッグ) 13.1. bash の呼び出し方 効果的なのは、第一行目にちょっと手を加えることです。 #!/bin/bash -x こうすると、出力に際に面白い情報が表示されます。 14. この文書について 提案や訂正、またこの文書に掲載すべき面白い事柄などなんでもお気軽に連絡 してください。できるだけ早く反映させます。 14.1. (無)保証について This documents comes with no warranty of any kind. and all that (ママ) この文書には、いかなる保証も伴いません。 14.2. 翻訳について イタリア語: by Willy Ghelfi (wizzy at tiscalinet.it) こちら フランス語: by Laurent Martelli ( URL は不明) 韓国語: Minseok Park http://kldp.org 韓国語: Chun Hye Jin ( URL は不明) スペイン語: 訳者不明 http://www.insflug.org もっと多くの翻訳があると思うのですが、情報がありません。御存知のかた は、わたしにメールをください。この章を更新します。 14.3. 謝辞 o この文書を翻訳してくれたひとたち(前章) o Nathan Hurst は詳細に校正してくれました。 o Jon Abbott は数式の評価についてコメントを送ってくれました。 o Felix Hudson は renna スクリプトを書いてくれました。 o Kees van den Broek (校正に協力し、便利なコマンドの章をリライトして くれました) o Mike (pink) は bash の位置を特定したりファイルをテストしたりするこ とについていくつか提案してくれました。 o Fiesh はループの章についていい提案をしてくれました。 o Lion はコマンドエラーについて述べるよう提案してくれまし た。(./hello.sh: Command not found.) o Andrea Beck は校正とコメントについて協力してくれました。 14.4. 改訂履歴 o 翻訳が新規に追加され、いくつか訂正を加えた。 o Kees のリライトによる便利なコマンドの章を付け加えた。 o 訂正を提案を大幅に取り入れた。 o 文字列比較について実例を増やした。 o v0.8 バージョン情報を外した。日付で充分だと思う。 o v0.7 訂正箇所多数。TO-DO の章を書き直し。 o v0.6 すこし訂正 o v0.5 リダイレクトの章を追加 o v0.4 前任のボスの意向でそれまでのページで公開できなくなったが、この 文書にとっては最適の場所が見つかった。 o それ以前のことは覚えていない。rcs も cvs も使っていなかった。 14.5. 参考ホームページ ( BeOS での)bash に関する紹介 Bourne Shell のプログラミング 14.6. 日本語訳について 翻訳 千旦 裕司 校正 井上 秀悟 武井 伸光 伊藤 祐一