Linux講座にようこそ。このページは「Linuxの使い方 - 第6章 シェルスクリプトの作り方」です。

Linuxの使い方

6. シェルスクリプトの作り方(3/8)

6.4 位置パラメータ(引数の取り扱い)

UNIX系OSのコマンドは引数としてオプションやパス名を指定できますが、シェルスクリプトでも同じように引数を指定できます。引数で指定した値はシェルスクリプト中では位置パラメータとして受け取ります。位置パラメータは前述した通り、1〜nの数字で表し、$n又は${n}の形式で値を参照します。

位置パラメータと関連する特殊パラメータには次のものがあります。

【表6-9】特殊パラメータ一覧
変数内容
$0コマンド名または、シェルスクリプト名(パス名)を値として持っています。
$#引数の数を値として持っています。
$*全ての引数を空白で区切り、まとめて1つの文字列とした値を持っています。
$@全ての引数を個別の値として持っています。

6.4.1 例題

例題1

位置パラメータの値を確認します。

$ set Param1 Param2 Param3 Param4 ← 位置パラメータ1から4に値を設定します
$ echo $0
/bin/bash ← コマンド名(bash)です
$ echo $1 ← 位置パラメータ1の値を表示します
Param1
$ echo $2
Param2
$ echo $3
Param3
$ echo $4
Param4
$ echo $# ← 値が設定されている位置パラメータの数を表示します
4
$ echo $*
Param1 Param2 Param3 Param4 ← 全ての位置パラメータの値です
$ echo $@
Param1 Param2 Param3 Param4 ← 全ての位置パラメータの値です
$ set "$*" ← set "$1 $2 $3 $4"と同じです
$ echo $1
Param1 Param2 Param3 Param4 ← 位置パラメータ1に全ての位置パラメータの値が設定されています
$ set Param1 Param2 Param3 Param4
$ set "$@" ← set "$1" "$2" "$3" "$4"と同じです
$ echo $1
Param1 ←  位置パラメータ1には"$1"の値が設定されています
$ echo $2
Param2
$

例題2

引数で指定したディレクトリのバックアップを取ります。(ex20.sh)

#!/bin/bash
# 機能  :  引数で指定したディレクトリの下のバックアップを~/BACKUPに取ります
# 作成  :  メリー
 
cd $1                                           # 第1引数の値をカレントディレクトリに設定
tar -cjvf ~/BACKUP/backup.tar.bz2 .              # バックアップ取得
 
echo "バックアップ完了"
$ ex20.sh $HOME/KANRI ← 引数を指定します
./
./manpo.ksp
./kakei.ksp
バックアップ完了
$

例題3

ディレクトリ(位置パラメータ1)の指定が無かったらメッセージを出力して中止します。(ex21.sh)

#!/bin/bash
# 機能  :  引数で指定したディレクトリの下のバックアップを~/BACKUPに取ります
# 作成  :  メリー
 
bkdir=${1:?"バックアップするディレクトリの指定がありません。中止します。"}
 
cd $bkdir
tar -cjvf ~/BACKUP/backup.tar.bz2 .              # バックアップ取得
 
echo "バックアップ完了"
$ ex21.sh ← ディレクトリの指定をしないで実行します
バックアップするディレクトリの指定がありません。中止します。 ← エラーメッセージです
$

6.5 文字列のエスケープ

シェルはユーザが入力した文字列を解釈して、さまざまなことを行っていますが、文字には特別な意味を持つ記号がいくつかあります。例えば、次のような記号が該当します。(全てではありません)

半角スペース タブ * ? [ ] > < ; | & $ { } ( ) #

これらの記号を単なる文字として扱いたい場合は特別な指定が必要です。例えば、「* Hello *」のメッセージを出力したい場合、単にecho * Hello *では期待した結果になりません。

