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
相关产品推荐
相关产品推荐

