Linux系统bash的四种模式与jenkins执行shel
2021-05-29 17:37:42 0 举报
AI智能生成
linux
作者其他创作
大纲/内容
前言
配置jenkins的执行节点,但是执行节点shell的PATH变量始终不对,无法找到git命令。我先前已经在/etc/profile中配置了git的PATH,通过putty连接的shell中也检查PATH变量是正确的,且git命令也能正常执行。后来查阅资料才知道这个问题是由于我没有很好的理解bash的四种模式而造成的。
Linux的bash的其实分为四种模式
bash会依据这四种模式而选择加载不同的配置文件,而且加载的顺序也有所不同.
四种bash模式分别是:
1、interactive + login
2、non-interactive + login
3、interactive + non-login
4、non-interactive + non-login
例子
执行 ssh wangxin@192.168.80.129 ~/hello.sh
得不到想要的结果
执行
[wangxin@localhost ~]$ ssh wangxin@192.168.80.129
wangxin@192.168.80.129's password:
Last login: Sat May 29 14:23:57 2021 from 192.168.80.1
[wangxin@localhost ~]$ ~/hello.sh
abc_test
[wangxin@localhost ~]$ ssh wangxin@192.168.80.129
wangxin@192.168.80.129's password:
Last login: Sat May 29 14:23:57 2021 from 192.168.80.1
[wangxin@localhost ~]$ ~/hello.sh
abc_test
能够获得想要的结果
查询
man ssh
If command is specified, it is executed on the remote host instead of a login shell.
这说明在指定命令的情况下,命令会在远程主机上执行,返回结果后退出。而未指定时,ssh会直接返回一个登陆的shell。
直接在远程主机上执行和在返回的登陆shell中执行有什么区别?
man bash
bash四种模式的shell
bash会依据这四种模式而选择加载不同的配置文件,而且加载的顺序也有所不同。
(一)、interactive + login模式的shell
交互式的登陆shell
交互式的登陆shell
概述
login故名思义,即登陆,login shell是指用户以非图形化界面或者以ssh登陆到机器上时获得的第一个shell,简单些说就是需要输入用户名和密码的shell。因此通常不管以何种方式登陆机器后用户获得的第一个shell就是login shell。
interactive意为交互式,这也很好理解,interactive shell会有一个输入提示符,并且它的标准输入、输出和错误输出都会显示在控制台上。
一般来说只要是需要用户交互的,即一个命令一个命令的输入的shell都是interactive shell。
如果无需用户交互,它便是non-interactive shell。
通常来说如bash script.sh此类执行脚本的命令就会启动一个non-interactive shell,它不需要与用户进行交互,执行完后它便会退出创建的shell。
例子
用户直接登陆到机器获得的第一个shell
用户使用ssh user@remote获得的shell
加载配置文件
这种模式下,shell首先加载/etc/profile,然后再尝试依次去加载下列三个配置文件之一,一旦找到其中一个便不再接着寻找:
/etc/profile
~/.bash_profile
~/.bash_login
~/.profile
代码演示
echo "echo @ /etc/profile" >> /etc/profile && \
echo "echo @ ~/.bash_profile" >> ~/.bash_profile && \
echo "echo @ ~/.bash_login" >> ~/.bash_login && \
echo "echo @ ~/.profile" >> ~/.profile
echo "echo @ ~/.bash_profile" >> ~/.bash_profile && \
echo "echo @ ~/.bash_login" >> ~/.bash_login && \
echo "echo @ ~/.profile" >> ~/.profile
使用bash -l命令,它会打开一个login shell
man bash : -l Make bash act as if it had been invoked as a login shell
得到以下输出:
@ /etc/profile
@ /home/user/.bash_profile
@ /home/user/.bash_profile
bash首先会加载全局的配置文件/etc/profile,然后去查找~/.bash_profile,因为其已经存在,所以剩下的两个文件不再会被查找。
接下来移除~/.bash_profile
启动login shell得到结果如下:
@ /etc/profile
@ /home/user/.bash_login
因为没有了~/.bash_profile的屏蔽,所以~/.bash_login被加载,但最后一个~/.profile仍被忽略。
再次移除~/.bash_login
启动login shell的输出结果为:
@ /etc/profile
@ /home/user/.profile
@ /home/user/.profile
确认加载顺序
/etc/profile > ~/.bash_profile> ~/.bash_login > ~/.profile
使用ssh也会得到一个login shell,所以如果在另外一台机器上运行ssh user@remote时,也会得到上面一样的结论。
配置文件的意义
为什么bash要弄得这么复杂?每个配置文件存在的意义是什么?
/etc/profile很好理解,它是一个全局的配置文件。后面三个位于用户主目录中的配置文件都针对用户个人,也许你会问为什么要有这么多,只用一个~/.profile不好么?究竟每个文件有什么意义呢?这是个好问题。
原因
原来一切都是为了兼容,这么设计是为了更好的应付在不同shell之间切换的场景。
因为bash完全兼容Bourne shell,所以.bash_profile和.profile可以很好的处理bash和Bourne shell之间的切换。
但是由于C shell和bash之间的基本语法存在着差异,作者认为引入.bash_login并不是个好主意。所以由此我们可以得出这样的最佳实践:
应该尽量杜绝使用.bash_login,如果已经创建,那么需要创建.bash_profile来屏蔽它被调用
.bash_profile适合放置bash的专属命令,可以在其最后读取.profile,如此一来,便可以很好的在Bourne shell和bash之间切换了
(二)、non-interactive + login模式的shell
第二种模式的shell为non-interactive login shell,即非交互式的登陆shell,这种是不太常见的情况。一种创建此shell的方法为:bash -l script.sh,前面提到过-l参数是将shell作为一个login shell启动,而执行脚本又使它为non-interactive shell。
对于这种类型的shell,配置文件的加载与第一种完全一样,在此不再赘述。
(三)、interactive + non-login模式的shell
第三种模式为交互式的非登陆shell,这种模式最常见的情况为在一个已有shell中运行bash,此时会打开一个交互式的shell,而因为不再需要登陆,因此不是login shell。
加载配置文件
对于此种情况,启动shell时会去查找并加载/etc/bash.bashrc和~/.bashrc文件。
代码演示
配置文件
echo "echo @ /etc/bash.bashrc" >> /etc/bash.bashrc && \
echo "echo @ ~/.bashrc" >> ~/.bashrc
echo "echo @ ~/.bashrc" >> ~/.bashrc
启动一个交互式的非登陆shell,直接运行bash即可
[root@wangxin-test ~]# bash
得到以下结果
@ /etc/bash.bashrc
@ /home/user/.bashrc
@ /home/user/.bashrc
非常容易的验证了结论。
bashrc VS profile
第一个文件是全局性的,第二个文件属于当前用户。
在前面的模式当中,已经出现了几种配置文件,多数是以profile命名的,那么为什么这里又增加两个文件呢?这样不会增加复杂度么?我们来看看此处的文件和前面模式中的文件的区别。
首先看第一种模式中的profile类型文件,它是某个用户唯一的用来设置全局环境变量的地方, 因为用户可以有多个shell比如bash, sh, zsh等, 但像环境变量这种其实只需要在统一的一个地方初始化就可以, 而这个地方就是profile,所以启动一个login shell会加载此文件,后面由此shell中启动的新shell进程如bash,sh,zsh等都可以由login shell中继承环境变量等配置。
接下来看bashrc,其后缀rc的意思为Run Commands,由名字可以推断出,此处存放bash需要运行的命令,但注意,这些命令一般只用于交互式的shell,通常在这里会设置交互所需要的所有信息,比如bash的补全、alias、颜色、提示符等等。
所以可以看出,引入多种配置文件完全是为了更好的管理配置,每个文件各司其职,只做好自己的事情。
(四)、non-interactive + non-login模式的shell
最后一种模式为非交互非登陆的shell,创建这种shell典型有两种方式:
bash script.sh
ssh user@remote command
这两种都是创建一个shell,执行完脚本之后便退出,不再需要与用户交互。
加载配置文件
对于这种模式而言,它会去寻找环境变量BASH_ENV,将变量的值作为文件名进行查找,如果找到便加载它。
代码演示
user@remote > cat ~/script.sh
echo Hello World
再次执行bash script.sh,结果为:
Hello World
从输出结果可以得知,这个新启动的bash进程并没有加载前面提到的任何配置文件。
接下来设置环境变量BASH_ENV
user@remote > export BASH_ENV=~/.bashrc
再次执行bash script.sh
@ /home/user/.bashrc
Hello World
Hello World
~/.bashrc被加载,而它是由环境变量BASH_ENV设定的。
关于bash四种模式的直观示图
至此,四种模式下配置文件如何加载已经讲完,因为涉及的配置文件有些多,我们再以两个图来更为直观的进行描述:
bash的每种模式会读取其所在列的内容,首先执行A,然后是B,C。而B1,B2和B3表示只会执行第一个存在的文件:
上图只给出了三种模式,原因是第一种login实际上已经包含了两种,因为这两种模式下对配置文件的加载是一致的。
更直观的图:
上图的情况稍稍复杂一些,因为它使用了几个关于配置文件的参数:--login,--rcfile,--noprofile,--norc,这些参数的引入会使配置文件的加载稍稍发生改变,不过总体来说,不影响我们前面的讨论
bash与sh陷阱
设置成sh和bash有什么区别?带着这些疑问,再来查看man bash:
If the program is a file beginning with #!, the remainder of the first line specifies an interpreter for the program.
它表示这个文件的解释器,即用什么程序来打开此文件,就好比Windows上双击一个文件时会以什么程序打开一样。因为这里不是bash,而是sh,那么我们前面讨论的都不复有效了
看看这个sh的路径:
user@remote > ll `which sh`
lrwxrwxrwx 1 root root 9 Apr 25 2014 /usr/bin/sh -> /bin/bash
lrwxrwxrwx 1 root root 9 Apr 25 2014 /usr/bin/sh -> /bin/bash
原来sh只是bash的一个软链接,既然如此,BASH_ENV应该是有效的啊,为何此处无效?还是回到man bash,同样在INVOCATION一节的下部看到了这样的说明:
简而言之,当bash以是sh命启动时,即我们此处的情况,bash会尽可能的模仿sh,所以配置文件的加载变成了下面这样:
interactive + login: 读取/etc/profile和~/.profile
non-interactive + login: 同上
interactive + non-login: 读取ENV环境变量对应的文件
non-interactive + non-login: 不读取任何文件
所以为了解决问题,只需要把sh换成bash,再设置环境变量BASH_ENV即可。
另外,其实我们还可以设置参数到第一行的解释器中,如#!/bin/bash --login,如此一来,bash便会强制为login shell,所以/etc/profile也会被加载。相比上面那种方法,这种更为简单。
总结与建议
典型模式总结
为了更好的理清这几种模式,下面我们对一些典型的启动方式各属于什么模式进行一个总结:
登陆机器后的第一个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
建议
提到的所有配置文件
/etc/profile
~/.bash_profile
~/.bash_login
~/.profile
/etc/bash.bashrc
~/.bashrc
$BASH_ENV
$ENV
最佳实践供各位参考
~/.bash_profile:应该尽可能的简单,通常会在最后加载.profile和.bashrc(注意顺序)
~/.bash_login:在前面讨论过,别用它
~/.profile:此文件用于login shell,所有你想在整个用户会话期间都有效的内容都应该放置于此,比如启动进程,环境变量等
~/.bashrc:只放置与bash有关的命令,所有与交互有关的命令都应该出现在此,比如bash的补全、alias、颜色、提示符等等。
0 条评论
下一页