Fork me on GitHub

Linux系统中的shell解释器介绍

目录

  • 背景

  • 第一部分 shell解释器种类

  • 第二部分 shell执行命令模式

  • 第三部分 shell命令的类型

  • 第四部分 shell初始化和调用

  • 第五部分 启动文件介绍

  • 第六部分 生产运维注意事项

  • 参考文献及资料

背景

shell是用C语言编写的程序,用户用它来和Linux进行交互。我们通常说的 shell 都是指 shell 脚本,但 shell 和 shell script 是两个不同的概念,这是后话。

第一部分 shell解释器种类

1.1 shell常见解释器类型

Linux shell常用类型有:bashkshcshzshash等。可以通过下面的命令查看本机操作系统支持的shell类型(下面是ubuntu系统):

1
2
3
4
5
6
root@deeplearning:/# cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash

使用下面的命令查看当前(ubuntu)正在使用的shell:

1
2
root@deeplearning:/# echo $SHELL
/bin/bash

这里SHELL是一个环境变量,用于记录当前用户使用的shell类型。在当前shell环境中,可以打开其他的shell环境(子shell):

1
2
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源码中,即存在于内存中,一般比较简短,代码量很少,执行起来速度快,经常会使用,比如cdecho。它与shell本身处在同一进程内(就写在Shell这个程序里面),当打开Shell时,操作系统会将Shell程序放入内存 。

类似Python的程序中的内置函数(Build-in Function),Python解析器初始化化就会加载这些函数。

3.2 外部命令

外部命令一般功能比较强大,包含的代码量也较大,所以在系统加载时并不随系统一起被加载到内存中,而是在需要时才调用,它们是存在于文件系统中某个目录下的单独的程序,当执行外部命令时,会到文件系统中文件的目录中寻找,例如 cprmifconfig

3.3 查看命令类型

对于一个命令是否是内部或者外部命令,可以使用type命令来检测。

1
2
3
4
root@vultr:~# type cd 
cd is a shell builtin
root@vultr:~# type cp
cp is /bin/cp

其中builtin就是指是内部命令,类似Python中builtin包。另外type本身也是一个内部命令:

1
2
root@vultr:~# type type
type is a shell builtin

第四部 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
2
3
root@VM-0-5-ubuntu:~# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
......

然后这个登录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
2
3
4
5
6
7
.bash_logout # 用户登出shell是加载
# 下面三个是用户级启动文件
.profile
.bash_profile
.bash_login

.bashrc # Centos操作系统用户级启动文件

第六部分 生产运维注意事项

6.1 su命令注意事项

在Linux系统使用中,很多用户无法区分su 用户名su - 用户名两个命令的区别。甚至不懂差异,经常混用,非常危险,特别是生产运维中使用。两个命令的区别我们举个栗子说明一下。假如当前是root用户,su guest执行后,只是切换了用户身份由root切换成guest,但是shell环境仍然继承了root用户的shell环境。但是su - guest命令不仅切换了用户身份,而且shell环境也切换成guest登录后的shell环境。

事实上,下面三个命令形式是等价的:

1
2
3
4
# ubuntu用户切换到guest用户(使用登录方式)
ubuntu@VM-0-5-ubuntu:~$ su - guest
ubuntu@VM-0-5-ubuntu:~$ su -l guest
ubuntu@VM-0-5-ubuntu:~$ su --login guest

另外这个差异还可以通过命令执行后的用户目录更为形象的感知:su guest执行后,工作目录并没有切换,而su - guest执行后工作目录切换为/home/guest

对于生产环境,应该统一使用su - 用户名的方式,避免用户shell的继承,造成对切换用户后环境变量的变化(通常应用用户会有特殊的环境变量)。

6.2 crontab中的shell

首先需要注意的是:crontab中的shell脚本,既不是交互式shell,也不是登录shell。所以不会加载启动配置文件。所以环境变量需要自行加载,不能想当然脚本在用户shell环境能执行,部署crontab也能执行。

另外下面是crontab的PATH值:

1
2
ubuntu@VM-0-5-ubuntu:~$ grep PATH /etc/crontab
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

而操作系统用户的PATH值如下,也是有差异的。

1
2
root@VM-0-5-ubuntu:~# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

可以按照下面的方式解决:

  • 可以把shebang改为#!/bin/bash -l让脚本用登录Shell来解释执行,这个时候,执行脚本要采用路径执行的方式
  • 调用Bash解释器,加-l参数,即 /bin/bash -l shell脚本

参考文献及资料

1、GNU Bash,链接:https://www.gnu.org/software/bash/

本文标题:Linux系统中的shell解释器介绍

文章作者:rong xiang

发布时间:2020年04月21日 - 12:04

最后更新:2022年10月25日 - 23:10

原始链接:https://zjrongxiang.github.io/posts/e6431388/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%