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

Ansible报错'dict object'无stdout_lines属性,替换results仍未解决

问题描述

作为Ansible新手,复制扩展第三方维护的Playbook后,通过以下调试命令运行:

/home/.../ansible-venv/bin/ansible-playbook -vvv playbooks/dump_data_into_csv.yml >/dump_data_into_csv.log 2>&1
  • 注释- hosts: myhosts,启用指定单个/多个主机的配置时,Playbook运行正常;
  • 启用- hosts: myhosts配置时,执行到第64行的"Dump data to csv"任务时报错:

    The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'stdout_lines'

尝试将代码中的{% if host.grep_output.stdout_lines %}和{% for line in host.grep_output.stdout_lines %}替换为{% if host.grep_output.results %}和{% for line in host.grep_output.results %},但报错依旧。

相关Playbook代码如下:

- hosts: myhosts
# - hosts: 
#     - myhost-010_c1
#     - myhost-010_c3
#     - myhost-014_c4
    
  order: sorted
  gather_facts: False

  vars:
    current_date: "{{ '%Y-%m-%d' | strftime }}"

  tasks:

    - name: Grep over syslog
      become: true
      ansible.builtin.shell:          
        cmd: |
          tomorrow_date=$(date '+%Y-%m-%d' -d "$input_date + 1 day")
          six_days_ago_date=$(date '+%Y-%m-%d' -d "$input_date - 6 days")       

          last_seven_days_syslogs=$(find /var/log/ -type f -name "syslog*" -newermt "$six_days_ago_date" ! -newermt "$tomorrow_date" | sort)

          sudo zgrep -aHF "my_pattern" $last_seven_days_syslogs | cut -d'[' -f 1

      register: grep_output
      ignore_errors: true
      no_log: true


    - name: Dump data to csv
    #  ^ this is  line 64, column 7
      copy:
        dest: /tmp/data_{{ '%Y-%m-%d' | strftime }}.csv
        content: |
          {{ ['idm', 'idc', 'mese', 'giorno', 'orario'] | map('trim') | join(';') }}
          {% for host in hosts_list %}
          {% set idm = host.inventory_hostname.split('_')[0].split('-')[1] %}
          {% set idm_padded = '%03d' % idm|int %}
          {% set idm_padded = '"' + idm_padded + '"' %}
          {% set idc = host.inventory_hostname.split('_')[1].upper() %}
          {% if host.grep_output.stdout_lines %}
          {% for line in host.grep_output.stdout_lines %}
          {% set month = line.strip().split(':')[1].replace('  ', ' ').split(' ')[0] %}
          {% set day = line.strip().replace('  ', ' ').split(' ')[1] %}
          {% set time = line.strip().replace('  ', ' ').split(' ')[2] %}
          {{ [idm_padded, idc, month, day, time] | map('trim') | join(';') }}
          {% endfor %}
          {% endif %}
          {% endfor %}
          
      vars:
        hosts_list: "{{ ansible_play_hosts | map('extract', hostvars) | list }}"
      delegate_to: localhost
      register: csv_content
      run_once: yes
问题分析

问题出在**myhosts组中存在执行失败/不可达的主机**时,host.grep_output的结构发生了变化:

  • 执行成功的主机,grep_output包含stdout_lines字段;
  • 执行失败(比如主机不可达、命令报错且ignore_errors: true)的主机,grep_output中没有stdout_lines,反而会有failed、msg等字段,直接访问stdout_lines就会触发变量未定义错误。

你替换成results的操作是错误的——results是任务使用loop循环时才会生成的结构,当前grep_output是单任务的注册结果,不存在results字段。

解决方案

方案1:模板中增加状态判断

修改"Dump data to csv"任务的模板部分,先判断任务执行状态,再访问stdout_lines,同时增加行格式校验避免拆分出错:

{{ ['idm', 'idc', 'mese', 'giorno', 'orario'] | map('trim') | join(';') }}
{% for host in hosts_list %}
{% set idm = host.inventory_hostname.split('_')[0].split('-')[1] %}
{% set idm_padded = '%03d' % idm|int %}
{% set idm_padded = '"' + idm_padded + '"' %}
{% set idc = host.inventory_hostname.split('_')[1].upper() %}
{# 先判断任务是否成功,再检查stdout_lines是否存在且非空 #}
{% if host.grep_output is defined and not host.grep_output.failed and host.grep_output.stdout_lines is defined and host.grep_output.stdout_lines %}
{% for line in host.grep_output.stdout_lines %}
{% set line_parts = line.strip().replace('  ', ' ').split() %}
{# 校验行格式,避免拆分后字段不足 #}
{% if line_parts | length >=3 %}
{% set month = line_parts[0] %}
{% set day = line_parts[1] %}
{% set time = line_parts[2] %}
{{ [idm_padded, idc, month, day, time] | map('trim') | join(';') }}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

方案2:任务注册时设置默认值

修改"Grep over syslog"任务,用failed_when: false替代ignore_errors: true(更精准控制失败状态),并给失败主机设置默认空的stdout_lines:

- name: Grep over syslog
  become: true
  ansible.builtin.shell:          
    cmd: |
      tomorrow_date=$(date '+%Y-%m-%d' -d "$input_date + 1 day")
      six_days_ago_date=$(date '+%Y-%m-%d' -d "$input_date - 6 days")       

      last_seven_days_syslogs=$(find /var/log/ -type f -name "syslog*" -newermt "$six_days_ago_date" ! -newermt "$tomorrow_date" | sort)

      sudo zgrep -aHF "my_pattern" $last_seven_days_syslogs | cut -d'[' -f 1

  register: grep_output
  failed_when: false
  no_log: true
  # 给失败的主机设置默认空的stdout_lines
  vars:
    grep_output: "{{ {'stdout_lines': []} if grep_output.failed else grep_output }}"

这样模板中只需判断host.grep_output.stdout_lines是否非空即可,无需额外判断任务状态。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 03:01:06