在 Ansible 中,command 和 shell 模块都用于在目标主机上执行命令,但它们在实现和功能上存在显著差异。正确选择模块对于确保任务的正确执行和安全性至关重要。

command 模块

  • 功能:在目标主机上执行命令,但绕过 shell,直接调用命令。
  • 特点:
    • 不支持 shell 功能(如变量扩展、重定向、管道等)。
    • 更安全,因为不会受到 shell 环境变量的影响。
    • 始终将 changed 状态设置为 True,因为 Ansible 无法预测命令是否改变了系统状态。
  • 适用场景:当需要直接执行系统命令时,例如运行二进制文件或简单的系统工具。

shell 模块

  • 功能:在目标主机上执行 shell 命令,通过 shell 执行命令。
  • 特点:
    • 支持所有 shell 功能,如变量扩展、重定向、管道、条件语句等。
    • 更灵活,但安全性较低,因为可能会受到 shell 环境变量的影响。
    • 始终将 changed 状态设置为 True。
  • 适用场景:当需要使用 shell 功能时,例如执行复杂的命令链或使用 shell 内置功能。

最佳实践

  • 优先使用专用模块:在可能的情况下,优先使用 Ansible 提供的专用模块(如 yum、apt、file 等),而不是直接使用 command 或 shell 模块。
  • 避免使用 shell 模块:如果不需要 shell 功能,尽量避免使用 shell 模块,以减少安全风险和不可预测性。
  • 记录输出:使用 register 关键字记录命令的输出,以便后续任务可以使用这些输出。

示例

command 模块示例

---
- name: command module demo
  hosts: all
  tasks:
    - name: check uptime
      ansible.builtin.command: uptime
      register: command_output
    - name: command output
      ansible.builtin.debug:
        var: command_output.stdout_lines

执行结果:

$ ansible-playbook -i inventory command_shell/uptime.yml
PLAY [command module demo] ***********************************
TASK [Gathering Facts] **************************************
ok: [demo.example.com]
TASK [check uptime] ******************************************
changed: [demo.example.com]
TASK [command output] ****************************************
ok: [demo.example.com] => {
    "command_output.stdout_lines": [
        " 12:51:34 up 8 min,  1 user,  load average: 0.00, 0.05, 0.06"
    ]
}
PLAY RECAP ***************************************************
demo.example.com           : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

shell 模块示例

---
- name: shell module demo
  hosts: all
  tasks:
    - name: list file(s) and folder(s)
      ansible.builtin.shell: 'ls -l *'
      register: command_output
    - name: command output
      ansible.builtin.debug:
        var: command_output.stdout_lines

执行结果:

$ ansible-playbook -i inventory command_shell/list_files.yml
PLAY [shell module demo] *************************************
TASK [Gathering Facts] **************************************
ok: [demo.example.com]
TASK [list file(s) and folder(s)] ****************************
changed: [demo.example.com]
TASK [command output] ****************************************
ok: [demo.example.com] => {
    "command_output.stdout_lines": [
        "-rwxr-xr-x. 1 devops wheel 31 Mar 30 13:39 example.sh"
    ]
}
PLAY RECAP ***************************************************
demo.example.com           : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

错误使用 command 模块

如果尝试使用 command 模块执行需要 shell 功能的命令(如 ls -l *),可能会导致错误:

---
- name: shell module demo
  hosts: all
  tasks:
    - name: list file(s) and folder(s)
      ansible.builtin.command: 'ls -l *'
      register: command_output
    - name: command output
      ansible.builtin.debug:
        var: command_output.stdout_lines

执行结果:

$ ansible-playbook -i inventory command_shell/list_files_command.yaml
PLAY [shell module demo] *************************************
TASK [Gathering Facts] **************************************
ok: [demo.example.com]
TASK [list file(s) and folder(s)] ****************************
fatal: [demo.example.com]: FAILED! => {"changed": true, "cmd": ["ls", "-l", "*"], "delta": "0:00:00.003038", "end": "2022-04-06 13:01:54.498403", "msg": "non-zero return code", "rc": 2, "start": "2022-04-06 13:01:54.495365", "stderr": "ls: cannot access '*': No such file or directory", "stderr_lines": ["ls: cannot access '*': No such file or directory"], "stdout": "", "stdout_lines": []}
PLAY RECAP ***************************************************
demo.example.com           : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

总结

  • command 模块:适用于直接执行系统命令,不支持 shell 功能。
  • shell 模块:适用于需要使用 shell 功能的场景,但需注意安全性。
  • 最佳实践:优先使用专用模块,避免不必要的 shell 功能,确保任务的可预测性和安全性。