command 适合执行简单、确定路径的命令,不支持管道、重定向、环境变量等;shell 才支持完整 shell 功能,如 |、>、$PATH、~ 展开,应优先使用 shell 而非 command。

直接说结论:command 适合执行简单、确定路径的命令,不带管道/重定向/环境变量;shell 才是真正能干活的,支持 |、>、$PATH、~ 展开等所有 shell 行为。别图省事默认用 command,一碰到重定向就报错。
command 模块:看似简单,但限制极多
command 不经过 shell 解析,而是直接调用 execve() 执行二进制或脚本,所以它“干净但脆弱”。
- 不支持
|、&&、;、>、>>、、*(通配符)、$HOME等任何 shell 特性 - 不能用
cd /tmp && ls,但可以用chdir=/tmp ls参数切换工作目录 - 不能靠
export VAR=1; echo $VAR,因为根本没启动 shell 进程 - 如果命令里含空格,必须加引号:
ansible web -m command -a "ls -l /var/log",否则会被截断 - 常见误用:
ansible web -m command -a "echo hello > /tmp/test"→ 报错Failed to find required executable echo,因为重定向由 shell 处理,command不认
shell 模块:默认用 /bin/sh,但行为更接近你本地终端
shell 会通过远程主机的 /bin/sh 启动子 shell,所以几乎所有你在终端敲的命令都能跑通,但也因此引入了额外风险和兼容性问题。
- 支持完整 shell 语法:
ansible web -m shell -a "ps aux | grep nginx | wc -l" - 支持重定向:
ansible db -m shell -a "mysql -u root -e 'show databases;' > /tmp/dbs.txt" - 注意环境变量继承有限:远程用户登录时的
.bashrc或.profile默认不加载,如需自定义 PATH 或变量,得显式写出来:ansible app -m shell -a "source ~/.bashrc && mytool --version" - 通配符生效:
ansible web -m shell -a "rm -f /tmp/*.log",但command下这句会原样传给rm,导致找不到文件 - 可指定解释器:
ansible web -m shell -a "echo $0" -a "executable=/bin/bash",避免某些老系统/bin/sh不支持[[ ]]或数组
什么时候该换 raw 或 script 模块?
不是所有场景都适合 command 或 shell —— 它们依赖远程主机装了 Python,且能调用 sh。遇到极端情况就得绕开。
- 目标机没装 Python(比如最小化 Alpine 或嵌入式设备)→ 用
raw:ansible edge -m raw -a "apk add curl",纯 SSH 执行,不走 Ansible 框架 - 要执行本地写的复杂脚本(含多行、条件、函数)→ 用
script:ansible web -m script -a "/local/deploy.sh --env=prod",Ansible 自动上传 + 赋权 + 执行 - 想确保命令在特定 shell 下运行(比如必须用
zsh或fish)→shell的executable参数比硬写zsh -c '...'更可靠 - 批量操作中混用模块很常见:先
copy配置,再shell重启服务,最后command校验状态码 —— 别强求一个模块打天下
最常被忽略的一点:shell 模块里写的路径,比如 ~/app/start.sh,展开的是远程用户的 home 目录,不是控制机的;而 copy 模块的 src= 是控制机路径,dest= 是远程路径 —— 这个主客体混淆,90% 的路径错误都出在这儿。











