[シェル] 特殊パラメータ @ のパラメータ展開

シェルにおいて、同じパラメータ展開でもパラメータの種類が特殊パラメータか配列か等によって動作が異なる場合があります。

本記事では、特殊パラメータ `@` の場合のパラメータ展開の挙動についてまとめます。

# 0. まとめ

|説明|コード|`@` に適用|各位置パラメータに適用|
|-|-|-|-|
|デフォルト値|`"${@:-word}"`|✓||
|エラー表示|`"${@:?}"`<br>`"${@:?word}"`|✓||
|代替値|`"${@:+word}"`|✓||
|部分列|`"${@:offset}"`<br>`"${@:offset:length}"`|✓||
|長さ|`$#`<br>`${#@}`|✓||
|パターン削除|`"${@#pattern}"`<br>`"${@##pattern}"`<br>`"${@%pattern}"`<br>`"${@%%pattern}"`||✓|
|パターン置換|`"${@/pattern/string}"`<br>`"${@//pattern/string}"`<br>`"${@/#pattern/string}"`<br>`"${@/%pattern/string}"`<br>`"${@/pattern}"`<br>`"${@//pattern}"`<br>`"${@/#pattern}"`<br>`"${@/%pattern}"`||✓|
|大文字小文字変換|`"${@^}"`<br>`"${@^^}"`<br>`"${@^pattern}"`<br>`"${@^^pattern}"`<br>`"${@,}"`<br>`"${@,,}"`<br>`"${@,pattern}"`<br>`"${@,,pattern}"`||✓|
|変換|`"${@@operator}"`||✓|

※パラメータ展開 `${parameter:=word}` は特殊パラメータに対して使用不可。
※各パラメータ展開の詳細は `man bash` 等を参照。

参考「[3.5.3 Shell Parameter Expansion - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion)」

# 1. デフォルト値

```bash:使い方
"${@:-word}"
```

```bash:例
# 
function f() {

	set -- foo bar baz
	echo "${@:-qux}"

	set -- foo bar ''
	echo "${@:-qux}"

	set --
	echo "${@:-qux}"

	set -- ''
	echo "${@:-qux}"

}

f

# 
function g() {

	local -r word='qux'

	set -- foo bar baz
	echo "${@:-$word}"

	set -- foo bar ''
	echo "${@:-$word}"

	set --
	echo "${@:-$word}"

	set -- ''
	echo "${@:-$word}"

}

g
```

```text:例の実行結果
foo bar baz
foo bar
qux
qux
foo bar baz
foo bar
qux
qux
```

# 2. エラー表示

```bash:使い方
"${@:?}"
"${@:?word}"
```

```bash:例
# 
function f() {

	set -- foo bar baz
	echo "${@:?}"

	set -- foo bar ''
	echo "${@:?}"

	set --
	# echo "${@:?}" # エラー発生

	set -- ''
	# echo "${@:?}" # エラー発生

}

f

# 
function g() {

	set -- foo bar baz
	echo "${@:?qux}"

	set -- foo bar ''
	echo "${@:?qux}"

	set --
	# echo "${@:?qux}" # エラー発生

	set -- ''
	# echo "${@:?qux}" # エラー発生

}

g

# 
function h() {

	local -r word='qux'

	set -- foo bar baz
	echo "${@:?$word}"

	set -- foo bar ''
	echo "${@:?$word}"

	set --
	# echo "${@:?$word}" # エラー発生

	set -- ''
	# echo "${@:?$word}" # エラー発生

}

h
```

```text:例の実行結果
foo bar baz
foo bar
foo bar baz
foo bar
foo bar baz
foo bar
```

# 3. 代替値

```bash:使い方
"${@:+word}"
```

```bash:例
# 
function f() {

	set -- foo bar baz
	echo "${@:+qux}"

	set -- foo bar ''
	echo "${@:+qux}"

	set --
	echo "${@:+qux}"

	set -- ''
	echo "${@:+qux}"

}

f

# 
function g() {

	local -r word='qux'

	set -- foo bar baz
	echo "${@:+$word}"

	set -- foo bar ''
	echo "${@:+$word}"

	set --
	echo "${@:+$word}"

	set -- ''
	echo "${@:+$word}"

}

g
```

```text:例の実行結果
qux
qux


qux
qux


```

# 4. 部分列

