You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ansible打印变量时因未定义变量报错,如何处理分支输出差异?

Ansible处理多分支Shell输出的变量提取问题

问题场景

编写了带if-else分支的Shell任务:当certlocation不是.key文件时,执行两条echo输出两行内容;如果是.key文件,仅执行一条echo输出单行内容。需要通用的debug语句适配两种分支场景,后续还要将方案应用到set_facts中赋值变量。

原任务代码

- name: Extract certificate or key information
  shell: >     
    if [[ "{{ certlocation }}" != *.key ]]; then
      cert_cn=$(openssl x509 -in "{{ certlocation }}" -noout -subject -nameopt sep_multiline)
      echo "~$cert_cn"
      cert_san=$(openssl x509 -in "{{ certlocation }}" -noout -ext subjectAltName)
      echo "~$cert_san"
    else
      cert_san=$(openssl x509 -in "{{ certlocation }}" -noout -ext subjectAltName)
      echo "~$cert_san"   
    fi
  register: cert_info
  delegate_to: localhost

- name: Print specific information
  debug:
    msg: |
      cert_cn: "{{ cert_info.stdout_lines[0].split('~')[1] | default('NA') }}"
      cert_san: "{{ cert_info.stdout_lines[1].split('~')[1] | default(cert_info.stdout_lines[0].split('~')[1]) }}"

报错信息

执行else分支(仅输出单行)时触发索引越界错误:

TASK [Print specific informations] ***************************************************************************************************************************Monday 29 July 2024  23:51:46 -0500 (0:00:00.022)       0:00:00.517 ***********
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: list object has no element 1\n\nThe error appears to be in '/home/wladmin/teststdoutlines.yml': line 17, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - name: Print lines using index from stdout_lines\n      ^ here\n"}

问题核心:直接访问stdout_lines[1]会触发致命的索引越界,此时*default*过滤器根本无法生效——Ansible在解析变量阶段就会报错终止。

解决方案

方法1:用*nth*过滤器安全访问列表元素

nth过滤器支持指定索引和默认值,当索引不存在时返回预设默认值,避免直接索引的报错:

- name: Print specific information
  debug:
    msg: |
      cert_cn: "{{ (cert_info.stdout_lines | nth(0, '')).split('~')[1] | default('NA') }}"
      cert_san: "{{ (cert_info.stdout_lines | nth(1, cert_info.stdout_lines[0])).split('~')[1] }}"

方法2:预处理输出为统一格式的列表

先将所有输出行拆分后整理成列表,再根据列表长度判断取值,逻辑更直观:

- name: Process certificate output
  set_fact:
    cert_parts: "{{ cert_info.stdout_lines | map('split', '~') | map('last') | list }}"

- name: Print specific information
  debug:
    msg: |
      cert_cn: "{{ cert_parts[0] if cert_parts | length > 1 else 'NA' }}"
      cert_san: "{{ cert_parts[1] if cert_parts | length > 1 else cert_parts[0] }}"

方法3:优化Shell脚本输出格式(推荐)

在Shell层统一输出格式,无论分支如何都输出两行内容,空行用占位符填充,后续无需处理分支差异:

- name: Extract certificate or key information
  shell: >     
    if [[ "{{ certlocation }}" != *.key ]]; then
      cert_cn=$(openssl x509 -in "{{ certlocation }}" -noout -subject -nameopt sep_multiline)
      echo "~$cert_cn"
      cert_san=$(openssl x509 -in "{{ certlocation }}" -noout -ext subjectAltName)
      echo "~$cert_san"
    else
      echo "~NA"
      cert_san=$(openssl x509 -in "{{ certlocation }}" -noout -ext subjectAltName)
      echo "~$cert_san"   
    fi
  register: cert_info
  delegate_to: localhost

- name: Print specific information
  debug:
    msg: |
      cert_cn: "{{ cert_info.stdout_lines[0].split('~')[1] }}"
      cert_san: "{{ cert_info.stdout_lines[1].split('~')[1] }}"

应用到set_facts

以方法2为例,将提取的变量赋值到Ansible事实中:

- name: Process certificate output
  set_fact:
    cert_parts: "{{ cert_info.stdout_lines | map('split', '~') | map('last') | list }}"
    cert_cn: "{{ cert_parts[0] if cert_parts | length > 1 else 'NA' }}"
    cert_san: "{{ cert_parts[1] if cert_parts | length > 1 else cert_parts[0] }}"

- name: Verify facts
  debug:
    var: [cert_cn, cert_san]

内容的提问来源于stack exchange,提问作者Ashar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 07:50:00