# TLCL 輪読会
## 32 - Positional Parameters
2023/10/03 [@kdnakt](https://twitter.com/kdnakt)
---
#### これまでに書いたシェルスクリプト
- コマンドラインのオプションや引数なし
- それでいいの?
---
## 今日の範囲
### 32 - Positional Parameters
- Accessing the Command Line
- Handling Positional Parameters en Masse
- A More Complete Application
---
### Accessing the Command Line
- 位置パラメータ
- $0:実行されるプログラムのパス名
- $1〜$9以降:渡された引数
- `${10}`以降:`${10}`, `${55}`, `${211}`...
----
- 位置パラメータの使い方
```shell!
#!/bin/bash
# posit-param: コマンドラインパラメータを表示する
echo "
\$0 = $0
\$1 = $1
\$2 = $2
\$3 = $3
\$4 = $4
\$5 = $5
\$6 = $6
\$7 = $7
\$8 = $8
\$9 = $9
"
```
----
- 実行結果
```shell!
~$ posit-param a b c d
$0 = /home/me/bin/posit-param
$1 = a
$2 = b
$3 = c
$4 = d
$5 =
$6 =
$7 =
$8 =
$9 =
```
----
#### Determining the Number of Arguments
- `$#`:引数の数にアクセスできる
```shell!
#!/bin/bash
echo "
Number of arguments: $#
\$0 = $0
\$1 = $1
\$2 = $2
"
```
```shell!
~$ posit-param a b
Number of arguments: 2
$0 = /home/me/bin/posit-param
$1 = a
$2 = b
```
----
#### shift - Getting Access to Many Arguments
- 引数が多いときにどう処理するか?
- `shift`コマンドでずらす
- `$2`が`$1`に、`$3`が`$2`に移動する
- `$0`は変化しない
----
- shiftの使い方
```shell!
#!/bin/bash
# posit-param2: 全パラメータを表示するスクリプト
count=1
while [[ $# -gt 0 ]]; do
echo "Argument $count = $1"
count=$((count + 1))
shift
done
```
```
$ ./posit-param2 a b c d
Argument 1 = a
Argument 2 = b
Argument 3 = c
Argument 4 = d
```
----
#### Simple Applications
- shiftなしのシンプルな実装
- basenameコマンド
- パスからファイル名のみ抽出
```shell!
#!/bin/bash
# file-info: シンプルなファイル情報表示
PROGNAME="$(basename "$0")"
if [[ -e "$1" ]]; then
echo -e "\nFile Type:"
file "$1"
echo -e "\nFile Status:"
stat "$1"
else
echo "$PROGNAME: usage: $PROGNAME file" >&2
exit 1
fi
```
----
#### Using Positional Parameters with Shell Functions
- 位置パラメータはシェル関数でも利用可能
- `.bashrc`などで活躍
- `$FUNCNAME`:自動的更新される変数
```shell!
file_info () {
# file_info: ファイル情報を表示する関数
if [[ -e "$1" ]]; then
echo -e "\nFile Type:"
file "$1"
echo -e "\nFile Status:"
stat "$1"
else
echo "$FUNCNAME: usage: $FUNCNAME file" >&2
return 1
fi
}
```
---
### Handling Positional Parameters en Masse
- 位置パラメータをまとめて扱う
- 別のプログラムのラッパーなど
- 2つの特別なパラメータ:`$*`と`$@`
- どちらも位置パラメータのリストに展開
- ダブルクオートありの動作が異なる
- `"$*"`:1つの文字列になる
- `"$@"`:個別の引数になる
----
- posit-params3: `"$*"`と`"$@"`の比較
```shell!
#!/bin/bash
print_params () {
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
}
pass_params () {
echo -e "\n" '$* :'; print_params $*
echo -e "\n" '"$*" :'; print_params "$*"
echo -e "\n" '$@ :'; print_params $@
echo -e "\n" '"$@" :'; print_params "$@"
}
pass_params "word" "words with spaces"
```
----
- 実行結果
```shell!
~$ posit-param3
$* :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$*" :
$1 = word words with spaces
$2 =
$3 =
$4 =
```
```shell!
$@ :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$@" :
$1 = word
$2 = words with spaces
$3 =
$4 =
```
----
- 引数が"word"と"words with spaces"の場合
- `"$*"`= `"word words with spaces"`
- `"$@"`= `"word" "words with spaces"`
- 元の引数が維持される`"$@"`の方が便利
---
### A More Complete Application
- 25章,27章の`sys_info_page`への機能追加
- ヘルプ
- インタラクティブモード
- ファイル出力機能
----
- ヘルプの実装と引数処理
```shell!
# ヘルプのシェル関数
usage () {
echo "$PROGNAME: usage: $PROGNAME [-f file | -i]"
return
}
```
```shell!
# コマンドラインオプションを処理
interactive=
filename=
while [[ -n "$1" ]]; do
case "$1" in
-f | --file) shift
filename="$1"
;;
-i | --interactive) interactive=1
;;
-h | --help) usage
exit
;;
*) usage >&2
exit 1
;;
esac
shift
done
```
----
- インタラクティブモードの実装
```shell!
if [[ -n "$interactive" ]]; then
while true; do
read -p "Enter name of output file: " filename
if [[ -e "$filename" ]]; then
read -p "'$filename' exists. Overwrite? [y/n/q] > "
case "$REPLY" in
Y|y) break
;;
Q|q) echo "Program terminated."
exit
;;
*) continue
;;
esac
```
```shell!
elif [[ -z "$filename" ]]; then
continue
else
break
fi
done
fi
```
----
- ファイル出力の実装(1)
```shell!
write_html_page () {
cat <<- _EOF_
<html><head><title>$TITLE</title></head>
<body>
<h1>$TITLE</h1>
<p>$TIMESTAMP</p>
$(report_uptime)
$(report_disk_space)
$(report_home_space)
</body>
</html>
_EOF_
return
}
```
----
- ファイル出力の実装(2)
```shell!
# HTMLを出力
if [[ -n "$filename" ]]; then
if touch "$filename" && [[ -f "$filename" ]]; then
write_html_page > "$filename"
else
echo "$PROGNAME: Cannot write file '$filename'" >&2
exit 1
fi
else
write_html_page
fi
```
---
### Summing Up
- これで終わりじゃないよ!
- 次回、for文でさらに改善しよう
{"title":"TLCL輪読会32章","slideOptions":"{\"transition\":\"slide\"}","description":"2023/10/03 kdnakt","contributors":"[{\"id\":\"df36d0f0-b67e-41ac-96b3-f3988326d230\",\"add\":6370,\"del\":1190}]"}