特殊パラメータ `@` 自体は配列でないですが、パラメータ展開の部分列は配列と同様に特殊パラメータ `@` に対しても使用することができます。

```bash:使い方
"${@:offset}"
"${@:offset:length}"
```

特殊パラメータ `@` の場合、`offset` に負の数を指定することは出来ますが、`length` に負の数を指定することは出来ません。

部分列の終端を末尾からのオフセットで指定したい場合は、特殊パラメータ `$#` を用いて計算します。

```bash:例
# 
function f() {

	set -- foo bar baz qux

	echo "${@:2}"

	echo "${@: -3}"
	# echo "${@:-3}" # 間違い

	local -ri offset=2
	echo "${@:offset}"

}

f

# 
function g() {

	set -- foo bar baz qux

	echo "${@:2:2}"

	echo "${@: -3:2}"
	# echo "${@:-3:2}" # 間違い

	echo "${@:2:$#-2}"
	# echo "${@:2:-1}" # エラー発生

	local -ri offset=2
	local -ri length=2
	echo "${@:offset:length}"

}

g
```

```text:例の実行結果
bar baz qux
bar baz qux
bar baz qux
bar baz
bar baz
bar baz
bar baz
```

他に以下のような部分列の使い方があります。

|部分列|意味|
|-|-|
|`"${@:0:1}"`|シェルまたはシェルスクリプトの名前<br>`"$0"`|
|`"${@:1:1}"`|最初の位置パラメータ<br>`"$1"`|
|`"${@:offset:1}"`|`$offset` 番目の位置パラメータ<br>`"${!offset}"`|
|`"${@:$#}"`<br>`"${@: -1}"`|最後の位置パラメータ<br>(※書き換えは後述)|
|`"${@:0}"`|`"$0" "$@"`|
|`"${@:1}"`|`"$@"`|

```bash:例
# 
function f() {

	set -- foo bar baz

	echo "${@:0:1}"
	echo "$0"

	echo "${@:1:1}"
	echo "$1"

	local -ri offset=2
	echo "${@:offset:1}"
	echo "${!offset}"

	echo "${@:$#}"
	echo "${@: -1}"

}

f

# 
function g() {

	set -- foo bar baz

	echo "${@:0}"
	echo "$0" "$@"

	echo "${@:1}"
	echo "$@"

}

g
```

```text:例の実行結果の例
./name.sh
./name.sh
foo
foo
bar
bar
baz
baz
./name.sh foo bar baz
./name.sh foo bar baz
foo bar baz
foo bar baz
```

# 5. 長さ

パラメータ展開 `${#parameter}` を用いて `${#@}` とすることで位置パラメータの数を取得できますが、特殊パラメータ `$#` の値と等しくなるため `$#` を使って問題ないと思います。

```bash:使い方
$#
${#@}
```

```bash:例
set -- foo bar baz
echo "$# ${#@}"

set -- 'foo bar baz'
echo "$# ${#@}"

set --
echo "$# ${#@}"

set -- ''
echo "$# ${#@}"

set -- foo bar ''
echo "$# ${#@}"
```

```text:例の実行結果
3 3
1 1
0 0
1 1
3 3
```

# 6. パターン削除

```bash:使い方
"${@#pattern}"
"${@##pattern}"
"${@%pattern}"
"${@%%pattern}"
```

パターンに関しては別記事にしました。

