UNIX/Linuxにおけるシェルの1つであるbashでは、コマンドライン上で、または、shell関数ファイルやshellスクリプト内で変数を定義、利用することができます。
shellには環境変数の他にshell変数、一般的な変数、特殊変数(自動変数)というものがありますが、bashでは環境変数とシェル変数が明確に分けられておらず、更にシェル変数と極一般に利用される他のスクリプトや言語でいうところの変数とも境界が曖昧です。
まず、環境変数とシェル変数については、環境変数はオプションなしのenv/printenvで確認できる変数、シェル変数はオプションなしのsetコマンドで確認できる変数、また、シェル変数と一般的な変数については、シェル変数は環境変数とは別の領域に、または環境変数とシェル変数共に登録される(または登録する)変数、一般的な変数は一時的に利用される変数と考えるとすっきりするかもしれません。
bashの変数は宣言する必要がなく、また明示的な型はありません、そういう意味ではPerlと同様ですが、そのPerlでも複数ある変数参照時の先頭に付加する記号はbashでは$(ドル記号)のみで配列(リスト)も扱うことができます。
もちろん宣言してもエラーにはなりませんが、必要都度その場で使い始めることができ、代入する際にはスペースを入れず変数名だけを書いて直接、参照する際には変数名の先頭に$を付加して参照します。
$ var_name=foo
$ echo $var_name
foo
代入して参照すると、(ログインシェルやサブシェルなど個々の)shell内で有効な変数として扱われますが、例えばコマンドラインで定義した場合には、そのshell内の更にそのコマンドライン上でのみ有効な一時的な変数となり(exportすると環境変数になり、特定のオプション付きでdeclareするとシェル変数になり)ます。
#!/usr/bin/sh
# test.sh
var_array=(abc def)
# または
# var_array[0]=abc
# var_array[1]=def
echo ALL_LIST : ${var_array[@]}
echo LIST1 : ${var_array[0]}
echo LIST2 : ${var_array[1]}
$ chmod u+x test.sh
$ ./test.sh
ALL_LIST : abc def
LIST1 : abc
LIST2 : def
配列を利用する場合には、$なしの変数名に( )に既定ではスペース区切りでリストを渡すか、または、変数名[ ]として添え字を入れたものに代入します。
参照方法はCやPerlの配列とは若干異なり、${ }内に変数名[ 添え字 ]を入れ、添え字に@を入れるとリスト全体、0から始まる数字を入れると個々の要素を参照できます。
尚、普通の変数もこの方法で参照でき、冒頭の例の$var_nameを${var_name}としても同じです。
また、代入時に( )を使っていますが、( )内にバッククォートを伴うコマンドを入れれば、コマンドの結果がリストとして格納されます(例えば`ls`を入れればファイルリスト)。
#!/usr/bin/sh
var_command=`ls "*.txt"`
while :
do
command
done < $var_command
尚、それとはまた別に代入のリンク先にもあるようにコマンドの結果ではなく、バッククォートで括ったコマンドそれ自体を代入することもできます。
例えば、これはls コマンドの指示を変数に代入し、ループする場合の一例です。
変数のスコープについては、Bourne ShellではBourne Shell組み込みコマンドにも相応のコマンドもなく全てグローバルとなっていましたが、bash組み込みコマンドとしてlocalコマンドやdeclareコマンドが追加され、「関数内の変数に限り変数のスコープを限定した局所変数」とすることができるようになっています。
これはログインシェルとサブシェルなど親シェルと子シェル間における関数及び関数内shell変数のスコープに関して対策が講じられたもので、仮にテストケースとして関数の外でlocal宣言すると局所化されて結果表示されるケースもありますが、それでもlocalコマンドは関数内でのみ利用できる旨のエラーも同時に出力されます。
前述のようにbashには、環境変数、シェル変数、一般的な変数の他に特殊変数(自動変数)があり、特殊変数には、シェルスクリプトやシェル関数の引数や引数の数、式の結果を自動的に格納する位置変数パラメータと特殊変数パラメータがあります。
#!/usr/bin/bash
# script_name : script_foo
echo $# # 引数の数
echo $* # 実引数のリスト
echo $@ # 実引数のリスト
# $0はプログラム名、$1からは個々の実引数
echo "$0:$1:$2"
...
例えば2つの引数を想定しているscript_fooというスクリプトがあったとすると。。。
引数の数は、自動的に格納される変数名シャープに変数参照のドル記号を伴った$#、全ての実引数はドル記号を伴うアスタリスク$*かアットマーク$@で参照でき、Perlと同様に$0には実行ファイル名(例では-bash)、$1、$2には実際の引数が自動的に格納されます。
尚、位置変数パラメータとも呼ばれるこれら$1、$2...$nに自動的に格納される数における論理的な制限はありませんが、何れにしてもアクセスできるのは$9までなのでフロー制御などでそれ以降の値を参照する場合にはshiftなどを利用して配列に格納するなど工夫が必要です。
...
# "$*" は=> "$1,$2,$3...$n"
# "$@" は=> "$1","$2","$3"..."$n"
for wq_asta_var in "$*"
do
echo asta : $wq_asta_var
done
for wq_atmark_var in "$@"
do
echo atmark : $wq_atmark_var
done
実引数のリストを自動的に格納する$*と$@の違いは、2つのfor文にあるechoの出力結果のようにダブルクォートで括った時に出てきます(ちなみにシングルクォートで括ってしまうと$*または$@という文字列1つのリストと解釈されてしまいます)。
"$*"はリスト全体をダブルクォートで括った状態なので、そのままでは個々の引数を取得することができず、一方、"$@"の場合はリスト内の個々の要素がダブルクォートで括られた状態なので、そのままで個々の要素を取りだすことができます。
尚、リスト内の区切り(セパレータ)はbashではオプションなしのsetコマンド(bash組み込みコマンド)で閲覧可能なシェル変数に含まれるIFS(Internal Field Separator)に設定されている空白(半角スペースとタブ)と改行の内、半角スペースです。
$ script_foo abc 123 def
3 # $#の結果
abc 123 def # $*の結果
abc 123 def # $@の結果
-bash : abc : 123 # $0,$1,$2の結果
asta : abc 123 def # "$*"のfor文の結果
atmark : abc # "$@"のfor文の結果
atmark : 123
atmark : def
ちなみにechoコマンドの引数にある[ asta : ]、[ atmark : ]は、出力時に区別する為の単なるラベルです。
この例では引数を3つ渡していますが、(Perlが$0に左詰めして帳尻を合わせるのと異なり)bashスクリプトやbash shell関数では($3が)参照されていない3つめの引数「def」は無視されるので$0,$1,$2の結果には表示されていません。
尚、ドル記号にハイフン$-は、set -xを設定した際同様、shell内の現在位置を表示します。
$ expr 128 + 128
256
$ echo $?
0
また、UNIX/Linuxでは実行結果の成功・失敗を表す際に、その戻り値として成功は0、失敗にはゼロ以外という慣習がありますが、C言語同様、bashもこの慣習通り(Perlはundef、""も偽ですが0も偽、0以外が真で普通は1)で、bashには直前に評価された判定結果を参照できる特殊変数$?があります。
変数名クエスチョンマークに変数参照の為のドル記号を付加した特殊変数$?は、この例では直前の計算が成功した結果としてその正常終了値0が自動的に格納されています。
尚、ここのケースとは全く別にPerlとの連携でインタフェースを一致させるなどに必要なのか、そうしなければならない状況をにわかには想定できませんが、bashを-cオプション付きで起動すると引数として渡す文字列をコマンドとして認識し、$0には第一引数が格納されるとmanには書いてあります。
$ echo $$
2054
$ bash
$ echo $$
3611
$ echo $!
5378
更にドル記号を2つ並べた$$は、現在のプロセス(例では最初の数値が親シェル、次の数値がサブシェルとして起動したbash)のPIDを、またドル記号にエクスクラメーションマーク(びっくりマーク)を付加した$!は、バックグラウンドで最も直近に実行された最後のジョブのPIDを表し、いずれもプロセスとジョブにあるようにps コマンドやjobs コマンドで確認できます。