Boune Shell コーディング規約

ライセンス

Copyright 2017 Yuuki Enomoto

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

目次

変数展開

変数は${}をつけて展開します.

val="xxx"
echo ${val}

ただし以下の特殊な変数を展開するさいは${}をつけません.

関数

関数は以下のように書きます.

#
# 関数の例.
#
function()
{
  # この中に処理を書く.
}

コメントも忘れずに書きましょう.

関数内のローカル変数

関数内で変数を宣言して使う場合はlocal修飾子をつけます.

function()
{
  local i
  i="aaa"
}

コメント

シェルスクリプトは非常に読みにくいのでコメントは必須です. 関数の説明を書く場合は

#
# description of function
#
function()
{
}

のようにコメントの始まりと終わりに1行空行をいれます.複数行にまたがる場合は

#
# description of function
# description of function
# description of function
#
function()
{
}

とします.

一方,関数の説明以外であれば

# ......をする.
process || return 1

のように1行で済ませます.しかし2行以上にまたがる場合は

#
# ......のため,
# ......をする.
#
process || return 1

とコメントの始まりと終わりを1行ずつ空けてください.

コマンド

シェルスクリプト内で使うコマンドは,あらかじめ大文字の同名変数に絶対パスを格納して使います.

LS="/bin/ls"
SORT="/usr/bin/sort"
UNIQ="/usr/bin/uniq"

${LS} | ${SORT} | ${UNIQ}

最大限,移植性を考慮するなら以下のように書き, initスクリプトなどで絶対パスを調べながら置換していくとよいでしょう.

LS="@LS@"
SORT="@SORT@"
UNIQ="@UNIQ@"

${LS} | ${SORT} | ${UNIQ}
#!/bin/sh

#
# which コマンドはPOSIXユーティリティではないので,
# このようにtype コマンドのwrapperを書く必要がある.
#
_which()
{
  ans=$(LANG=C LC_ALL=C type "$1" 2>/dev/null) || exit $?
  case "$1" in
    */*) printf '%s\n' "$1" ; exit ;;
  esac
  case "$ans" in
    */*) printf '%s\n' "/${ans#*/}"; exit ;;
  esac
  printf '%s\n' "$1"
}

sed -e "s%@LS@%`_which ls`%g" \
    -e "s%@SORT@%`_which sort`%g" \
    -e "s%@UNIQ@%`_which uniq`%g" \
    /path/to/target_script

ちなみに,ここまでやる必要はあまりありません.

インデント

インデントはスペース2文字を使います.

行の長さ

1行80文字までとします.80文字を超える場合はバックスラッシュを使って行を分割してください.

for文

for文は以下のように書きます.

for i in ${LS}; do
done

if文

if文は以下のように書きます.

if []; then
fi

しかし可能な限りtestコマンドで済ませます.

${PWD_CMD}="/bin/pwd"
TEST="/bin/test"

# これならif文を使わなくてもよい.
if [ $? -eq 0 ]; then
  ${PWD_CMD}
fi

# 上の処理はこう書ける.
${TEST} $? -eq 0 && ${PWD_CMD}

エラー処理

エラー処理をしすぎることはないので可能な限り書いてください.

SED="/usr/bin/sed"

#
# 簡単なエラー処理の例.
# sedの処理に失敗すると,sedの実行終了ステータスを返して
# プログラムが終了する.
#
${SED} 's/before/after/g' ./text.txt || return $?

エラー処理用の関数を書いてもいいでしょう.

CAT="/bin/cat"
KILL="/bin/kill"
SED="/usr/bin/sed"

pid=$$

bomb()
{
  ${CAT} >&2 <<MESSAGE

ERROR: $@
*** PROCESS ABORTED ***
MESSAGE
  ${KILL} ${pid}
  exit 1
}

${SED} 's/before/after/g' ./text.txt || bomb "sed failed."

エラー処理をしっかりと書いていれば,デバッグも楽になります.