参考「[シェルのパターンマッチング - Qiita](https://qiita.com/kerupani129/items/2d3f8cfa17856e25a5fc)」

```bash:例
set -- 'foo bar baz'
echo "${@#* }"
echo "${@##* }"
echo "${@% *}"
echo "${@%% *}"

set -- 'foo bar baz' 'foo bar baz' 'foo bar baz'
echo "${@#* }"
echo "${@##* }"
echo "${@% *}"
echo "${@%% *}"

set -- foo bar baz
echo "${@#* }"
echo "${@##* }"
echo "${@% *}"
echo "${@%% *}"
```

```text:例の実行結果
bar baz
baz
foo bar
foo
bar baz bar baz bar baz
baz baz baz
foo bar foo bar foo bar
foo foo foo
foo bar baz
foo bar baz
foo bar baz
foo bar baz
```

# 7. パターン置換

```bash:使い方
"${@/pattern/string}"
"${@//pattern/string}"
"${@/#pattern/string}"
"${@/%pattern/string}"

"${@/pattern}"
"${@//pattern}"
"${@/#pattern}"
"${@/%pattern}"
```

パターンに関しては別記事にしました。

参考「[シェルのパターンマッチング - Qiita](https://qiita.com/kerupani129/items/2d3f8cfa17856e25a5fc)」

```bash:例
set -- 'foo bar baz'
echo "${@/ /_}"
echo "${@// /_}"
echo "${@/#foo/***}"
echo "${@/%baz/***}"

set -- 'foo bar baz' 'foo bar baz' 'foo bar baz'
echo "${@/ /_}"
echo "${@// /_}"
echo "${@/#foo/***}"
echo "${@/%baz/***}"

set -- foo bar baz
echo "${@/ /_}"
echo "${@// /_}"
echo "${@/#foo/***}"
echo "${@/%baz/***}"
```

```text:例の実行結果
foo_bar baz
foo_bar_baz
*** bar baz
foo bar ***
foo_bar baz foo_bar baz foo_bar baz
foo_bar_baz foo_bar_baz foo_bar_baz
*** bar baz *** bar baz *** bar baz
foo bar *** foo bar *** foo bar ***
foo bar baz
foo bar baz
*** bar baz
foo bar ***
```

```bash:例
set -- 'foo bar baz'
echo "${@/ }"
echo "${@// }"
echo "${@/#* }"
echo "${@/% *}"

set -- 'foo bar baz' 'foo bar baz' 'foo bar baz'
echo "${@/ }"
echo "${@// }"
echo "${@/#* }"
echo "${@/% *}"

set -- foo bar baz
echo "${@/ }"
echo "${@// }"
echo "${@/#* }"
echo "${@/% *}"
```

```text:例の実行結果
foobar baz
foobarbaz
baz
foo
foobar baz foobar baz foobar baz
foobarbaz foobarbaz foobarbaz
baz baz baz
foo foo foo
foo bar baz
foo bar baz
foo bar baz
foo bar baz
```

# 8. 大文字小文字変換

## 8.1. 大文字へ変換

```bash:使い方
"${@^}"
"${@^^}"
"${@^pattern}"
"${@^^pattern}"
```

パターンに関しては別記事にしました。

参考「[シェルのパターンマッチング - Qiita](https://qiita.com/kerupani129/items/2d3f8cfa17856e25a5fc)」

```bash:例
set -- foo bar baz
echo "${@^}"
echo "${@^^}"
echo "${@^f}"
echo "${@^^a}"

set -- 'foo bar baz'
echo "${@^}"
echo "${@^^}"
echo "${@^f}"
echo "${@^^a}"

set -- 'foo bar baz' 'foo bar baz' 'foo bar baz'
echo "${@^}"
echo "${@^^}"
echo "${@^f}"
echo "${@^^a}"
```

```text:例の実行結果
Foo Bar Baz
FOO BAR BAZ
Foo bar baz
foo bAr bAz
Foo bar baz
FOO BAR BAZ
Foo bar baz
foo bAr bAz
Foo bar baz Foo bar baz Foo bar baz
FOO BAR BAZ FOO BAR BAZ FOO BAR BAZ
Foo bar baz Foo bar baz Foo bar baz
foo bAr bAz foo bAr bAz foo bAr bAz
```

## 8.2. 小文字へ変換

```bash:使い方
"${@,}"
"${@,,}"
"${@,pattern}"
"${@,,pattern}"
```

パターンに関しては別記事にしました。

参考「[シェルのパターンマッチング - Qiita](https://qiita.com/kerupani129/items/2d3f8cfa17856e25a5fc)」

```bash:例
set -- FOO BAR BAZ
echo "${@,}"
echo "${@,,}"
echo "${@,F}"
echo "${@,,A}"

set -- 'FOO BAR BAZ'
echo "${@,}"
echo "${@,,}"
echo "${@,F}"
echo "${@,,A}"

set -- 'FOO BAR BAZ' 'FOO BAR BAZ' 'FOO BAR BAZ'
echo "${@,}"
echo "${@,,}"
echo "${@,F}"
echo "${@,,A}"
```

```text:例の実行結果
fOO bAR bAZ
foo bar baz
fOO BAR BAZ
FOO BaR BaZ
fOO BAR BAZ
foo bar baz
fOO BAR BAZ
FOO BaR BaZ
fOO BAR BAZ fOO BAR BAZ fOO BAR BAZ
foo bar baz foo bar baz foo bar baz
fOO BAR BAZ fOO BAR BAZ fOO BAR BAZ
FOO BaR BaZ FOO BaR BaZ FOO BaR BaZ
```

# 9. 変換

```bash:使い方
"${@@operator}"
```

## 9.1. 大文字小文字変換

|変換|意味|
|-|-|
|`"${@@U}"`|大文字へ変換<br>`"${@^^}"`|
|`"${@@u}"`|最初の文字を大文字へ変換<br>`"${@^}"`|
|`"${@@L}"`|小文字へ変換<br>`"${@,,}"`|

```bash:例
# 
function f() {

	set -- foo bar baz
	echo "${@@U}"
	echo "${@^^}"

	set -- 'foo bar baz'
	echo "${@@U}"
	echo "${@^^}"

	set -- 'foo bar baz' 'foo bar baz' 'foo bar baz'
	echo "${@@U}"
	echo "${@^^}"

}

f

# 
function g() {

	set -- foo bar baz
	echo "${@@u}"
	echo "${@^}"

	set -- 'foo bar baz'
	echo "${@@u}"
	echo "${@^}"

	set -- 'foo bar baz' 'foo bar baz' 'foo bar baz'
	echo "${@@u}"
	echo "${@^}"

}

g

# 
function h() {

	set -- FOO BAR BAZ
	echo "${@@L}"
	echo "${@,,}"

	set -- 'FOO BAR BAZ'
	echo "${@@L}"
	echo "${@,,}"

	set -- 'FOO BAR BAZ' 'FOO BAR BAZ' 'FOO BAR BAZ'
	echo "${@@L}"
	echo "${@,,}"

}

h
```

```text:例の実行結果
FOO BAR BAZ
FOO BAR BAZ
FOO BAR BAZ
FOO BAR BAZ
FOO BAR BAZ FOO BAR BAZ FOO BAR BAZ
FOO BAR BAZ FOO BAR BAZ FOO BAR BAZ
Foo Bar Baz
Foo Bar Baz
Foo bar baz
Foo bar baz
Foo bar baz Foo bar baz Foo bar baz
Foo bar baz Foo bar baz Foo bar baz
foo bar baz
foo bar baz
foo bar baz
foo bar baz
foo bar baz foo bar baz foo bar baz
foo bar baz foo bar baz foo bar baz
```

## 9.2. エスケープ関連

|変換|意味|
|-|-|
|`"${@@Q}"`|エスケープ|
|`"${@@E}"`|バックスラッシュエスケープを展開|
|`"${@@P}"`|プロンプト文字列として展開|
|`"${@@A}"`|位置パラメータの値を割り当てる `set` コマンドに変換|

※パラメータ展開 `"${@@a}"` は空文字列になります (特殊パラメータの変数属性は変更出来ないため) 。
※パラメータ展開 `"${@@K}"` および `"${@@k}"` は `"${@@Q}"` と同じ結果になります (特殊パラメータ `@` は配列でなく、位置パラメータに配列を設定することも出来ないため) 。

```bash:例
set -- foo '\' \'
echo "${@@Q}"

set -- foo '\\' '\'\'
echo "${@@E}"

set -- '\'s '\'v
echo "${@@P}"

set -- foo '\' \'
echo "${@@A}"
```

```text:例の実行結果
'foo' '\' \'
foo \ '
bash 5.2
set -- 'foo' '\' \'
```

# 10. おまけ: 位置パラメータの間接展開

間接展開で位置パラメータを扱うことができます。

```bash:使い方
"${!offset}"
```

```bash:例
# 
function f() {

	set -- foo bar baz

	local -ri offset=2
	echo "${!offset}"
	echo "${@:offset:1}"

	local -ri offset_last=$#
	echo "${!offset_last}"
	echo "${@:$#}"
	echo "${@: -1}"

}

f
```

```text:例の実行結果
bar
bar
baz
baz
baz
```

間接展開 `"${!#}"` に関しては、Bash で POSIX モードを無効化している場合に使用可能です。

```bash:例
set -o posix

# 
function f() {

	set -- foo bar baz

	# echo "${!#}" # エラー発生

	set +o posix
	echo "${!#}"
	set -o posix

	echo "${@:$#}"
	echo "${@: -1}"

}

f
```

```text:例の実行結果
baz
baz
baz
```