最近 他の人の作ったシェルスクリプトをメンテナンスする機会があり、変数操作(記載)で初めて見る記載方法がいくつかありました。シェルスクリプトは昔から触っていたのですが、独学で書いていたので、体系的に学んだことがありませんでした。基本である変数の記載方法でさえ知らないことが結構あり、シェルスクリプトの独特な記載のため「呪文」の様に見えることもしばしばで、検索にも苦労しましたので自分のためにもここにまとめておきます。
今回紹介するのは、bash で利用可能な「特殊な変数」と「変数の操作」についてです。初心者の方や勉強中の方、ど忘れしてしまった方などの役立てば幸いです。
動作は、すべて bash ( GNU bash, バージョン 4.2.46(2)-release ) で確認しています。
特殊な変数
シェルスクリプトでは以下の変数は特殊に扱われます。
以下のうち、$* と $@ はダブルクォーテーション(“)で括られていない場合は違いはありません。$* と $@ がダブルクォーテーション(“)で括られている場合に挙動が変わります。$*は “1st 2nd 3rd” と一つの文字列として展開されますが、$@は”1st” “2nd” “3rd”とダブルクォーテーションで囲まれた三つの文字列として扱われます。
変数 | 意味 |
---|---|
$0 | シェルスクリプトの名前 |
$1~$9 | スクリプトの引数(1番目の引数は$1、2番目の引数は$2… となる) |
$# | スクリプトの引数の数 |
$@ | 全ての引数をまとめる |
$* | 全ての引数をまとめて「1つ」として処理 |
$? | 直前実行したコマンドの終了値(0は成功、1は失敗) |
$$ | このシェルスクリプトのプロセスID |
$! | 最後に実行したバックグラウンドプロセスID |
動作確認
以下のスクリプトを、vartest.sh として作成し、実行してみます。
func1 () {
local arg1=$1
local arg2=$2
local arg3=$3
echo func1/arg1 : $arg1
echo func1/arg2 : $arg2
echo func1/arg3 : $arg3
}
echo "\$0(スクリプト名): $0"
echo "\$1(1番目の引数): $1"
echo "\$2(2番目の引数): $2"
echo "\$#(引数の数): $#"
echo "--------------"
echo "\$*: "$*""
echo "\$@: "$@""
echo Call func1 --- \$* ---
func1 $*
echo Call func1 --- "\$*" ---
func1 "$*"
echo Call func1 --- \$@ ---
func1 $@
echo Call func1 --- "\$@" ---
func1 "$@"
echo "--------------"
echo "一つ前のコマンドの終了コード : $?"
error "No such command" > /dev/null 2>&1
echo "一つ前のコマンドの終了コード : $?"
echo "このプロセスID : $$"
echo "background" > /dev/null 2>&1 &
echo "バックグラウンドプロセスID : $!"
実行結果
第一引数に「1st」を 第二引数に「”2nd second”」を渡して実行してみます。func1に $*,”$*”,$@,”$@” が渡った後の動きの違いに注意しましょう。
$0(スクリプト名): vartest.sh
$1(1番目の引数): 1st
$2(2番目の引数): 2nd second
$#(引数の数): 2
--------------
$*: "1st 2nd second"
$@: "1st 2nd second"
Call func1 --- $* ---
func1/arg1 : 1st
func1/arg2 : 2nd
func1/arg3 : second
Call func1 --- "$*" ---
func1/arg1 : 1st 2nd second
func1/arg2 :
func1/arg3 :
Call func1 --- $@ ---
func1/arg1 : 1st
func1/arg2 : 2nd
func1/arg3 : second
Call func1 --- "$@" ---
func1/arg1 : 1st
func1/arg2 : 2nd second
func1/arg3 :
--------------
一つ前のコマンドの終了コード : 0
一つ前のコマンドの終了コード : 127
このプロセスID : 4074
バックグラウンドプロセスID : 4076
変数値の置換
変数 (以下の例では var )を以下のように記載することで、変数が設定されているかどうかにより、挙動を変えることが出来ます。初期値(デフォルト値)を利用する場合などに便利です。
記載 | 動作 |
---|---|
${var:-word} | 変数がセットされていない(空文字)場合、word を返します。var を変更しません。 |
${var:=word} | 変数がセットされていない(空文字)場合、word を返します。var を変更します。 |
${var:?word} | 変数がセットされていない(空文字)場合、置換に失敗し、標準エラー出力します。 |
${var:+word} | 変数がセットされている場合、word を返します。var を変更しません。 |
動作確認
以下のスクリプトを、vartest2.sh として作成して実行してみます。
echo "${var:-default1}"
echo "1. var = $var"
var=test1
echo "${var:-default2}"
echo "2. var = $var"
echo "----------------"
unset var
echo "${var:=default3}"
echo "3. var = $var"
var=test2
echo "${var:=default4}"
echo "4. var = $var"
echo "----------------"
unset var
# エラーで止まるのでコメントアウト
#echo "${var:?default5}"
#echo "5. var = $var"
var=test3
echo "${var:?default6}"
echo "6. var = $var"
echo "----------------"
unset var
echo "${var:+default7}"
echo "7. var = $var"
var=test4
echo "${var:+default8}"
echo "8. var = $var"
実行結果
default1
1. var =
test1
2. var = test1
----------------
default3
3. var = default3
test2
4. var = test2
----------------
test3
6. var = test3
----------------
7. var =
default8
8. var = test4
変数内の文字列を部分的に取得する
変数を部分的に抜き出すこともできます。知らなかったら処理を書いちゃいそうですね。
${::} を利用して、部分取得
変数 (以下の例では var )を、以下のように記載することで、部分的に文字列を取得することが出来ます。
オフセットとは、切り取り開始位置を表します。長さは切り取り位置から切り取る長さを表します。具体的には以下の様に利用します。
#echo ${var:0:5}
12345
#echo ${var:5:5}
67890
#echo ${var:2:5}
34567
${#} ${##} を利用して、左側除外
変数 (以下の例では var )を、以下のように記載することで、左側を除外できます。パスの除外や拡張子の取得に利用できます。#は最短マッチ、##は最長マッチとなっています。
具体的には、以下の様にして拡張子だけ取得したり、ファイル名だけ取得したりします。
#echo ${var##*.}
txt
#echo ${var##*/}
file.txt
${%} ${%%} を利用して、右側除外
変数 (以下の例では var )を、以下のように記載することで、右側を除外できる。パスの取得に利用できる。%は最短マッチ、%%は最長マッチとなっています。
具体的には、以下の様にしてファイル名を除外してパスを取得することができます。
#echo ${var%/*}
/home/user/dir