Function
シェルファイルのようなもの。パラメータを受け取れる。ポジショナルパラメータ $1, $2...
も受け取れる。
構文は次の通り
- function name() {...} # オススメ
- function name {...}
ポイントとしては、名前の重複に気をつけること。例えばls
という Function を定義すると、もともとあったlsが使えなくなるので注意。
サンプル1
引数も、戻り値も使える
#!/bin/bash
multiply() {
return $(( $1 * $2 ))
}
multiply 4 5
echo $?
実行1
$ ./funcdemo
20
サンプル2
ただし、この例では、特に戻り値はいらないので、省略してみる。
#!/bin/bash
multiply() {
echo $(( $1 * $2 ))
}
multiply 4 5
結果は同じ
サンプル3
次は、関数の戻り値がtrue or false で帰って来るので、それで判別している。関数の戻り値は、$?
で取得しているのがポイント
#!/bin/bash
starts_with_b () {
[[ $1 == [bB]* ]];
return $?
}
result=$(starts_with_b Boy)
echo $?
if starts_with_b Boy; then
echo "Start with B"
else
echo "Not start with B"
fi
実行
$ ./funcdemo
0
Start with B
ポイント
Function の中で定義された関数はローカルになる。
戻り値は、Exit ステータスコードにする。return ステートメントがなければ、Functionは最後のステートメントのステータスを返す
戻り値
スクリプトにグローバルな変数を使う
もしくは、Output ストリームに返す
Export Functions
export -f fun
サンプルの数々
box
入力した文字の周りのボックスを作る。${#1}
はパラメータの文字数のカウント
#!/bin/bash
drawline () {
declare line=""
declare char="-"
for (( i=0; i<$1; ++i )); do
line="${line}${char}"
done
printf "%s\n" "$line"
}
[[ ! $1 ]] && exit 0
declare -i len="${#1} + 4"
drawline $len
printf "| %s |\n" "$1"
drawline $len
実行
$ box hello
---------
| hello |
---------
count 改造
ポイントは、ヒアドキュメントの書き方、それから、errorの関数は、エラー出力に吐くようになっている。標準出力もエラー出力に出力される書き方。
一つ謎なのが、getopts の ":hb:s:r"
の書き方。こうしないと動かない。最初は見間違いと思ったが。
#!/bin/bash
usage () {
cat <<END
count [-r] [-b n] [-s n] stop
Print each number up to stop, beginning at 0
-b: number to begin with (default: 0)
-h: show this help message
-r: reverses the count
-s: sets step size (default: 1)
END
}
error() {
echo "Error: $1"
usage
exit $2
} >&2
isnum() {
[[ $1 =~ ^[0-9]+$ ]]
}
declare reverse=""
declare -i start=0
declare -i step=1
while getopts ":hb:s:r" opt; do
case $opt in
r)
reverse="yes"
;;
h)
usage
exit 0
;;
b)
isnum ${OPTARG} || error "${OPTARG} is not number" 1
start="${OPTARG}"
;;
s)
isnum ${OPTARG} || error "${OPTARG} is not number" 1
step="${OPTARG}"
;;
:)
echo "Option -${OPTARG} is missing an argument"
exit 1
;;
\?)
echo "Unknown option: -${OPTARG}" >&2
exit 1
;;
esac
done
shift $(( OPTIND -1 ))
[[ $1 ]] || { echo "Argument is missing" >&2; exit 1; }
declare end="$1"
if [[ ! $reverse ]]; then
for (( i=start; i <= end; i+=step )); do
echo $i
done
else
for (( i=end; i >= start; i-+step )); do
echo $if
done
fi
exit 0
実施結果
$ count -b 5 -s b 100
Error: b is not number
count [-r] [-b n] [-s n] stop
Print each number up to stop, beginning at 0
-b: number to begin with (default: 0)
-h: show this help message
-r: reverses the count
-s: sets step size (default: 1)
read_pipe
Bash の初心者がやりがちな問題。次のプログラムは想定した通りに動かない。理由は、$* | count_lines
の行で、count_lines
が実行されるのは、サブプロセスだから、ここで定義された変数の数字をアップデート出来ない。ちなみに、$*
はポジショナルパラメーターを展開して初めからスタートしたもの。
#!/bin/bash
declare -i count=0
count_lines () {
while read -r; do
((++count))
done
echo $count
}
$* | count_lines
echo $count
結果
$ read_pipe ls
9
0
hist
ディレクトリのファイルサイズと、パーセンテージを表す。ちなみに、bash の場所が違うのは、このシェルの実行には、V4 が必要で、Macは3xなので、インストールする必要があったため。
#!/usr/local/bin/bash
# This script prints a histrogram of how much space
# directories in the current working directory use
error () {
echo "Error: $1"
exit $2
} >&2
# Create a temp file.
my_mktemp () {
mktemp || mktemp -t hist
} 2> /dev/null
echo $BASH_VERSINFO[0]
(( BASH_VERSINFO[0] < 4 )) && error "Bash 4+ required." 1
declare -A file_sizes
declare -r tempfile=$(my_mktemp) || error "Can not create templfile" 2
declare -ir term_cols=$(tput cols)
declare -i max_name_len=0 max_size=0 total_size=0
drawline () {
declare line=""
declare char="-"
for (( i=0; i<$1; ++i )); do
line="${line}${char}"
done
printf "%s" "$line"
}
read_filesizes () {
while read -r size name; do
file_sizes["$name"]="$size"
(( total_size += size ))
(( max_size < size )) && (( max_size=size ))
(( max_file_len < ${#name} )) && (( max_file_len=${#name} ))
done
}
{ du -d 0 */ || du --max-depth 0 *; } 2>/dev/null > "$tempfile"
read_filesizes < "$tempfile"
declare -i length percentage
declare -i cols="term_cols - max_file_len - 10"
echo "file_sizes${!file_sizez[@]}"
for k in "${!file_sizes[@]}"; do
(( length=cols * file_sizes[$k] / max_size ))
(( percentage=100 * file_sizes[$k] / total_size ))
printf "%-${max_file_len}s | %3d%% | %s\n" "$k" "$percentage" $(drawline $length)
done
printf "%d Directories\n" "${#file_sizes[@]}"
printf "Total size: %d blocks\n" "$total_size"
rm "$tempfile"
結果
$ hist
5[0]
file_sizes
variables/ | 5% | -----------
file/ | 5% | -----------
forloop/ | 40% | ----------------------------------------------------------------------------------------
bin/ | 50% | ---------------------------------------------------------------------------------------------------------------
4 Directories
Total size: 160 blocks
まとめ
- Function の直後にリダイレクトをすることが可能
fun () {...} >&2
- コマンドのパイプラインは、サブシェルで実行される
ls | while read -r; do ((++count)):done
動作しない。ファイルを使う - ヒアドキュメントは
<< Tag ...Tag