天天看点

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

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

前言

本文主要记录

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 [email protected]

    获得的 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 [email protected] 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 [email protected] script.sh

    :non-login + non-interactive
  • 远程执行脚本,同时请求控制台,如

    ssh [email protected] -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

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

继续阅读