Ansible Playbook内での文字列操作の難しさ
Playbook内でシェル芸をしようとすると、Ansibleから警告が出て鬱陶しいです。しかし、かといってPlaybookの中で文字列操作をするには、Jinja2というテンプレートを使いこなす必要があります。これが、なかなかの曲者で、PythonのようでPythonとは異なる作法がところどころに見られ、ドキュメントもAnsibleのものだけでは不十分で、Jinja2公式のものを合わせても物足りません。
そこで、本記事はPlaybook内でシェル芸もどきをするのに必要なノウハウに絞って解説します。
Task実行中に変数に値を代入したい
set_factモジュールを使います。
- tempfile:
state: directory
register: working_directory_info
- set_fact:
working_directory: '{{ working_directory_info.path }}'
この例だと、working_directoryという変数を宣言しています。
ページの内容を変数に入れる
uriモジュールを使用します。get_urlモジュールは、ページの内容をファイルとして保存することしかできません。もう1つの注意点として、return_contentをtrueにしないと、ページの内容が変数に保存されないので、注意してください。
- uri:
url: http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/
return_content: yes
register: repo
- debug:
msg: '{{ repo.content }}'
コマンドの出力結果を変数に入れる
command・shellなどのモジュールを実行し、結果を変数に保存します。その変数の要素.stdout・.stdout_lines(改行で区切られた配列)が実行したコマンドの出力結果(標準出力)となります。なお、末尾の改行は削除されるようです。
- command: runlevel
register: runlevel_result
changed_when: no
- debug:
# ↓現在のrunlevelを出力
msg: '{{ runlevel_result.stdout.split(" ") | last }}'
文字列をシェルスクリプトのように行単位で処理したい
まず、(文字列).splitlines()で行ごとに分割します。
grep
select("match", "(正規表現)")というフィルタで正規表現にマッチする行だけを抽出することができます。
head・tail
head -n 1はfirstというフィルタで書き換えられます。tail -n 1はlastです。
sed
sed 's/(置換前)/(置換後)/(オプション)'というパターンに対しては、regex_replace("(置換前)", "(置換後)")というフィルタを使用します。grep -o相当のことをする場合は、丸括弧及び\1,\2,・・・を上手く活用してください。
スペース等による文字列の分割
変数.split("(区切り文字)")で行います。スペースが2つ以上並んでいる文字列に対しスペースで分割すると、空文字が要素に混じります。{{ 変数.split(" ") | reject("eq", "") }} で取り除きます。rejectはPythonのfilterに似ていますが、要素の除去条件が逆です。(Trueで除去)
文字列の連結
フィルタjoin("\n")を使用します。
テンプレート内で引用符2種("・')を両方とも使いたい
ブロックスタイル文字列を使用するのですが、制御記号には必ず最終行の改行を取り除く-を指定してください。それ以外の行の改行を保存する|か、スペースに置き換える>かはお好みですが、基本は>でいいと思います。
- set_fact:
variable_from_complex_template: >-
{{ ansible_distribution_version | replace(".", "'") }}