シェルスクリプトに挑戦しよう(5)スクリプトで引数を使用する[その1]:“応用力”をつけるためのLinux再入門(25)
シェルスクリプトでは、単にコマンドを実行するだけではなく、条件によって処理を変えたり、繰り返し処理を行ったり、また、通常のコマンドのように引数を受け取って、引数に応じた処理を行ったりすることができます。今回は、シェルスクリプトで「引数」を扱う方法を解説します。簡単なスクリプトを加工しながら試してみましょう。
引数の参照
「引数」は「$1」「$2」「$3」……のように「$数値」で参照します。
「$1」が1つ目の引数で、「$2」が2つ目の引数……となります。何番目の引数を参照しているのかを明確にするなどの目的で「{〜}」を使い、「${1}」のように書く場合もあります。
この他、「$0」で実行コマンド名、「$#」で引数の個数を参照できます。まずは、以下のスクリプトで試してみましょう。
#! /bin/bash echo "script=" $0 #スクリプト名を表示 echo "count=" $# #引数の個数を表示 echo "No.1=" $1 #1つ目の引数を表示 echo "No.2=" $2 #2つ目の引数を表示 echo "all=" $@ #全ての引数を表示
$ chmod +x argtest $ ./argtest aaa bbb ccc script= ./argtest count= 3 No.1= aaa No.2= bbb all= aaa bbb ccc
変数 | 定義 |
---|---|
$0 | プログラム名 |
$1 | 1番目の引数 |
$2 | 2番目の引数 |
$n | n番目の引数(nが2桁以上の場合は、${12}のように{}で囲む) |
$@ | 全ての引数 |
$* | 全ての引数($*の場合、ダブルクォーテーションで"$*"とした場合、1つの変数となるのに比べ、"$@"は“個々の引数の配列”となる。引数として空白を含むファイル名などを受け取る可能性がある場合は、"$@"を使用する) |
$# | 引数の個数 |
$? | 最後に実行したコマンドの結果(0は成功。0以外は失敗で、1またはエラーコードが入っていることが多い) |
$$ | 実行中のシェルのプロセスID |
$! | シェルが最後に実行したバックグラウンドプロセスのプロセスID |
$- | シェルの実行オプション |
▲主な定義済みの変数 |
引数を使ってコマンドを実行する
シェルスクリプトを実際に作りながら、引数の使い方を見ていきましょう。
例えば、“カレントディレクトリ下にある「*.txt」というファイルの中で、「diary」と書かれている箇所を探したい“、という処理で考えてみます。
「find」コマンドで対象ファイルを探し、「grep」コマンドを実行するのであれば、「find 場所 -name ファイル名 -exec grep 対象文字列」のように組み立てればよさそうです(例1)。
grepコマンドだけで実行するのであれば、「grep -r --include=ファイル名 検索文字列 場所」でできそうです(例2)。
なお、以下の例では、grepコマンドで、対象ファイルが1個だった場合もファイル名が表示されるように「-H」オプションと、場所が分かりやすいように行番号を表示する「-n」オプションを使っています。
カレントディレクトリ下の「*.txt」から「diary」と書かれている行を探すコマンドライン
(例1)find . -name "*.txt" -exec grep -Hn "diary" {} \;
(例2)grep -Hn -r --include="*.txt" diary .
今回は、例2のgrepコマンドだけを使う方法で、「grepall」というスクリプトを作成してみましょう。
●引数を使用する順番について考える
スクリプトを実行する際、コマンドラインで指定する引数と、スクリプト内のコマンドで使用する引数の順番は自由に変えることができます。
今回作成するスクリプト「grepall」の場合、引数で指定したいのは「検索文字列」と「ファイル名」です。また、カレントディレクトリ以外のファイルも探したくなるかもしれないので、「場所」も設定できるようにしましょう。
先ほど確認したgrepコマンドの引数に当てはめると、「grep -r --include=ファイル名 検索文字列 場所」になります。この「ファイル名」「検索文字列」「場所」を、単純に左から「$1」「$2」「$3」とした場合、スクリプトを実行する際のコマンドラインは「grepall "*.txt" diary .」となります(例1)。
しかし、grepコマンドを普段使う場合のイメージで考えると、コマンドラインでは検索文字列を先にした「grepall diary . "*.txt"」(例2)、あるいは「grepall diary . "*.txt"」(例3)のような指定の方が使いやすそうです。
引数を指定する順番とスクリプトの内容
(例1)「grepall "*.txt" diary .」で実行するスクリプトの場合
grep -Hn -r --include="$1" "$2" "$3"
(例2)「grepall diary . "*.txt"」で実行するスクリプトの場合
grep -Hn -r --include="$3" "$1" "$2"
(例3)「grepall diary "*.txt" . 」で実行するスクリプトの場合
grep -Hn -r --include="$2" "$1" "$3"
●引数を省略できるようにする
引数の順番は、指定のしやすさの他に、「省略できるようにする」という観点からも考えることができます。
今回は「場所を指定しなかった場合はカレントディレクトリを探す」として、「場所」を省略しても使えるようにしてみましょう。それには、「grepall 検索文字列 ファイル名 場所」のように、「場所」を最後に指定するようにします。
従って、スクリプト内でのgrep行は、「grep -Hn -r --include="$2" "$1" "$3"」となります。ただ、この書き方だと「$1」と「$3」の役割が分かりにくいので、検索文字列を明示するために「-e」オプションを使い、「grep -Hn -r --include="$2" -e "$1" "$3"」とします。順番を入れ替えて「grep -Hn -r -e "$1" --include="$2" "$3"」のようにすると、さらに分かりやすくなります。
以上を踏まえて、grepallというスクリプトを作ると次のようになります。なお、今回と次回で、このスクリプトを加工しながらシェルスクリプトのさまざまな書き方を紹介します。
#! /bin/bash grep -Hn -e "$1" -r --include="$2" "$3"
$ chmod +x grepall $ ./grepall diary "*.txt" ~/Documents ← ホームディレクトリの「Documents」フォルダにある全ての「*.txt」を対象に、「diary」という文字列をgrepコマンドで探す
【※】上記スクリプトの実行結果では、コマンドラインでの入力を簡略化するために「diary」と「.」の引用符を省略しています。カレントディレクトリには「*.txt」に該当するファイルが存在する可能性があるため、「"*.txt"」の引用符は省略していません。
次回は、このスクリプトを加工して、引数の個数によって処理内容を変化させるようにしてみます。また、スクリプトの簡単な使い方も表示できるようにします。
筆者紹介
西村 めぐみ(にしむら めぐみ)
PC-9801NからのDOSユーザー。PC-486DX時代にDOS版UNIX-like toolsを経てLinuxへ。1992年より生産管理のパッケージソフトウェアの開発およびサポート業務を担当。著書に『図解でわかるLinux』『らぶらぶLinuxシリーズ』『Accessではじめるデータベース超入門[改訂2版]』『macOSコマンド入門』など。2011年より、地方自治体の在宅就業支援事業にてPC基礎およびMicrosoft Office関連の教材作成およびeラーニング指導を担当。
Copyright © ITmedia, Inc. All Rights Reserved.