目录
背景
第一部分 shell解释器种类
第二部分 shell执行命令模式
第三部分 shell命令的类型
第四部分 shell初始化和调用
第五部分 启动文件介绍
第六部分 生产运维注意事项
参考文献及资料
背景
shell是用C语言编写的程序,用户用它来和Linux进行交互。我们通常说的 shell 都是指 shell 脚本,但 shell 和 shell script 是两个不同的概念,这是后话。
第一部分 shell解释器种类
1.1 shell常见解释器类型
Linux shell常用类型有:bash
、ksh
、csh
、zsh
、ash
等。可以通过下面的命令查看本机操作系统支持的shell类型(下面是ubuntu
系统):
1 | root@deeplearning:/# cat /etc/shells |
使用下面的命令查看当前(ubuntu
)正在使用的shell:
1 | root@deeplearning:/# echo $SHELL |
这里SHELL
是一个环境变量,用于记录当前用户使用的shell类型。在当前shell环境中,可以打开其他的shell环境(子shell):
1 | root@deeplearning:/# /bin/dash |
上面命令我们打开了dash
,如果要退出,直接使用exit
命令即可退出子shell。
1.2 常用shell详细介绍
1.2.1 ash
ash Shell是由Kenneth Almquist编写的,是Linux 中占用系统资源最少的一个小Shell。但是它只包含24个内部命令,因而使用起来很不方便。
1.2.2 bash
bash是Linux系统默认使用的Shell,它由Brian Fox 和Chet Ramey共同完成,是BourneAgain Shell的缩写,内部命令一共有40 个。Linux 使用它作为默认的Shell是因为它具有以下特色:
- 可以使用类似DOS下面的doskey的功能,用上下方向键查阅和快速输入并修改命令。
- 自动通过查找匹配的方式,给出以某字串开头的命令。
- 包含了自身的帮助功能,你只要在提示符下面键入help就可以得到相关的帮助信息。
1.2.3 ksh
ksh是Korn Shell的缩写,由Eric Gisin编写,共有42 条内部命令。该Shell最大的优点是几乎和商业发行版的ksh 完全相容,这样就可以在不用花钱购买商业版本的情况下尝试商业版本的性能了。
1.2.4 csh
csh 是Linux 比较大的内核,它由以William Joy 为代表的共计47 位作者编成,共有52个内部命令。该Shell其实是指向/bin/tcsh这样的一个Shell,也就是说,csh其实就是tcsh。
1.2.5 zch
zch是Linux 最大的Shell之一,由Paul Falstad完成,共有84 个内部命令。如果只是一般的用途,没有必要安装这样的Shell
第二部分 Shell执行命令的模式
Shell按照是否交互,分为交互式和非交互两种模式。这就像Python等解释型语言,有交互式和非交互式。
2.1 交互式(Interactive Shell)
交互式(Interactive):解释执行用户的命令,用户输入一条命令,Shell就解释执行一条。
2.2 非交互式(Non Interactive Shell)
非交互式即批处理(batch)。用户提前编写好Shell脚本,文件中存储多条命令。Shell读取并执行文件中命令,直到读到文件的结束EOF,Shell终止。另外对于使用管道连接的多个命令也算批处理。
可以使用打印
$-
来判别当前交互模式。echo $-
,输出himBH为交互式,输出hB表示非交互式。
另外按照是否需要登录(使用用户名/密钥),分为登录式和非登录式。
2.3 登录式(Login Shell)
需要用户名、密码登录后才能进入的shell(或者通过--login
选项生成的shell)。
2.4 非登录式(Non Login Shell)
不需要输入用户名和密码即可打开的Shell。例如在当前shell交互命令行中,执行bash
就会开启一个新的shell(子shell)环境,这就是一个非登录式的shell。
2.5 shell的退出
执行exit命令,退出一个shell(登录或非登录shell)。
执行logout命令,退出登录shell(不能退出非登录shell)。
第三部分 shell命令的类型
3.1 内部命令
内部命令内置于Shell源码中,即存在于内存中,一般比较简短,代码量很少,执行起来速度快,经常会使用,比如cd
、echo
。它与shell本身处在同一进程内(就写在Shell这个程序里面),当打开Shell时,操作系统会将Shell程序放入内存 。
类似Python的程序中的内置函数(Build-in Function),Python解析器初始化化就会加载这些函数。
3.2 外部命令
外部命令一般功能比较强大,包含的代码量也较大,所以在系统加载时并不随系统一起被加载到内存中,而是在需要时才调用,它们是存在于文件系统中某个目录下的单独的程序,当执行外部命令时,会到文件系统中文件的目录中寻找,例如 cp
、rm
、ifconfig
。
3.3 查看命令类型
对于一个命令是否是内部或者外部命令,可以使用type
命令来检测。
1 | root@vultr:~# type cd |
其中builtin
就是指是内部命令,类似Python中builtin
包。另外type本身也是一个内部命令:
1 | root@vultr:~# type type |
第四部 shell的初始化和调用
当shell被调用时,会读取一些初始化启动文件。主要作用是为shell本身或用户设定运行环境,包含一些函数、 变量、别名等等。
shell有两种类型的初始化文件:
- 系统级启动文件。这些包含适用于系统上所有用户的全局配置,通常位于
/etc
目录中。 包括:/etc/profiles
和/etc/bashrc
或/etc/bash.bashrc
(不同操作系统差异) 。 - 用户级启动文件 。这些存储配置适用于系统上的单个用户,通常位于用户主目录中的点文件(使用
la
命令查看)。 包括:.profiles
,.bash_profile
,.bashrc
和.bash_login
。
shell可以以三种模式被调用,分别是:交互式登录、非登录交互式、非交互式。
4.1 交互式登录shell
用户成功登录系统后,使用/bin/login
登录,随后读取/etc/passwd
文件,获取用户凭证后调用shell。/etc/passwd
文件中配置了用户默认的shell类型(每行最后)。
1 | root@VM-0-5-ubuntu:~# cat /etc/passwd |
然后这个登录shell 将查找几个不同的启动文件来处理其中的命令(它的作用是初始化linux系统相关配置)。例如 bash shell
处理文件的顺序如下:
系统登录后,shell首先执行
/etc/profile
文件中的命令(系统级)。设置这个文件后,可以为系统内所有的用户建立默认的特征(不同版本的Linux此文件放置路径有区别)。如果是超级用户则提示符用
#
,如果是普通用户则提示符用$
.当某个用户登录后,shell依次查找
~/.bash_profile
、~/.bash_login
、~/.profile
这几个文件。其中
~/.bash_profile
、~/.bash_login
和~/.profile
文件往往只存在一个,这与Linux的发行版本有关。ubuntu则为~/.profile
如果用户级有与系统级
/etc/profile
相同的环境变量,将会重新更新系统级的值。对于Centos系统,加载的是
.bash_profile
。另外还会加载~/.bashrc
,~/.bashrc
文件中还会调用文件:/etc/bashrc
。
- 当用户注销时,bash执行文件
~/.bash_logout
中的命令,这个文件包含了退出会话时执行的清理命令和退出等,如:exit
退出。
4.2 交互式非登录Shell
这种情况下调用时,它将拷贝父shell的环境,并读取相应用户级的~/.bashrc
配置文件。交互式非登录shell 就是指你在当前图形界面中打开“终端”出来的那种窗口程序,和登录shell相比,它是“非登录”的,你并不需要输入用户名和码;和非交互式shell相比,这是“交互式”的,就像你说的那它:你输入什么,它就解释出什么。
4.3 非交互式Shell
当执行脚本时,则调用非交互式shell。在这种模式下,它将处理所运行的脚本中的命令、函数等操作,不需要进行交互式输入(除非脚本需要交互式输入)。使用的环境继承自父shell。
第五部分 启动文件介绍
5.1 系统级启动文件
/etc/profile
,文件保存了登录时系统级环境配置和启动程序。如果你想配置对于所有用户的环境生效,可以加入此文件。/etc/bashrc(ubuntu为 /etc/bash.bashrc)
,包含应用于所有用户的系统级函数、变量、别名等配置信息。
5.2 用户级启动文件
在/home/用户名
该目录下面一般有下面文件(存在操作系统差异):
1 | .bash_logout # 用户登出shell是加载 |
第六部分 生产运维注意事项
6.1 su命令注意事项
在Linux系统使用中,很多用户无法区分su 用户名
和su - 用户名
两个命令的区别。甚至不懂差异,经常混用,非常危险,特别是生产运维中使用。两个命令的区别我们举个栗子说明一下。假如当前是root用户,su guest
执行后,只是切换了用户身份由root切换成guest,但是shell环境仍然继承了root用户的shell环境。但是su - guest
命令不仅切换了用户身份,而且shell环境也切换成guest登录后的shell环境。
事实上,下面三个命令形式是等价的:
1 | ubuntu用户切换到guest用户(使用登录方式) |
另外这个差异还可以通过命令执行后的用户目录更为形象的感知:su guest
执行后,工作目录并没有切换,而su - guest
执行后工作目录切换为/home/guest
。
对于生产环境,应该统一使用su - 用户名
的方式,避免用户shell的继承,造成对切换用户后环境变量的变化(通常应用用户会有特殊的环境变量)。
6.2 crontab中的shell
首先需要注意的是:crontab中的shell脚本,既不是交互式shell,也不是登录shell。所以不会加载启动配置文件。所以环境变量需要自行加载,不能想当然脚本在用户shell环境能执行,部署crontab也能执行。
另外下面是crontab的PATH值:
1 | ubuntu@VM-0-5-ubuntu:~$ grep PATH /etc/crontab |
而操作系统用户的PATH值如下,也是有差异的。
1 | root@VM-0-5-ubuntu:~# echo $PATH |
可以按照下面的方式解决:
- 可以把shebang改为
#!/bin/bash -l
让脚本用登录Shell来解释执行,这个时候,执行脚本要采用路径执行的方式 - 调用Bash解释器,加-l参数,即 /bin/bash -l shell脚本
参考文献及资料
1、GNU Bash,链接:https://www.gnu.org/software/bash/