【问题标题】:how to select regex matches in jinja2?如何在 jinja2 中选择正则表达式匹配?
【发布时间】:2016-12-12 06:18:59
【问题描述】:

玩具示例

基本上,我想做这样的事情:

['hello', 'apple', 'rare', 'trim', 'three'] | select(match('.*a[rp].*'))

这会产生:

['apple', 'rare']

我在说什么?

match 过滤器和 select 过滤器。我的问题源于选择过滤器仅支持一元“测试”。

我正在使用 Ansible 1.9.x。

我的实际用例

...更接近:

lookup('dig', ip_address, qtype="PTR", wantList=True) | select(match("mx\\..*\\.example\\.com"))

所以,我想获取与 IP 关联的所有 PTR 记录,然后过滤掉所有不适合给定正则表达式的记录。我还想确保结果列表中只有一个元素,并输出该元素,但这是另一个问题。

【问题讨论】:

    标签: ansible jinja2 template-engine


    【解决方案1】:

    这样可以吗?

    ---
    - hosts: localhost
      connection: local
      vars:
        my_list: ['hello', 'apple', 'rare', 'trim', 'three']
        my_pattern: '.*a[rp].*'
      tasks:
        - set_fact: matches="{{ my_list | map('regex_search',my_pattern) | select('string') | list }}"
          failed_when: matches | count > 1
        - debug: msg="I'm the only one - {{ matches[0] }}"
    

    更新:它是如何工作的......

    • map 应用 过滤器 – 过滤器不是是/否的东西,它们适用于 输入列表的每个项目和修改项目的返回列表。我用 regex_search 过滤器,在每个项目中搜索模式并返回 如果找到则匹配,如果没有匹配则为 None。所以在这一步我得到 此列表:[null, "apple", "rare", null, null]

    • 然后我们使用select,它适用于测试——测试是是/否的事情,所以 他们根据所选测试减少列表。我使用string 测试,即 当列表的项目是字符串时为真。所以我们得到:["apple", "rare"]

    • mapselect给了我们一些内部python类型,所以我们转换成list 毕竟通过应用list 过滤器。

    【讨论】:

    • 不。一方面,regex_search 不存在;你只需使用search。另一方面,map 在这里返回一个布尔值列表,因此select('string') 将始终产生一个空列表。
    • 啊,错过了你在 1.9... 切换到现代工具 :)
    • 为什么map 返回一个布尔值列表?它应用过滤器并为您提供过滤项目的列表。在我的示例中,它为您提供找到匹配项的字符串和未找到匹配项的 null。
    • 正则表达式 search 或正则表达式 match 背后的想法是,您想知道某些内容是否出现在搜索中,或者是否匹配。由于这些是是/否问题,因此每种情况下的答案都是布尔值。但是,如果您想替换,则可以执行 regex_replace 之类的操作。
    • @ParthianShot 我已经用一些解释更新了我的答案。
    【解决方案2】:

    如果你想在 Ansible 中过滤列表,我发现了以下技巧(获取具有空值的列表并与空列表有所不同):

    ---
    - hosts: localhost
      connection: local
      vars:
        regexp: '.*a[rp].*'
        empty: [null]
      tasks:
        - set_fact: matches="{{ ['hello', 'apple', 'rare', 'trim', 'three'] | map('regex_search',regexp)  | list|difference(empty) }}"
        - debug: msg="{{ matches }}"
    

    这是输出:

    ok: [localhost] => {
        "msg": [
            "apple", 
            "rare"
        ]
    }
    

    【讨论】:

      【解决方案3】:

      这种设计模式对我有用:

      ----
      - hosts: localhost
        connection: local
        vars:
          my_list: ['hello', 'apple', 'rare', 'trim', "apropos", 'three']
          my_pattern: 'a[rp].*'
        tasks:
          - set_fact:
              matches: "{%- set tmp = [] -%}
                        {%- for elem in my_list | map('match', my_pattern) | list -%}
                          {%- if elem -%}
                            {{ tmp.append(my_list[loop.index - 1]) }}
                          {%- endif -%}
                        {%- endfor -%}
                        {{ tmp }}"
          - debug:
              var: matches
            failed_when: "(matches | length) > 1"
      

      【讨论】:

      • 我发现这个答案很有帮助,因为它让我相信参数化的 filter 真的丢失了,我很难相信。这很恶心,但至少它成功了,让我继续前进......
      • This is disgusting 这里没有参数。模板中的这种通用编程绝对是一种反模式。当我这样做时,我在我的积压工作中打开了一个问题单,以将该逻辑移到其他地方。谢天谢地,我几乎不用开这样的票。
      • @ParthianShot 上面的代码将引发TemplateRuntimeError 异常,因为match 不是过滤器。同样如您之前的答案中所述,我不明白您为什么会引发错误,因为匹配列表包含多个元素(failed_when: "(matches | length) > 1"),这使得剧本在工作时失败
      • 值得注意的是,我在 2016 年写了这个答案,即使那时我也没有使用最新版本的 ansible。此解决方案可能仅适用于 ansible 1.x 版本,因为 ansible 2.x 升级破坏了一切。
      【解决方案4】:

      你可以像这样使用select

      ['hello', 'apple', 'rare', 'trim', 'three'] | select('match', '.*a[rp]')
      

      这会产生:

      ['apple', 'rare']
      

      match 使用re.match 实现,因此匹配字符串的开头。因此,您需要 .* 在正则表达式的开头,而不是结尾。

      或者您可以使用search 完全避免.*

      ['hello', 'apple', 'rare', 'trim', 'three'] | select('search', 'a[rp]')
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-28
        • 2023-03-17
        • 1970-01-01
        相关资源
        最近更新 更多