韜晦日記

韜晦日記

Rietveldよりもプログラミングメインになりつつある

Rietveld解析初心者による備忘録とつぶやき

Shellメモ

メモ用の走り書きだし正確性や可読性については勘弁してほしい。

$@, $*, "$@", "$*"の違い

引数に与えられる位置パラメータについて。

shell, bashなどではコマンドラインから実行する際に、引数は位置パラメータに設定される。
これを使って引数をプログラムに与えることが出来るののだ。また引数として(-oとか-hの様な)オプションを与えることも出来るようになる。
もっともオプションを使えるようにするためにはコマンドラインの入力を解析するためにプログラムは複雑になる。
こんな時はオプション解析コマンド getoptsを使えばかなり楽になるが、ここでは関係ないためこの辺にとどめておく。

閑話休題
この位置パラメータについて、具体的にかくとこんな感じだ。

コマンド 説明
$0 コマンドラインで実行されたファイルのファイルパスかコマンド
$1~$n コマンドラインで入力された引数を先頭から順番に格納してる
$# コマンドラインで入力された引数の総数を表す。
$@ コマンドラインで入力された引数の全てが格納される
$* $@と同じだが" "で囲んだ時の挙動が"$@"と違う

こんな感じだ、と言われてもピンとこないと思うので試してみる。
ここで実行するshellのファイルはtest.shとする。
また実行するtest.shはカレントディレクトリに配置されているものとする。
まずは与えられた引数がどのように格納されているかみる。

$0と$1 ~ $nについて

#!/bin/sh
echo $0
echo $1
echo $2
echo $3
echo $4

上記のtest.shを次のように実行すればこのように出力される。

$ sh test.sh aa bb cc
test.sh
aa
bb
cc

ここでshはshellを実行する宣言みたいなもの。
test.shは実行するファイル
aa bb ccが引数となる。
正しくはtest.shも引数である。多分
この時、コマンドラインに与えられた引数は3つのみなのに、echo $4としているため、出力の最後は空白行となっている。
このようにコードに書いた$nと引数が一致しなくてもプログラムはエラーを吐かないので注意が必要。

$10以上の時

また、$nの整数 nは基本的にはなんぼでもいけるが、$10以降は$10と分けて認識されてしまうため問題が生じる。
試しにやってみる。

#!/bin/sh
echo $10
echo ${10}
$ sh test.sh aa bb cc dd ee ff gg hh ii jj
aa0
jj

このように$10だと第一引数のaaと、$10の認識されなかった0が出力されてしまう。
この対策として${n}として{ }で囲うことで正しく出力されていることがわかる。

$@と$*について

この2つの挙動は同じである。
試しにやってみる。

#!/bin/sh
echo "\$@ -> $@"
echo "\$* -> $*"

これはこの様な結果になる。

$ sh test.sh aa bb cc dd ee ff gg hh ii jj
$@ -> aa bb cc dd ee ff gg hh ii jj
$* -> aa bb cc dd ee ff gg hh ii jj

同じですね。 もっと言えば、引数全てがスペース区切りで繋げられた状態で返されると認識しているといいかもしれない。

"$@"と"$*"について

試しにやってみる。

#!/bin/sh
echo "\$@ -> "$@""
echo "\$* -> "$@""

これはこの様な結果になる。

$ sh test.sh aa bb cc dd ee ff gg hh ii jj
$@ -> aa bb cc dd ee ff gg hh ii jj
$* -> aa bb cc dd ee ff gg hh ii jj

$@$*の時と同じ様に見える。
ここで$#setを使って引数の挙動を観察してみる。

#!/bin/sh
set -- "$@"
echo "$@"
echo $#

set -- "$*"
echo "$*"
echo $#
$ sh test.sh aa bb cc dd ee ff gg hh ii jj
aa bb cc dd ee ff gg hh ii jj
10
aa bb cc dd ee ff gg hh ii jj
1

$@の場合、setを通っても引数の数は10のまま変わない。
これに対して$*の場合は全て一つの引数としてまとめられたことがわかる。

したがって、この二つは引数全体を一つのパラメータとして扱う場合と引数を個別のパラメータとして扱う場合に使い分けることが出来る。
単に引数をLogとして出力する場合なんかは"$*"を使えば良い。

また、補足になるが引数を" "で囲んだ場合は囲まれた中身が1つのパラメータとして認識される。

#!/bin/sh
echo $#
echo $@
$ sh test.sh aa bb "cc dd"
3
aa bb cc dd

$@と"$@"の違い

この2つの違いは引数を" "で囲んだ部分がある場合に" "内を1つの引数として認識するかどうかである。 試しにやってみる。

echo $#
set -- $@
echo "$@"
echo $#

set -- "$@"
echo "$@"
echo $#
$ sh test.sh aa bb "cc dd"
3
aa bb cc dd
4
aa bb cc dd
4

実行時は" "で囲まれた部分が1つの引数として認識されている。
しかし、$@setしなおすと" "が無視されて引数が3から4に増えたことがわかる。 したがってsetする場合やforで値を取り出す場合に注意が必要である。 試しにforで引数を取り出してみる。

echo \$@
for i in $@; do
  echo ${i}
done

echo ""
echo "\$@"
for i in "$@"; do
  echo ${i}
done

echo ""
echo "\$*"
for i in "$*"; do
  echo ${i}
done
$sh test.sh aa bb "cc dd"

$@
aa
bb
cc
dd

"$@"
aa
bb
cc dd

"$*"
aa bb cc dd

ExchangeCharCo

この様に、$@の場合は" "を無視されていることがわかる。
また、"$*"では引数が1つにまとめられている事もわかる。

位置パラメータの再設定

プログラム内で位置パラメータを再設定することはできない。 例えば以下の様に再設定しようとしてもエラーが吐かれて終わる。

#!/bin/sh
$1=jj

位置パラメータを変更したい場合にはsetを用いる。

#!/bin/sh
echo $1 $2
set kk ll
echo $1 $2
$ sh test.sh aa bb
aa bb
kk ll