$ ls * ← カレントディレクトリの下の全ファイルとディレクトリを表示します(単に、lsだけでも同じです)
ex01.sh  ex02.sh  ex03.sh  ex10.sh  ex20.sh  ex21.sh  loop.c  loop.prg
$
$ echo * ← 引数の*はシェルにより、カレントディレクトリの下の全ファイルとディレクトリに置き換えられます
ex01.sh ex02.sh ex03.sh ex10.sh ex20.sh ex21.sh loop.c loop.prg
$ 
$ echo * Hello *
ex01.sh ex02.sh ex03.sh ex10.sh ex20.sh ex21.sh loop.c loop.prg Hello 
ex01.sh ex02.sh ex03.sh ex10.sh ex20.sh ex21.sh loop.c loop.prg
$

単に「*」だけを指定したのでは、シェルは「*」を「任意の長さの任意の文字列」と解釈してしまいます。ここでは「*」を単なる文字として扱いたいので、その旨、シェルに伝える必要があります。このことをエスケープと呼んでおり、次のようなエスケープを表す記号があります。

"(引用符)
変数を認識します。
'(アポストロフィ)
変数を認識しません。(全ての文字を単なる文字として取り扱います)
\(バックスラッシュ)
後ろの1文字を文字として認識します。

先程の、echo * Hello *の場合であれば、引用符かアポストロフィで括ります。

$ echo '* Hello *' ← アポストロフィで括ってエスケープします
* Hello *
$

また、半角スペースはコマンド引数の区切り記号ですので、単に半角スペースを含むパス名を指定すると、その半角スペースは区切り記号として解釈されてしまいますので、この場合もエスケープが必要です。

$ ls
'My Documents'  'Program Files'   SCRIPT ← 半角スペースを含むディレクトリがあります
$
$ cd My Documents ← エスケープしていません
bash: cd: 引数が多すぎます ← cdコマンドのエラーメッセージです
$
$ cd 'My Documents' ← アポストロフィで括ってエスケープします
$ pwd
/home/merry/TMP/WORK/DOC/My Documents
$

6.5.1 例題

例題1

アポストロフィと引用符の相違を確認します。

$ name=メリー
$
$ echo '*** 今日は${name}さん ***' ← アポストロフィで括ります
*** 今日は${name}さん ***
$
$ echo "*** 今日は${name}さん ***" ← 引用符で括ります
*** 今日はメリーさん ***
$
$ echo \* Hello \* ← *をバックスラッシュでエスケープします
* Hello *
$

例題2

バックアップしたディレクトリのパス名をメッセージとして表示します。(ex30.sh)

#!/bin/bash
# 機能  :  引数で指定したディレクトリの下のバックアップを~/BACKUPに取ります
# 作成  :  メリー
 
bkdir=${1:?"バックアップするディレクトリの指定がありません。中止します。"}
 
cd "$bkdir"                                            # 引用符で括ってエスケープ
tar -cjvf ~/BACKUP/backup.tar.bz2 .                     # バックアップ取得
 
echo "*** ${bkdir}以下のバックアップをとりました ***"  # 引用符で括ってエスケープ
$ ls
'My Documents'  'Program Files'   SCRIPT
$
$ ex30.sh ./SCRIPT
./
./loop.prg
./ex02.sh
./ex10.sh
./loop.c
./ex20.sh
./ex03.sh
./ex21.sh
./ex01.sh
*** ./SCRIPT以下のバックアップをとりました ***
$
$ ex30.sh './My Documents'
./
./財産目録.ods
./精算表.ods
./貸借対照表.ods
./合計残高資産表.ods
./総勘定元帳.ods
*** ./My Documents以下のバックアップをとりました ***
$

例題3

コマンドを実行するときにもエスケープが必要な場合があります。

$ grep #! *.sh ← 拡張子が".sh"のファイルから、「#!」を含む行を表示します
使用法: grep [オプション]‥‥ 文字列パターン [ファイル]‥‥ ← grepの構文エラーです
詳しくは`grep --help'を実行してください。
$
$ grep '#!' *.sh ← アポストロフィで括ってエスケープします
ex01.sh:#!/bin/bash
ex02.sh:#!/bin/bash
ex03.sh:#!/bin/bash
ex10.sh:#!/bin/bash
ex20.sh:#!/bin/bash
ex21.sh:#!/bin/bash
ex30.sh:#!/bin/bash
$