てくなべ (tekunabe)

ansible / network / automation

[Ansible] 内包表記のようにリストの各要素に処理して別の要素を生成する

はじめに

Ansible で、リストの中の各要素に対して一律でなにかの処理をして、別の要素を生成したいときがあります、

Python の内包表記でリストを生成するイメージです。

全く同じというわけではないのですが、map フィルターを利用すると近いことが少しできることを知りました。

少し説明しにくいので、 Python で書く場合と比較していくつかサンプルをご紹介します。

  • 動作確認環境
    • Ansible 2.9.9
    • Jinja2 2.11.2


各要素に文字列を結合する

Python

Python だとこのイメージ。

>>> ['Hello ' + i for i in ['funa', 'kingyo', 'same']]
['Hello funa', 'Hello kingyo', 'Hello same']

Ansible

Ansible だとこのような感じです。

もっとシンプルにする方法があると良いのですが、正規表現を使う方法しか思いつきませんでした。

    - name: naihou
      debug: 
        msg: "{{ ['funa', 'kingyo', 'same'] | map('regex_replace', '^(.+)$', 'Hello \\1') | list }}"
  • 結果
ok: [localhost] => {
    "msg": [
        "Hello funa",
        "Hello kingyo",
        "Hello same"
    ]
}

なお、map フィルターに regex_replace を渡せることは以下の記事で知りました。ありがとうございます。 Jinja2のmapフィルタの中身に引数が必要なフィルタを指定する - Qiita


各要素を大文字にする

Python

Python だとこのイメージ。

>>> [i.upper() for i in ['funa', 'kingyo', 'same']]
['FUNA', 'KINGYO', 'SAME']

Ansible

Ansible だとこのような感じです。upper フィルターを併用します。

    - name: naihou
      debug: 
        msg: "{{ ['funa', 'kingyo', 'same'] | map('upper') | list }}"
  • 結果
ok: [localhost] => {
    "msg": [
        "FUNA",
        "KINGYO",
        "SAME"
    ]
}


各要素を int にキャストする

Python

Python だとこのイメージ。

>>> [int(i) for i in ['111', '222', '333']]
[111, 222, 333]

Ansible

Ansible だとこのような感じです。

    - name: naihou
      debug: 
        msg: "{{ ['111', '222', '333'] | map('int') | list }}"
  • 結果
ok: [localhost] => {
    "msg": [
        111,
        222,
        333
    ]
}


おわりに

リスト loop で回しながら、処理して set_fact する方法でもできますが(こちらのほうが柔軟)、一つのタスクするまでもないときには、このように内包表記チックにやるのもいいと思います。

なお、Jinja2 として、内包表記そのものはサポートしていないとドキュメントに明記されています。