一、 shell简介
1. shell的类型
系统启用什么样的shell取决于用户配置,在/etc/passwd的最后一个字段列出了用户的默认shell,一般为/bin/bash,其他还有zsh,tcsh,dash,csh等。
bash shell程序位于/bin目录下,使用长列表可以看出它是一个可执行程序:
另外一个默认的shell是/bin/sh,作为默认的系统shell,用于需要在启动时使用的系统shell脚本。centos中使用软链接将默认的系统shell设置成bash shell。这样说来,sh其实就是bash。
2. shell的父子关系
bash shell及之前提到的shell 都是父shell,要创建一个子shell程序很简单,输入bash就会创建一个新的shell程序,被称为子shell。要区分是父shell还是子shell,需要使用ps 查看进程。
在root用户下,先看没有子shell的父shell进程,如下图所示:
接下来输入bash,创建一个子shell,该子shell的PPID父进程是1748,对应-bash进程的(PID) 1748
当然子shell还可以继续创建子shell,但注意生成子shell的成本不低,速度还慢,创建嵌套的子shell去处理性能更为糟糕。
可以用ps -forest命令查看树状关系图
3. 命令列表与进程列表
通过;可以在一行中执行一系列命令,称为命令列表,例如
pwd ; ls ; cd /etc ; pwd ; ls
上面的命令可以依次执行,但这并不是进程列表。命令列表要想变为进程列表,将这些命令包含在()中即可。
(pwd ; ls ; cd /etc ; pwd ; ls)
对于进程列表,Linux会生成一个子shell执行对应命令。Linux有一个环境变量 $BASH_SUBSHELL,可以查看该值判断是否有子shell(为0则没有)。
pwd ; ls ; cd /etc ; pwd ; ls ; echo $BASH_SUBSHELL # echo $BASH_SUBSHELL返回0
(pwd ; ls ; cd /etc ; pwd ; ls ; echo $BASH_SUBSHELL) # echo $BASH_SUBSHELL返回1
4. 外部命令与内建命令
外部命令即存在于bash shell之外的程序,通常位于各类bin目录下,例如ps、sqlplus等。执行外部命令会自动forking出一个子进程,由子进程实际执行该外部命令。前面说过forking子进程需要额外代价,因此外部命令效率相对较低。
内建命令则是存在于bash shell中的程序,它们和shell编译成了一体,例如cd、exit等。
可以使用type查看命令是外部命令还是内建命令
type ps
type cd
二、 shell变量
编程语言总绕不过变量,shell也是一样。bash中每个变量都是字符串,都以字符串形式存储。
1. 变量分类及使用
- 根据是系统自带还是用户自定义,分为:环境变量、用户定义变量
- 根据作用范围又分为:全局变量、局部变量
① 环境变量
用于存储shell会话和工作环境信息,通常是事先定义好的。
- 全局环境变量:对所有shell都可见
#查看所有与此终端相关的环境变量
env
#或
printenv
#查看进程环境变量
cat /proc/$PID/env
#查看单个环境变量值
printenv $ORACLE_HOME
echo $ORACLE_HOME
- 局部环境变量:只对创建它们的shell可见。Linux中没有只显示局部环境变量的命令
#set 命令可显示为特定进程设置的所有变量,包括局部、全局环境变量、用户定义变量,还会按字母排序,输出通常很长
set
② 用户定义变量
- 局部用户定义变量:在当前shell进程中创建的仅当前进程可见的变量(子进程不可见)
使用等号=定义
my_var=123 #等号两边不要有空格
echo $my_var
my_var2="Hello World"
echo $my_var2
#删除变量
unset my_var2
- 全局用户定义变量:在当前shell进程创建的当前进程及其子进程可见的变量(注意对别的进程和其父进程还是不可见的)
使用export可将变量导出至全局环境(对其子进程可见)
2. 默认的shell环境变量
以下仅为部分,完整版非常长,大部分编程时也用不上,不列出了
variable_name | variable_value | instructions |
BASH | /bin/bash | bash二进制执行文件 |
BASH_VERSINFO[0] | 4 | 主版本号 |
BASH_VERSINFO[1] | 2 | 次版本号 |
BASH_VERSINFO[2] | 46 | 更新次数 |
BASH_VERSINFO[3] | 2 | 构建次数 |
BASH_VERSINFO[4] | release | 分发状态 |
BASH_VERSINFO[5] | x86_64-redhat-linux-gnu | 架构 |
BASH_VERSION | 4.2.46(2)-release | bash版本号 |
DIRSTACK | $PWD | 当前目录 |
FUNCNAME | 正在执行的函数的名字 | 可以放在函数里定位函数执行过程 |
GROUPS | 0 | 当前登录的用户所属组的组id号,root默认为0 |
HOME | /root | 当前登录的用户家目录 |
HOMENAME | 主机名 | 主机名 |
HOSTTYPE | x86_64 | 设备硬件类型 |
IFS | 内部域分隔,默认为空白,可以自定义设置 | Bash 在解释字符串时如何识别域,或者单词边界 |
LINENO | 记录其所在的行号 | 记录其所在shell脚本中的行号 |
MACHTYPE | 系统硬件架构 | 系统设备 |
3. 定位系统环境变量
当登入Linux系统启动bash shell时,bash默认会在几个文件中查找可执行命令,这些文件称为启动文件或环境文件。
根据不同的bash启动方式,检查的启动文件有所不同:
- 登录时作为默认登录shell
- 非登录时的交互式shell
- 非交互式shell
① 登录时作为默认登录shell
当登入Linux系统时,bash shell会作为登录shell启动,此时会从5个启动文件中查找可执行命令。
- /etc/profile
- $HOME/.bash_profile
- $HOME/.bashrc
- $HOME/.bash_login
- $HOME/.profile
其中/etc/profile是共用的,是默认的主启动文件,每个用户登录时都会执行。
其余4个不一定存在,一般建在各用户家目录下,针对每个用户进行定制。shell会按照以下顺序运行第一个被找到的文件,其他则被忽略。
- $HOME/.bash_profile
- $HOME/.bash_login
- $HOME/.profile
可以看到顺序中并没有$HOME/.bashrc文件,它的检查和调用包含在$HOME/.bash_profile中
可以看到$HOME/.bashrc做了两件事:
- 定制命令别名
- 查找/etc/bashrc文件(不建议修改),若存在则调用
② 非登录时的交互式shell
如果bash shell不是在登录时启动也不是在脚本中启动,而是类似在命令行中输入bash启动的,它就不会访问/etc/profile文件,只检查$HOME/.bashrc文件
③ 非交互式shell
运行脚本时的情况,bash会检查BASH_ENV环境变量,若设置则使用。若未设置,对于会启动子shell的脚本,会继承父shell的全局变量;对于不启动子shell的脚本,则执行进程为当前进程,可使用当前进程所有变量。
4. 数组变量
要给某个变量设置多个值,可以将值放在括号中并用空格分隔,不太常用但可以了解。
mydb=(oracle mysql mssql pg redis)
#仅显示第一个值
echo $mydb
#显示指定下标值(从0开始)
echo ${mydb[3]}
#显示所有值
echo ${mydb[*]}
参考:《Linux命令行与shell脚本编程大全》