Bash初心者のために、制御文(if文、for文、while文)の書き方をテンプレ化してみました。
ちなみにIFS
の値は、こうなっていること前提です。
$ printf "${IFS}" | od -An -tx1
20 09 0a
$ bash --version
GNU bash, version 4.3.46(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
#ふつうのフレンズたち
##if文のフレンズ
# 条件部がコマンド(一般形式)
if command; then
echo hoge
elif command; then
echo fuga
else
echo hogefuga
fi
# true, falseコマンド
if true; then
echo hoge
fi
# testコマンド
if [ 1 -eq 1 ]; then
echo hoge
fi
# 複合コマンド([[]])
if [[ 1 -eq 1 ]]; then
echo hoge
fi
# 複合コマンド(算術式)
if ((1==1)); then
echo hoge
fi
##for文のフレンズ
# 直書きリストのイテレーション
for x in aaa bbb ccc; do
echo "$x"
done
# 変数に格納された値をイテレーション
xs="aaa bbb ccc"
for x in $xs; do
echo "$x"
done
# GLOB展開の結果をイテレーション
for x in *.log; do
echo "$x"
done
# コマンド置換の結果を単語単位でイテレーション
for x in $(command); do
echo "$x"
done
# ファイルを単語単位でイテレーション(コマンド置換)
for x in $(cat sample.log); do
echo "$x"
done
# ファイルを単語単位でイテレーション(コマンド置換(上より高速))
for x in $(<sample.log); do
echo "$x"
done
# 整数のイテレーション(ブレース展開)
for x in {1..10}; do
echo "$x"
done
# 整数のイテレーション(seqコマンド)
for x in $(seq 1 10); do
echo "$x"
done
# C言語風のイテレーション
for((i=0; i<10; ++i)); do
echo "$i"
done
# 配列のイテレーション
xs=("aaa" "bbb" "ccc")
for x in "${xs[@]}"; do
echo "$x"
done
##while文のフレンズ
# 無限ループ
while true; do
echo hoge
done
# ファイル行単位のイテレーション(パイプライン)
cat sample.log | while read line; do
echo "$line"
done
# ファイル行単位のイテレーション(リダイレクト)
while read line; do
echo "$line"
done <sample.log
# ファイル行単位のイテレーション(プロセス置換)
while read line; do
echo "$line"
done < <(cat sample.log)
#ワンライナーのフレンズたち
ふつうのフレンズたちを、すべてワンライナーにしてみました。
# if文ワンライナーのフレンズ
if true; then echo "$x"; fi
if [ 1 -eq 1 ]; then echo hoge; fi
if [[ 1 -eq 1 ]]; then echo hoge; fi
if ((1==1)); then echo hoge; fi
# for文ワンライナーのフレンズ
for x in aaa bbb ccc; do echo "$x"; done
for x in $xs; do echo "$x"; done
for x in *.log; do echo "$x"; done
for x in $(cat sample.log); do echo "$x"; done
for x in $(<sample.log); do echo "$x"; done
for x in {1..10}; do echo "$x"; done
for x in $(seq 1 10); do echo "$x"; done
for((i=0; i<10; ++i)); do echo "$i"; done
for x in "${xs[@]}"; do echo "$x"; done
# while文ワンライナーのフレンズ
while true; do echo hoge; done
cat sample.log | while read line; do echo "$line"; done
while read line; do echo "$line"; done <sample.log
while read line; do echo "$line"; done < <(cat sample.log)
#おまけのフレンズたち
##最終行の末尾改行の有無によって、イテレーションの結果が変わる
(このフレンズは「bashで意外だったこと、普段気になってるアレは何なんだ?を記録するノート」より転載したものです)
複数行のデータを読み込んでwhile
等でイテレーションする際、最終行の末尾に改行文字が存在しない場合、最終行が無視されてしまいます。
$ printf "aaa\nbbb\nccc" | while read line; do echo $line; done
aaa
bbb
$ printf "aaa\nbbb\nccc\n" | while read line; do echo $line; done
aaa
bbb
ccc
$ while read line; do echo $line; done < <(printf "aaa\nbbb\nccc")
aaa
bbb
$ while read line; do echo $line; done < <(printf "aaa\nbbb\nccc\n")
aaa
bbb
ccc
最終行末尾に改行のないファイルをcat
してパイプにつなげてテキスト処理する場合など、大変危険。
##if true; then ~; fi
if
文の条件部にtrue
やfalse
を書く場合、以下のように書くのが正しいです。
if true; then
echo hoge
fi
if false; then
echo hoge
fi
以下のように書くと「文字列"true"
("false"
)の長さは1以上である」という条件になってしまうため、恒真条件になります。[[ string ]]
は、[[ -n string ]]
と同じ意味になるためです。
if [ true ]; then
echo hoge
fi
if [[ false ]]; then
echo hoge
fi
##複合コマンドの条件部に複合コマンドを書く
条件部にはリストを書けるため、if文
や複合コマンドを書くこともできます。
while if true; then echo hoge; fi; do
echo fuga
done
だから何だ!
##for x; do ~; done
for
文は、以下のような書き方も可能です(in $xs
の部分がない!)
for x; do
echo "$x";
done
この場合、位置パラメータ$1
~$N
をイテレーションします。
$ bash -c 'for x; do echo "$x"; done' -s aaa bbb ccc
aaa
bbb
ccc
$ f(){ for x; do echo "$x"; done;}
$ f aaa bbb ccc
aaa
bbb
ccc
$ set +f; set -- aaa bbb ccc; set -f
$ for x; do echo "$x"; done
aaa
bbb
ccc
##for文のC言語風のイテレーションについて
@akinomyogaさんより頂きましたコメントにあります、for((;;)); do
のdo
の直前のセミコロンの省略についてです。
以下のfor
文ですが、
for((i=0; i<10; ++i)); do
echo "$i"
done
僕の環境では、これの++i)); do
のセミコロン;
をつけずに以下のように書いても正常動作しました。
for((i=0; i<10; ++i)) do
echo "$i"
done
また、ループ本体を{}
で括って、以下のように書いても正常動作しました(むしろ、こちらの方が有名ですよね)。
for((i=0; i<10; ++i)){
echo "$i"
}
C言語風のfor
ループに関しては記法がゆるやかなんだなあ…と思い、念のためman bash
を確認したところ、for((;;)); do :; done
形式の記法しか記載が無いではないですか。
for name [ [ in [ word ... ] ] ; ] do list ; done
The list of words following in is expanded, generating a list of items. The variable name is set to each element of this
list in turn, and list is executed each time. If the in word is omitted, the for command executes list once for each posi‐
tional parameter that is set (see PARAMETERS below). The return status is the exit status of the last command that executes.
If the expansion of the items following in results in an empty list, no commands are executed, and the return status is 0.
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
First, the arithmetic expression expr1 is evaluated according to the rules described below under ARITHMETIC EVALUATION. The
arithmetic expression expr2 is then evaluated repeatedly until it evaluates to zero. Each time expr2 evaluates to a non-zero
value, list is executed and the arithmetic expression expr3 is evaluated. If any expression is omitted, it behaves as if it
evaluates to 1. The return value is the exit status of the last command in list that is executed, or false if any of the
expressions is invalid.
というわけなので、僕はマニュアルに従ってfor((;;)); do :; done
形式で書くようにしたいと思います(@akinomyogaさんのコメント以後、本記事当該箇所のfor
文にも念のため;
をつける修正をしました)。
なお、上記のマニュアルの抜粋にもある通り、in
句の無いfor
文に関してはdo
直前のセミコロンは必須ではないようです。
$ bash -c 'for x do echo "$x"; done' -s aaa bbb ccc
aaa
bbb
ccc
$ bash -c 'for x; do echo "$x"; done' -s aaa bbb ccc
aaa
bbb
ccc