BashでAtCoderやる
read
コマンドを使う
cat << EOS > >(
read A B
echo $A $B
)
hoge piyo
EOS
cat << EOS > >(
# 配列の初期化
a=($(cat))
# 配列の長さ
echo ${#a[@]}
# 配列の全体
echo ${a[@]}
echo ${a[*]}
# i番目の要素
echo ${a[0]}
echo ${a[1]}
echo ${a[2]}
)
hoge piyo fuga
EOS
cat << EOS > >(
a=($(cat))
# 算術式では変数名に$はつけない
echo $((a[0]+a[1]+a[2]))
# forで合計を出す
for ((i=0,sum=0;i<${#a[@]};i++)); do
((sum+=a[i]))
done
echo $sum
# eval+let+ブレース展開で合計を出す
# IFS_BACKUP=$IFS # 同一プロセスで動かすときは、IFSをもとに戻す必要あり
IFS=,;eval let sum=0 sum+={"${a[*]}"}
# IFS=$IFS_BACKUP
echo $sum
)
1 2 3
EOS
cat
だと一気に最後まで読んでしまうので、一行ずつ処理したい場合は while read
で処理する
cat << EOS > >(
while read line; do
a=($line)
echo $((a[0]+a[1]+a[2]))
done
)
1 2 3
4 5 6
7 8 9
EOS
[[ ]]
を使用する。
# 文字列の一致
if [[ "hoge" = "hoge" ]]; then
echo '一致'
fi
# 文字列の不一致
if [[ "hoge" != "piyo" ]]; then
echo '不一致'
fi
# 正規表現マッチ
# 正規表現はクオートしない
if [[ "hoge" =~ ^h.{3}$ ]]; then
echo ${BASH_REMATCH[0]} # マッチ箇所はこれで取り出す
echo '一致'
fi
# 辞書順
# '<' および '>' は文字列の辞書順比較
if [[ "hoge" < "piyo" ]]; then
echo 'yeah!'
fi
# よくある間違い(桁数が一致してないと数値の比較はできない)
if [[ 1000 < 11 ]]; then
echo 'yeah!'
fi
(( )) |
[[ ]] |
---|---|
a > b |
$a -gt $b |
a >= b |
$a -ge $b |
a < b |
$a -lt $b |
a <= b |
$a -le $b |
a == b |
$a -eq $b |
a != b |
$a -ne $b |
a=10 b=20
# [[ ]]
if [[ $a -lt $b ]]; then
echo 'yeah!'
fi
# (( ))
# trueなら1が返る。
#こっちのほうがぱっと見頭に入ってくる(気がする)。
if ((a<b)); then
echo 'yeah!'
fi
意味 | 書き方 |
---|---|
AND | <condition> && <condition> |
OR | <condition> || <condition> |
NOT | ! <condition> |
グループ化 | (<condition>) |
# 例
a=10 b=20 c=30
if [[ (! $a -gt $b) && ($b -lt $c || $c -ne 30) ]]; then
echo 'yeah!'
fi
condition?expr1:expr2
のように書く。
condition
がtrue
と評価された場合にexpr1
が実行されるcondition
がfalse
と評価された場合にexpr2
が実行される
bashの算術式では、0
が false
と評価され、それ以外は true
となる。
((a=10,b=0))
(((a==10)?(b=10):(b=0)))
echo $b # 10
# もしくは
((a=10,b=0))
((b=(a==10)?10:0))
echo $b # 10
expr1||expr2
expr1
がtrue
と判定された場合、expr2
は実行されない
expr1&&expr2
expr1
がfalse
と判定された場合、expr2
は実行されない
! expr
expr
のtrue/false
を入れ替える0
なら1
に、0
以外なら0
になる
echo $((0||1)) # 1
echo $((1||0)) # 1
echo $((0&&1)) # 0
echo $((1&&0)) # 0
echo $((! 0)) # 1
echo $((! 123)) # 0
ちなみにだが、算術式の戻り値は、一番最後の式の評価結果が false
の時、ステータスコードが1となる。
bashの e
オプションを指定した場合、予期せずプログラムが終了してしまう場合がある。
((a=0))
echo $? # 1
複数のコマンドを &&
もしくは ||
でつなぐことで、前のコマンドの終了ステータスに応じて実行したり/しなかったりを制御できる。
COMMAND1 && COMMAND2
COMMAND1
の終了コードが0(正常終了)の時に限り、COMMAND2
を実行する
COMMAND1 || COMMAND2
COMMAND1
の終了コードが0以外(異常終了)の時に、COMMAND2
を実行する
! COMMAND1
!
を前につけると、終了コードの正常/異常を反転させる0
なら1
に、0
以外なら0
になる
true
- 常に終了コードが
0
のコマンド
- 常に終了コードが
false
- 常に終了コードが
1
のコマンド
- 常に終了コードが
true; echo $? # 0
false; echo $? # 1
! true; echo $? # 1
! false; echo $? # 0
true && echo 'OK' # OK
true || echo 'OK' # ''
false && echo 'OK' # ''
false || echo 'OK' # OK
&&
、 ||
は左結合なので、次のように書くと、三項演算子として振る舞う。
true && echo 'OK' || echo 'NG' # OK
(true && echo 'OK') || echo 'NG' # 同上
false && echo 'OK' || echo 'NG' # NG
(false && echo 'OK') || echo 'NG' # 同上
set -u
してたらエラーになるけど、デフォルト値の設定とかも書ける
hoge=$([[ $hoge ]] && echo $hoge || echo 'detault')
echo $hoge # default
hoge='hoge'
hoge=$([[ $hoge ]] && echo $hoge || echo 'detault')
echo $hoge # hoge
# 通常のwhileループ
count=0
while [[ $count -lt 10 ]]; do
echo count=$count
((count++))
done
# 算術式を使うと
count=0
while ((count<10)); do
echo count=$count
((count++))
done
# 無限ループ
# while :; do でもよい
count=0
while true; do
echo count=$count
((count++))
if [[ $count -eq 10 ]]; then
break
fi
done
# COMMANDの終了コードが0の間だけループ
while COMMAND; do
echo 'まだ大丈夫'
done
# whileと条件の扱いが逆
# COMMANDの終了コードが0になるまでループ
until COMMAND; do
echo 'まだかな?'
done
# リトライ回数の上限を設定
count=0
until COMMAND || ((count++,count>=10)); do
echo 'まだかな?'
done
# 通常のforループ
for ((i=0;i<10;i++)); do
echo i=$i
done
# for-inパターン
for i in 0 1 2 3; do
echo i=$i
done
# for-in+ブレース展開
for i in {0..9}; do
echo i=$i
done
# for-in+ブレース展開+インクリメント量2
# 3つ目の数字でインクリメント量を指定する
for i in {0..10..2}; do
echo i=$i
done
# for-in+配列
a=({0..9})
for i in "${a[@]}"; do
echo i=$i
done
# for-in+seqコマンド
for i in $(seq 0 9); do
echo i=$i
done
# for-in+seqコマンド+インクリメント量2
# 2つ目の数字でインクリメント量を指定する
for i in $(seq 0 2 10); do
echo i=$i
done
recursion='i<10?sum+=i,i++,recursion:sum'
echo $((sum=0,i=0,recursion)) # 45
単純なループならletとブレース展開で比較的簡単に書ける。
let sum=0 sum+={0..9}
echo $sum # 45
# ループの範囲を変数化する場合
# そのままだと展開されないので、evalをかませる
first=0 last=9
eval let sum=0 sum+={$first..$last}
echo $sum # 45
# seqを使う場合
# ブレース展開させるため、セパレータに ',' を指定する
first=0 last=9
eval let sum=0 sum+={$(seq -s, $first $last)}
echo $sum # 45
# 配列の要素をなめる場合
# eval+let+ブレース展開で合計を出す
# ブレース展開させるため、IFSに ',' を指定する
## これで "${a[*]}" したときに ',' 区切りになってブレース展開されるようになる
IFS_BACKUP=$IFS # 同一プロセスで動かすときは、IFSをもとに戻す必要あり
a=($(seq 0 9))
IFS=,
eval let sum=0 sum+={"${a[*]}"}
IFS=$IFS_BACKUP
echo $sum # 45
letコマンドの後に続けて式を書くと、(( ))
で括らなくても数値として式が評価される。
カンマ区切りじゃなくても式として評価されるので、ブレース展開と組み合わせることができる。
下に書いてある declare -i
、 local -i
でも大体同じことができる。
let a=10,b=20 c=30
echo $a $b $c # 10 20 30
もちろん再帰も書ける。
recursion='(i<10)?sum+=i,i++,recursion:sum'
let sum=0 i=0 recursion
echo $sum # 45
declare -i
で定義した変数は整数値として扱われる。
算術式を表す (())
はなくても式がちゃんと評価される。
declare -i hoge=2**62 piyo=2**63
echo $hoge # 4611686018427387904
echo $piyo # -9223372036854775808
hoge+=piyo; echo $hoge # -4611686018427387904
hoge=1+2+3; echo $hoge # 6
# これはエラーにならないが、代入されない
hoge='hoge'; echo $hoge # 6
# これはエラーになる。代入もされない。
hoge=1.5
# 再帰(ちょっとわかりにくいけど動く)
recursion='(i<10)?sum+=i,i++,recursion:sum'
declare -i sum=0 i=0
sum=$recursion
echo $sum # 45
(※ちゃんと調べてないので間違ってるかも)
local
は関数内でしか定義できない。
それ以外では、この二つは同じように振る舞う(と思う)。
どちらも、関数内で定義すればスコープは関数内で閉じる。
local -i a=0 # エラー
function hoge() {
local -i a=1+2+3
declare -i b=1+2+3
}
hoge
echo $a $b # 何も表示されない