断言模块:如果只找到一次字符串,是否有可能失败?

assert module: Is it possible to fail if string is found only once?

我正在解析 Windows 系统(使用 winrm)上的文本文件的一些输出。如果它没有多次看到 'test' 这个词,我试图让剧本失败。

如果它至少看到一次 'test' 这个词,这就是我所拥有的,仅用于继续剧本:

- name: "Windows Output | Parse file"
  win_shell: |
    (Get-Content C:\TEST\output.txt)
  register: 'testing_parse'

- name: "File output | verification"
  assert:
    that:
      - "'test' in testing_parse.stdout"
    fail_msg: "Exiting now."
    success_msg: "Proceeding with task."

我认为下面的方法行得通,但行不通:

- name: "File output | verification"
  assert:
    that:
      - "'test' in testing_parse.stdout >= '2'"
    fail_msg: "Exiting now."
    success_msg: "Proceeding with task."

如何修复我的 assert 任务,使其满足我的要求?

TL;DR

- name: "File output | verification"
  vars:
    watch_regex: '(?:^|\W)(test)(?:$|\W)'
  assert:
    that:
      - testing_parse.stdout | regex_findall(watch_regex) | length > 1
    fail_msg: "Exiting now."
    success_msg: "Proceeding with task."

为什么 in 测试不合适

in return 一个布尔值

让我们先看看我们在几种情况下从 in 测试中得到了什么

$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
    -e "my_test='a test'"

localhost | SUCCESS => {
    "msg": true
}

$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
    -e "my_test='a test and an other test'"

localhost | SUCCESS => {
    "msg": true
}

$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
    -e "my_test='no word we look for'"

localhost | SUCCESS => {
    "msg": false
}

如您所见,它始终 return 一个布尔值,具体取决于大海捞针中是否存在针。

in没有找到字

还要注意 in 不是很擅长找词(因为你提到过),如下所示:

# It will match a substring in a middle of an other word
$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
    -e my_test="blahtesttoto"

localhost | SUCCESS => {
    "msg": true
}

# It will not match a word inside an element of a list...
$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
    -e '{"my_test":["this is", "a test"]}'

localhost | SUCCESS => {
    "msg": false
}

# ... but only an exact match of an element of a list
$ ansible localhost -m debug -a "msg={{ 'test' in my_test }}" \
    -e '{"my_test":["a", "test"]}'

localhost | SUCCESS => {
    "msg": true
}

反正你的表达方式不对

最后,让我们看看你写表达式时的尝试:

'test' in testing_parse.stdout >= '2'

这意味着:

  1. 检查字符串 testing_parse.stdout 在词法上是否优于或等于字符串 '2'.
  2. 现在检查是否在前面的布尔结果中找到字符串 'test'

正如您现在可以根据解释猜测的那样,这绝对不可能 return true.

regex_findall 救援

查找特定单词的一种方法是使用正则表达式。下面将查找 test 单词,即 "test" 字符串前后跟任何非单词字符(行尾、行首、白色 space、制表符、标点符号.. ..).

(?:^|\W)(test)(?:$|\W)

如果您不熟悉正则表达式,请参阅 this answer for a specific explanation and https://www.regextutorial.org/ 获取一般资源(我不是附属的,您可以使用您喜欢的搜索引擎找到其他人)。

regex_find_all filter 可以 return 将正则表达式与字符串的所有匹配项放入列表中

$ ansible localhost -m debug \
    -a "msg={{ my_test | regex_findall('(?:^|\W)(test)(?:$|\W)') }}" \
    -e "my_test='test'"

localhost | SUCCESS => {
    "msg": [
        "test"
    ]
}

$ ansible localhost -m debug \
    -a "msg={{ my_test | regex_findall('(?:^|\W)(test)(?:$|\W)') }}" \
    -e "my_test='test and an other test but not blahtesttoto yet test'"

localhost | SUCCESS => {
    "msg": [
        "test",
        "test",
        "test"
    ]
}

$ ansible localhost -m debug \
    -a "msg={{ my_test | regex_findall('(?:^|\W)(test)(?:$|\W)') }}" \
    -e "my_test='notest'"

localhost | SUCCESS => {
    "msg": []
}

一旦我们有了它,我们只需要计算 returned 列表中带有 length 过滤器的元素数。

$ ansible localhost -m debug \
    -a "msg={{ my_test | regex_findall('(?:^|\W)(test)(?:$|\W)') | length }}" \
    -e "my_test='test and an other test but not blahtesttoto yet test'"

localhost | SUCCESS => {
    "msg": "3"
}

最后我们可以根据您的要求修复您的 assert 任务:

- name: "File output | verification"
  vars:
    watch_regex: '(?:^|\W)(test)(?:$|\W)'
  assert:
    that:
      - testing_parse.stdout | regex_findall(watch_regex) | length > 1
    fail_msg: "Exiting now."
    success_msg: "Proceeding with task."