gentos 执行sh文件_远程执行命令的填坑记录

0e5e8c35dc6422028624234d9fffd3b1.png

前言

本文主要记录 bash 四种模式的细节,以便于遇到问题时查阅。

远程执行出错了

最近使用 ansible 比较多,在某次使用 shell 模块远程执行命令的时候老是报 ‘command not found’

但是手动登录到远端机器执行命令是成功的,于是开始思考里面的细节。

特别感谢这篇博文


bash 的四种模式

遇到问题的时候就觉得应该是环境变量的关系。

因为使用的是 bash,那下面就来记录一下 bash 的细节。

1. interactive + login shell

第一种是交互式的登录 shell。

这里需要理解两个概念:interactive , login

login shell 指用户以非图形界面或者以 ssh 登录到机器上时获得的第一个 shell 。

  • 简单些说就是需要输入用户名和密码的 shell
  • 通常不管以何种方式登录机器后用户获得的第一个 shell 就是 login shell。

interactive shell 会有一个输入提示符,并且它的标准输入、输出和错误输出都会显示在控制台上。

  • 一般来说只要是需要用户交互的,即一个命令一个命令输入的 shell 都是 interactive shell。
  • 如果无需用户交互,它便是 non-interactive shell。
  • 通常来说如bash script.sh此类执行脚本的命令就会启动一个 non-interactive shell,它不需要与用户进行交互,执行完后它便会退出创建的 shell。

那么这个模式下最简单的两个例子就是:

  • 用户直接登录到机器获得的第一个 shell
  • 用户使用ssh user@remote获得的 shell

这种模式下,shell 首先加载/etc/profile,然后再尝试依次去加载下列三个配置文件之一,一旦找到其中一个便不再接着寻找:

  • ~/.bash_profile
  • ~/.bash_login
  • ~/.profile

2. non-interactive + login shell

第二种模式的 shell 为 non-interactive login shell,即非交互式的登录 shell,这种是不太常见的情况。

一种创建此 shell 的方法为:bash -l script.sh,-l 参数是将 shell 作为一个 login shell 启动,而执行脚本又使它为 non-interactive shell。

对于这种类型的 shell,配置文件的加载与第一种完全一样。

3. interactive + non-login shell

第三种模式为交互式的非登录 shell。

这种模式最常见的情况是在一个已有 shell 中运行 bash,此时会打开一个交互式的 shell,而因为不再需要登录,因此不是 login shell。

对于此种情况,启动 shell 时会去查找并加载 /etc/bash.bashrc~/.bashrc 文件。

bashrc vs profile

  • profile 类型文件,它是某个用户唯一的用来设置全局环境变量的地方 。
    因为用户可以有多个 shell 比如 bash, sh, zsh 等, 但像环境变量这种其实只需要在统一的一个地方初始化就可以了, 而这个地方就是 profile。
    所以启动一个 login shell 会加载此文件,后面由此 shell 中启动的新 shell 进程如 bash,sh,zsh 等都可以由 login shell 中 继承环境变量等配置
  • bashrc,其后缀 rc 的意思为 Run Commands,由名字可以推断出,此处存放 bash 需要运行的命令 。
    但注意,这些命令一般只用于交互式的 shell,通常在这里会设置交互所需要的所有信息,比如 bash 的补全、alias、颜色、提示符等等。

所以引入多种配置文件完全是为了更好的管理配置,每个文件各司其职,只做好自己的事情。

4. non-interactive + non-login shell

最后一种模式为非交互非登录的 shell,创建这种 shell 典型有两种方式:

  • bash script.sh
  • ssh user@remote command

这两种都是创建一个 shell,执行完脚本之后便退出,不再需要与用户交互。

对于这种模式而言,它会去寻找环境变量BASH_ENV,将变量的值作为文件名进行查找,如果找到便加载它。

典型模式总结

下面举一些例子:

  • 登录机器后的第一个 shell:login + interactive
  • 新启动一个 shell 进程,如运行 bash:non-login + interactive
  • 执行脚本,如bash script.sh:non-login + non-interactive
  • 运行头部有如#!/usr/bin/env bash的可执行文件,如./executable:non-login + non-interactive
  • 通过 ssh 登录到远程主机:login + interactive
  • 远程执行脚本,如ssh user@remote script.sh:non-login + non-interactive
  • 远程执行脚本,同时请求控制台,如ssh user@remote -t 'echo $PWD':non-login + interactive
  • 在图形化界面中打开 terminal:Linux 上 : non-login + interactive;Mac OS X 上 : login + interactive

有出路了

通过上面的总结,ansible 的 shell 模块远程执行命令应该就是属于 non-login + non-interactive。

对于这种模式,bash 会选择加载$BASH_ENV的值对应的文件。

但是,注意到命令里面的那个脚本的第一行#!/usr/bin/env sh,并不是 bash,而是 sh。

那么 bash 和 sh 有啥区别呢?

通过执行whereis命令查看发现,sh 只是 bash 的一个软链接。

再通过查看文档知道,当 bash 以 sh 命令启动时,bash 会尽可能的模仿 sh。

所以配置文件的加载变成了下面这样:

  • interactive + login : 读取 /etc/profile 和 ~/.profile
  • non-interactive + login : 同上
  • interactive + non-l gin : 读取 ENV 环境变量对应的文件
  • non-interactive + non-login : 不读取任何文件

所以如果是 sh 的话,不会加载任何环境变量,结果还是 command not found

最后的解决办法就是:

  • 第一步 设置$BASH_ENV/etc/profile
  • 第二步 将#!/usr/bin/env sh改成#!/usr/bin/env bash

参考

ssh连接远程主机执行脚本的环境变量问题​feihu.me
bcc22ad16476811d926347847e1974b1.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值