めもちょー

メモ帳代わりに使っています。

シェルスクリプトをより良く書く

linterを使う

$ sudo apt install shellcheck
$ shellcheck hoge.sh

github.com

条件式 や算術演算子を使う

if 文の条件を書く場合 [ ] (testコマンド)よりも (条件式)を使うべき。
testコマンドの場合、条件式を表す<>, (), &&, ||をエスケープする必要があったが、条件式の場合はそのまま書ける。
AND条件やOR条件を表す-aや-oの演算子がそれぞれ&&, || で書くことができ

# test
[ $i -lt 10 -a $i -lt 15 ] && ls
# 条件式
[[ $i -lt 10 && $i -lt 15 ]] && ls
# 算術演算式
(( i <10 && i < 15 )) && ls

条件の各数式に括弧をつける場合を見るとより、エスケープの必要性の違いが分かる。

# test 
[ \( $i -lt 10 \) -a \( $i -lt 15 \) ] && ls
# 条件式
[[ ($i -lt 10) && ($i -lt 15) ]] && ls
# 算術演算式
(( (i<10) && (i<15) )) && ls

ifを使わないで&&や||を代用する

&&で接続された場合、前方が真であれば後方が実行される。後方をグループコマンドで書くと、if文を使わなくても書ける。

cmp -s file1 file2 && {
    echo '重複を削除します'
    rm -f file2
}

`||`で接続された場合、前方が偽であれば後方が実行される。後方をグループコマンドで書くと、if文を使わなくても書ける。

test -f file1 || {
    echo 'file1が存在しません'
    exit 1
}

# 条件式
[[ -f README.md ]] && cat README.md

elseを含むif文の構造
testコマンドが真ならば&&以降が評価され, 偽なら `||`が評価される。

[ $i -lt 10 ] && {  # "$i"でもok
   echo 'iの値は10未満です'
} || {
    echo 'iの値は10以上です'
}

# 条件式で書くと
[[ $i -lt 10 ]] && echo "10未満"

# 算術式で書くと
((i < 10)) && echo "10未満"

whileでwhileの横にtestコマンドを使わない

sumの初期値は0なのに最初にsum<100という明らかに真の評価を行ってしまっている。

i=0
sum=0
while [ "$sum" -lt 100 ]
do
   i=`expr "$i" + 1`
   sum=`expr "$sum" + "$i"`
done

whileの最後にtestコマンドを書けば最初の評価を行わずに1回目のループに行くことができる。

i=0
sum=0
while
    i=`expr "$i" + 1`
    sum=`expr "$sum" + "$i"`
    [ "$sum" -lt 100]
do :; done

パラメタ展開を使いこなす

Shellの文字列変換操作は意外と優秀

# varが未定義 or 空文字ならばdefaultに展開される
# : は空文字
# - は未定義
${var:-default}

# varが未定義ならばdefaultに展開される
${var-default}

# varが空文字列ならばdefaultに展開される
${var:default}

# varが未定義 or 空文字ならばdefaultがvarに代入される
${var:=default}

# varが未定義 or 空文字列の場合にerrorを出力して終了する
${var:?error}

# varが定義されていればvalueの値に展開される
${var:+value}

# varの文字列長に展開される
${#var}

# 部分文字列
${var:offset:length}

# varの左側から最初に一致したpattern1がpattern2に置き換えられる
${var/pattern1/pattern2}

# varの左側からすべてのpattern1がpattern2に置き換えられる
${var//pattern1/pattern2}