如何在Ansible Playbook中通过循环将Shell命令输出写入文件
问题描述
我想把Shell命令的输出加载为变量,结合循环将每个用户的处理结果写入文件。具体是把循环的用户值传入getent命令,执行getent passwd {{item}} |sed -E 's/:[^ ]*:(.)[^ ]* ([^:]*).*/\L,\1\2/'后,期望将结果user1,uone和user2,utwo逐行写入文件,但当前Playbook写入了错误内容。
原Playbook脚本
- hosts: all remote_user: root gather_facts: no tasks: - name: Add user variable ansible.builtin.shell: getent passwd {{item}} |sed -E 's/:[^ ]*:(.)[^ ]* ([^:]*).*/\L,\1\2/' register: getent_result ignore_errors: true loop: - user1 - user2 - name: Add user to file.txt ansible.builtin.lineinfile: path: "/path/to/file/file.txt" regexp: '^{{item}}' line: '{{getent_result}}' insertafter: 'EOF' create: yes loop: - user1 - user2
实际写入内容
{'msg': 'All items completed', 'changed': True, 'results': [{'stderr_lines': [], 'ansible_loop_var': 'item', 'end': '2023-05-18 13:30:53.434569', 'stderr': '', 'stdout': 'user1,uone', 'changed': True, 'rc': 0, 'start': '2023-05-18 13:30:53.420151', 'failed': False, 'cmd': "getent passwd user1 |sed -E 's/:[^ ]*:(.)[^ ]* ([^:]*).*/\\L,\\1\\2/'", 'item': 'user1', 'delta': '0:00:00.014418', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, 'strip_empty_ends': True, '_raw_params': "getent passwd user1 |sed -E 's/:[^ ]*:(.)[^ ]* ([^:]*).*/\\L,\\1\\2/'", 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin_add_newline': True, 'stdin': None}}, 'stdout_lines': ['user1,uone'], 'ansible_facts': {'discovered_interpreter_python': '/usr/bin/python'}}, {'stderr_lines': [], 'ansible_loop_var': 'item', 'end': '2023-05-18 13:30:54.332020', 'stderr': '', 'stdout': 'user2,utwo', 'changed': True, 'rc': 0, 'failed': False, 'cmd': "getent passwd user2 |sed -E 's/:[^ ]*:(.)[^ ]* ([^:]*).*/\\L,\\1\\2/'", 'item': 'user2', 'delta': '0:00:00.011729', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': True, 'strip_empty_ends': True, '_raw_params': "getent passwd user2 |sed -E 's/:[^ ]*:(.)[^ ]* ([^:]*).*/\\L,\\1\\2/'", 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin_add_newline': True, 'stdin': None}}, 'stdout_lines': ['user2,utwo'], 'start': '2023-05-18 13:30:54.320291'}]}
期望写入内容
user1,uone user2,utwo
错误原因
当用loop注册变量时,getent_result是一个包含整个循环执行结果的复合对象,而非单个用户的命令输出。第二个任务直接引用{{getent_result}},会把整个对象的字符串表示写入文件,而不是提取每个用户对应的stdout值。
解决方案
修改第二个任务,循环遍历getent_result.results(存储每个循环项结果的列表),提取每个结果中的stdout字段写入文件:
- hosts: all remote_user: root gather_facts: no tasks: - name: 获取用户处理结果 ansible.builtin.shell: getent passwd {{item}} |sed -E 's/:[^ ]*:(.)[^ ]* ([^:]*).*/\L,\1\2/' register: getent_result ignore_errors: true loop: - user1 - user2 - name: 将结果写入文件 ansible.builtin.lineinfile: path: "/path/to/file/file.txt" regexp: '^{{item.item}}' # 匹配结果对应的用户名 line: '{{item.stdout}}' # 使用每个循环项的标准输出 insertafter: 'EOF' create: yes loop: "{{ getent_result.results }}" # 遍历第一个任务的所有结果项 when: not item.failed # 跳过执行失败的项(可选)
简化方案(合并任务)
如果不需要单独保存结果,也可以直接在一个任务中完成命令执行和写入:
- hosts: all remote_user: root gather_facts: no tasks: - name: 处理用户并写入文件 ansible.builtin.lineinfile: path: "/path/to/file/file.txt" regexp: '^{{item}}' line: "{{ lookup('pipe', 'getent passwd ' + item + \" |sed -E 's/:[^ ]*:(.)[^ ]* ([^:]*).*/\\\\L,\\\\1\\\\2/'\") }}" insertafter: 'EOF' create: yes loop: - user1 - user2
内容的提问来源于stack exchange,提问作者keith
相关产品推荐
相关产品推荐

