3
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

シェルスクリプトの画面出力を考えてみる

この記事は チームスピリット Advent Calendar 2017 の7日目です。
僕はシェルスクリプトはあまり詳しくありませんが、クソツールをみんなに押し付ける自動化するのが大好きです!

ユーザーの都合を知る

まずユーザーの都合について知っておこう、というわけでユーザーが引数で画面出力について指定できるようにする。

--trace

処理の中で実行するコマンドの標準出力を全て表示する。実行シェルが bashzsh ならシェルのトレースも有効にする。

--no-color

データなどを出力する場合は、ANSIカラーの出力を無効にしてパイプライン処理を行いやすくする。(といっても、大抵のコマンドは自動判別してくれる。)

ログレベル

大抵のアプリケーションは TRACE, DEBUG, INFO, WARN, ERROR だが、ツールとしてのシェルスクリプトを書くという前提で使いやすいように再定義してみる。

  • WAIT - 時間のかかる処理が始まったことを通知するメッセージ。
  • DONE - 時間のかかる処理が終了したことを通知するメッセージ。
  • WARN - 処理の続行は出来るが好ましくない状況が発生したことを通知するメッセージ。
  • FAIL - 処理が失敗したことを通知するメッセージ。

テキストフォーマット

  • 画面分割で処理することも考慮して、一行を80文字以内で収めることが好ましい。
    エディターがVimであれば、行末に # vi: et sw=2 cc=80 と記述しておくと分かりやすい。

  • 処理の間に発生したメッセージは、行頭にインデントとして半角スペース2つを挿入する。

  • 処理の間に発生したメッセージの後は空行を1行入れる。

メッセージカラー

GatsbyJS

  • WAIT - 青
  • DONE - 緑
  • WARN - 黄色
  • FAIL - 赤
  • 強調 (処理対象のファイルパスなど) - 白
  • コマンドのサジェスト - 水色(シアン)

ここらへんはFacebookのCLIツールのパクリです。センスがないので丸パクリ。

実装してみる

ロガー

zsh でのロガーの実装例です。

autoload -Uz colors
colors

__chalk-internal() {
  local prefix="$1"
  local suffix="$2"
  local text="$3"
  echo "${prefix}${text}${suffix}"
}

chalk-em() {
  local text="$1"
  __chalk-internal "${fg[white]}" "${reset_color}" "$text"
}

chalk-cmd() {
  local text="$1"
  __chalk-internal "${fg[cyan]}" "${reset_color}" "$text"
}

__log-internal() {
  local label_prefix="$1"
  local label_suffix="$2"
  local text_prefix="$3"
  local text_suffix="$4"
  local label="$5"
  local text="$6"
  echo "${label_prefix} ${label} ${label_suffix} ${text_prefix}${text}${text_suffix}"
}

log-wait() {
  local text="$1"
  __log-internal \
      "${bg[blue]}${fg[black]}" \
      "${reset_color}" \
      "${fg[blue]}" \
      "${reset_color}" \
      "WAIT" \
      "$text"
}

log-done() {
  local text="$1"
  __log-internal \
      "${bg[green]}${fg[black]}" \
      "${reset_color}" \
      "${fg[green]}" \
      "${reset_color}" \
      "DONE" \
      "$text"
}

log-warn() {
  local text="$1"
  __log-internal \
      "${bg[yellow]}${fg[black]}" \
      "${reset_color}" \
      "${fg[yellow]}" \
      "${reset_color}" \
      "WARN" \
      "$text"
}

log-fail() {
  local text="$1"
  __log-internal \
      "${bg[red]}${fg[black]}" \
      "${reset_color}" \
      "${fg[red]}" \
      "${reset_color}" \
      "FAIL" \
      "$text"
}

ロガーを使ったスクリプト

cat <<EOM
This is an article of the 7th day of $(chalk-em "TeamSpirit Advent Calendar 2017")!
You're still not using Vim? Calm down and run $(chalk-cmd "vimtutor").

EOM

log-wait 'Checking $EDITOR ...'

log-warn "\$EDITOR is not 'vim' nor 'nvim'. That's weird..."

log-fail "$EDITOR is 'emacs'!"

cat <<EOM
  Don't worry, the problem has been fixed!
  We've just added a line $(chalk-cmd "alias emacs='vim'") to your $(chalk-em ".zshrc") :D

EOM

log-done 'Finish successfully!'

実行結果

./logger.zsh

オマケ

僕がシェルスクリプトでツールを作る際に心掛けていることです。

  • 処理内容が作業ディレクトリに依存しない。(ヘルパーの参照など)
  • 処理が冪等である。複数回実行や途中中断しても整合性が保たれているようにする。(必要に応じて trap を使う)
  • 環境や依存コマンドのチェックなどは最初に済ませておき、処理の途中でエラーになることを極力防ぐ。(可能ならロールバックする。)
  • 処理が複数ある場合は実行引数で特定の処理に限定することが出来る。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
3
Help us understand the problem. What are the problem?