Bash スクリプト内でユーザが任意の意味を付与できるリターンコードの範囲について(あるいは、Ansible の shell モジュールで冪等な処理を一タスクで書くには)

結論を先に書くと、79~125 です。

シェルスクリプトを書いていて、リターンコードによってその後の処理を分けたい場合などに、一体どの範囲のコードを好きに使って良いのかが分からなかったので調べたメモ。

Bash については、右記に記載があります。 ∥ Exit Codes With Special Meanings

たとえば、Bash のコードで言うと下記など。 ∥ shell.h

...

/* Special exit statuses used by the shell, internally and externally. */
#define EX_BINARY_FILE	126
#define EX_NOEXEC	126
#define EX_NOINPUT	126
#define EX_NOTFOUND	127

...

しかしそれとは別に、libc 界隈あたりでエラーをカテゴライズして exit コードを統一しようという動きもあったようで、それは /usr/include/sysexits.h に記載されています。

以上から、こと Bash シェルスクリプトに限って言うと好きに使って良いのは 79~125 となりますが、ひょっとすると “79” はもっと上がってくるかも知れないので、余裕を持って 100 くらいから使っておきましょうかね。

そもそもなぜそんなことが気になったかというと、Ansible の shell モジュールで、冪等な処理を一タスクで書きたかったからです。本当は、状態を確認するタスク、判別するタスク、状態を書き換えるタスクなどに分けるべきなのかも知れませんが、面倒くさいので、一タスクで出来れば良いなと。で、その場合は処理の結果、状態変更が「あった」「なかった」をリターンコードで表したかったので、好きに使えるレンジを調べていたんですね。

結果としては、以下のような感じか。Git の設定が規定のものではなかったら、規定のものに変更する Ansible Playbook タスクです。

- name: Git config (name, email) is set
  args:
    executable: /bin/bash
  shell: |
    set -eu
    rc=0
    if test \
     "$(git config --global user.name)" != "{{git_user_name}}" -o \
     "$(git config --global user.email)" != "{{git_user_email}}"
    then
      git config --global user.name "{{git_user_name}}"
      git config --global user.email "{{git_user_email}}"
      rc=100 # EX__MAX (78) < 100 < EX_NOEXEC (126)
    fi
    exit $rc
  register: ret
  failed_when: ret.rc != 0 and not 79 <= ret.rc <= 125
  changed_when: ret.rc == 100

要点としては、

  • 何をもってエラーとするか(failed_when)については Bash 的に、
  • 何をもって変更が加わったとするか(changed_when)については当該タスク内の取り決めに従って、

判断してやれ、と。この二点がルールを守っていれば良いということですかね。このイディオムで書き進めます。

あ、本件に関してはどちらでも良いのですが、Bourne Shell (/bin/sh) ではなく Bash で実行して欲しい場合には argument に executable を指定する必要がありますね。