課題
Ansibleのansible.builtin.userモジュールを利用してユーザを作成する際にパスワードを設定したい。シェルスクリスプトをwrapperとしてこれを実現するとき、パスワードの文字列はどのように受け渡せば良いか。例えばパスワードをシェルスクリプトの引数として渡すと入力履歴に残ってしまい困る。
解決策
printfとreadとsttyを利用してインタラクティブに入力させる。
- printf:ユーザにキー入力を促すプロンプトを表示
- read:ユーザのキー入力値を変数に格納
- stty:端末設定を変更してキー入力時のエコーバックを停止
え?上記は全部readで完結するって?いいかここはPOSIX原理主義の配下だ。readコマンドには-rオプションしか存在しないんだ(read独自拡張の-pオプションと-sオプションを参照のこと)。
実際に実装した結果は以下の通り。
stty -echo while true; do printf 'password > '; read -r first_pass; echo "" if [ -z "${first_pass}" ]; then echo "${0##*/}: not-empty password must be specified" 1>&2 continue fi printf 'retype > '; read -r second_pass; echo ""; if [ "${first_pass}" != "${second_pass}" ]; then echo "${0##*/}: password not matched" 1>&2 continue fi break done stty echo
たいていのシステムでそうなっているように、設定したい文字列を2回入力させて、適切にパスワードを設定できるロジックも組み込んだ。
序盤のsttyによりキー入力のエコーバックを無効にしているが、例えばCtrl-C(SIGINT)によりプロセスが途中終了した場合、エコーバック無効の設定が端末に残り、キー入力しても端末に表示されなくなってしまう。これを防ぐためにEXITシグナルをtrapしてstty echoを再設定するようにしておくことを推奨する。
所感
独自拡張のread使ったほうが楽。