为何Ansible脚本与SSH登录时读取的系统环境存在差异?
Ansible执行Python脚本时os.popen输出与SSH登录不一致的原因及解决方法
我的check.py脚本
import getpass import sys import os print(getpass.getuser()) print(sys.executable) print(os.popen("which python3").read()) print(os.popen("cmake --version").read())
两种执行方式的输出对比
Ansible执行输出
administrator /usr/local/bin/python3 /usr/bin/python3 /bin/sh: cmake: command not found
SSH登录手动执行输出
administrator /usr/local/bin/python3 /usr/local/bin/python3 # 这里不一样! cmake version 3.25.2 # 这里也不一样!
差异原因
核心问题是环境变量PATH不同:
- SSH登录后是交互式登录shell,会自动加载用户的shell配置文件(比如
~/.bash_profile、~/.zshrc,取决于使用的shell),这些配置通常会把/usr/local/bin加到PATH最前面,所以which python3会找到/usr/local/bin/python3,cmake也能匹配到对应的路径。 - Ansible执行脚本时用的是非交互式非登录shell,不会加载这些用户级的配置文件,默认PATH里没有
/usr/local/bin,所以只能找到系统默认的/usr/bin/python3,cmake因为不在默认PATH范围内,直接提示找不到。
解决方法
方法1:在Python脚本中手动指定PATH
在脚本开头添加环境变量设置,把/usr/local/bin加到PATH最前面:
import getpass import sys import os # 手动更新PATH,优先查找/usr/local/bin os.environ['PATH'] = '/usr/local/bin:' + os.environ.get('PATH', '') print(getpass.getuser()) print(sys.executable) print(os.popen("which python3").read()) print(os.popen("cmake --version").read())
方法2:让Ansible用登录shell执行脚本
修改Ansible的main.yml,用shell模块代替script模块,强制使用登录shell加载用户配置:
- name: Run a check script with login shell ansible.builtin.shell: bash -l -c '/usr/local/bin/python3 /path/to/check.py' register: output - debug: var=output.stdout_lines
注:如果使用zsh,把bash -l换成zsh -l
方法3:在Ansible中全局设置PATH环境变量
在group_vars/all.yml里添加ansible_env配置,直接指定包含/usr/local/bin的PATH:
ansible_user: administrator ansible_password: admin_password ansible_become: ansible_become_user: root ansible_become_pass: root_password workdir: /Users/Shared/Jenkins node_type: bare_metal env: production # 添加PATH环境变量配置 ansible_env: PATH: "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
我的Ansible配置文件
ansible.cfg
[defaults] inventory = hosts host_key_checking = False action_warnings = False deprecation_warnings = False
main.yml
- name: Run a check script ansible.builtin.script: check.py args: executable: /usr/local/bin/python3 register: output - debug: var=output.stdout_lines
group_vars/all.yml
ansible_user: administrator ansible_password: admin_password ansible_become: ansible_become_user: root ansible_become_pass: root_password workdir: /Users/Shared/Jenkins node_type: bare_metal env: production
内容的提问来源于stack exchange,提问作者jing
相关产品推荐
相关产品推荐

