9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

dockerfileで長めの対話型コマンドをさばく(正規表現)

Last updated at Posted at 2021-06-13

※人生で初めて書いたQiitaですので、いたらぬ点があれば(やさしく)ご指摘ください..

長めの対話型コマンドをdockerfileに書きたい。

dockerfileで環境構築を自動化する際、自分はつまずきました。
dockerfile、expectファイル、正規表現3つを絡めて解説した記事があると便利かな、と思い、執筆した次第です。

全部 YES でOKなコマンドなら..

yes | (y 入力を必要とするコマンド) 

と記述すればOK。
(引用:コマンドの途中で聞いてくる yes を自動入力したい?それyesで出来るよ)

パスワード設定とか、YES以外の文字列を打つコマンドの場合は?

**「expectファイル」(.exp)**を使います! expect:「待ち受ける、予期する」 の意。
**「こう聞かれたらこう答えよ!」**という命令を書き込んでおくファイルです。

今回は、MYSQLをUbuntu(20.04)にインストール時に実行する、mysql_secure_installationコマンド を例に説明します。
こちらの記事の「ステップ2 — MySQLの設定」作業を、dockerfileで自動化することを想定します。
それぞれの設定項目の説明は割愛しますが、6つの質問に対し、
1. No
2. password (※自由に決めてください)
3. y
という、3種類の返答をしたい場合を考えます。(返答の順番はNo→password→y→y→y→y)

まず、dockerfileと、expectファイルはそれぞれこんな感じです。

Dockerfile
FROM ubuntu:20.04

RUN apt-get update && apt-get install -y sudo wget vim curl gawk make gcc

# 〜中略〜
# ↓mysqlのインストールあれこれ
RUN yes | sudo apt install mysql-server
RUN sudo usermod -d /var/lib/mysql/ mysql
RUN sudo service mysql start

# ↓本題のexp構文です!
COPY mysql_secure_installation.exp /
# dockerfileと同じディレクトリにあるmysql_secure_installation.expファイルの中身をコピー

RUN expect mysql_secure_installation.exp
# mysql_secure_installation.expの内容を実行

CMD [...]

COPY句で、Dockerfileと同じディレクトリに置いてあるmysql_secure_installation.expファイルを呼び出します。
次に、RUN expect mysql_secure_installation.expで、expファイルをそのまま実行。
気になるmysql_secure_installation.expの中身がこちら↓。

mysql_secure_installation.exp
# !/usr/bin/expect
 #↑これはシバン(shebang)と呼ばれる決まり文句みたいなもの。「インタプリタにexpectを指定」してるそうです(^^; 
 #参考:https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14146257229

set ans1 No  #"返答1"は"No"をセット
set ans2  password  #"返答2"は"password"をセット
set ans3 y #"返答3"は"y"をセット

spawn sudo mysql_secure_installation #実行するコマンドをspawnで指定

expect {
        -regexp ".*VALIDATE PASSWORD.*" { #正規表現でキーワードをキャッチ、質問を特定
                send "${ans1}\n" #そのキーワード("VALIDATE PASSWORD")を含む質問が来たら、ans1(No)を送る(send)
                exp_continue
        }
        -regexp ".*set the password for root.*" {#同じく正規表現でキーワードをキャッチ、質問を特定
                send "${ans2}\n" #同じく、キーワード("set the password for root")を含む質問が来たら、ans2(password)を送る
                exp_continue
        }
        "Re-enter new password:" { #この質問は短かったので、正規表現は使わずに、一字一句全部指定して質問を特定しています。
                send "${ans2}\n"
                exp_continue
        }
         -regexp ".*anonymous users.*" {
                send "${ans3}\n"
                exp_continue
        }
         -regexp ".*root login remotely.*" {
                send "${ans3}\n"
                exp_continue
        }
        -regexp ".*test database and access to.*" {
                send "${ans3}\n"
                exp_continue
        }
        -regexp ".*privilege tables.*" {
                send "${ans3}\n"
                exp_continue
        }
}
exit 0

簡単に解説すると、

まず冒頭の3行、SET〜で3種類の返答を規定します。

次に、spawn句で、実行するコマンド(=expectで返答を待ち受けたいコマンド=sudo mysql_secure_installation)を指定します。

そしてexpect句で、待ち受ける質問を特定する方法と、その質問に対する返答(ans1〜3のいずれか)を指定します。
-regexpは、正規表現を使いますよ、というオプションです。
(regexp:regular expression,直訳で正規表現)

dockerfileを書く時は、まず適当なコンテナ等でコマンドを一通り試してから書くはずで、本来は**対話型の質問は一字一句わかってるわけだし、正規表現なんてなんで使うの?**とそもそもの疑問を持たれた方もいるかもしれません。
自分はそうはいっても、そこそこ長い質問文を完全一致でexpect句に書くのは怖かったので(一文字足りないとか、スペースの有無とか)、regexpオプションを使いました。
キーワードだけ書くようにすれば、expectファイルもグッと見やすくなりますし、、ですが、その質問固有のキーワードを抜き出すことに注意してください!他の質問にも現れるキーワードは当然避ける!

exp_continueについて。本来ならばexpectは、対話がマッチした時点で指定したアクションを実行し、アクション実行後はexpectコマンドは終了、次のコマンドへ移るのですが、exp_continueを書くことで、マッチ&アクション後もexpectから抜けずにexpectの処理を続けることができます(この点参考にさせていただいた投稿)

mysql_secure_installationのコマンドが一通り終わったら、exit 0で正常終了。

以上、長くなりましたが、dockerfile,expect,regexを絡めて使う参考例でした。
その他参考にしたサイト1 2

9
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?