在 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 功能,确保任务的可预测性和安全性。