shell脚本基础
9. Shell脚本基础
9.1 概述
Shell是用户与Linux操作系统沟通的桥梁,用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作。在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用。
Linux的Shell种类众多,常见的有:Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shell(/usr/bin/ksh)、Shell for Root(/sbin/sh),等等。不同的Shell语言的语法有所不同,虽然大同小异,但每种Shell都有其特色之处,所以不能保证交换使用。在本文中,我们关注的重点是Bash,也就是Bourne Again Shell,由于易用和免费,Bash在日常工作中被广泛使用;同时,Bash也是大多数Linux系统默认的Shell。前面介绍过的一些Shell使用技巧就是针对Bash的。在一般情况下,并不需要区分 Bourne Shell和Bourne Again Shell,大多数Linux发行版中/bin/sh经常是链接到/bin/bash。
Shell的作用有两方面:
- 一是为用户提供交互式命令行操作界面,
- 二是作为一个脚本语言解释器去执行脚本。用户最常见使用Shell的形式是交互式Shell,Shell进程逐行读取用户输入的命令,执行命令产生结果,然后再等待用户输入下一条命令。当作为脚本语言解释器时,会读取特定的文本文件(即脚本)内容,逐条执行其中的命令。
Shell脚本不需要特别的开发工具,使用任何一款文本编辑器即可编写,执行时由Shell解释执行。Shell脚本并不是简单的多条命令批量执行,像其他编程语言一样,Shell脚本中可以使用变量,可以使用循环、分支、子程序等控制结构,借助于管道等机制,可以组合实现各种功能。
9.2 理解Shell执行命令的方式
相对于前面使用交互式Shell的简单方式,这里首先介绍一些在Shell中执行命令的不同方法,以便对Shell执行命令的方式有更深入的了解。
9.2.1 命令列表和子Shell中执行命令
-
命令列表:Shell允许在一个命令行中可以输入多条命令,方法就是
使用“;”进行分隔,Shell会依次执行这些命令,这种机制被称为命令列表。例如:在一行中输入如下命令:
- 最终的结果与依次输入cd /etc/X11和ls是一样的。
- 所以简单的命令列表可以实现多个命令的批量执行。

-
子shell:
用户输入的所有命令都是由Shell进程解释运行的,默认就是由提供命令行输入的Shell解释,但是应该了解,也可以由一个新的Shell进程(被称为子Shell)解释执行命令,理解这一点对于理解Shell脚本的运行有很大帮助。
在命令行中,Shell也提供了方法让用户指定在子Shell中执行命令,就是
使用圆括号把命令括起来例如:
粗看起来,这个命令的执行和前面命令列表的执行结果是一样的,但是如果仔细观察的话,会发现前一种情况执行后,当前目录改变了,而后一种情况下当前目录并未改变。
这种区别的原因就是:
- 后一种情况下,为了执行括号中的命令新创建了一个Shell进程,在该进程依次执行了两条命令,执行前面cd命令时,确实改变了当前目录,
- 但是改变的是新建Shell进程的当前目录(注意:当前目录的概念对于每一个进程是独立的),
- 执行后面ls命令时也列出的是当前目录的内容,但是ls命令执行完后,新建Shell进程完成了自己的使命以后退出,这时又回到原来的Shell进程提供的交互界面,此Shell进程的当前目录并未改变。
Shell脚本执行时,默认都是由新的子Shell执行的,脚本执行完子Shell进程就会退出,理解了这一点就会很容易理解后面要介绍的export和source命令。
9.2.2 命令的返回值
无论在Shell脚本中还是交互式Shell中,任何一个命令的执行都要由Shell进程启动,Shell会为该命令的执行创建新的进程,进程结束后都会有一个返回值,这个返回值会被Shell捕获,以了解该命令是否正确完成。
- 在交互式Shell中,如果逐条输入命令,返回值一般都会被忽略,
- 但是如果在Shell脚本中要执行多条相互关联的命令,就很有可能要根据前一个命令的返回值决定下一个命令执行与否。
要获知一个命令执行的返回值,可以使用一个特殊的Shell变量$?进行访问。关于Shell变量后面还要进一步介绍,此处只要简单知道可以通过命令echo $?显示前一个命令的返回值,一般约定返回0代表成功,非零值代表失败。
看一个简单的例子:

ls命令成功列出了指定文件信息,下一条的echo $?命令输出的是ls命令执行的结果,为0表示成功。再看一个例子:

当指定一个不存在的文件名,ls命令执行失败,接下来的echo $?命令输出1表示ls失败;注意,当再一次执行echo $?命令时,输出为0表示的是前一条echo命令执行成功。
9.2.3 根据返回值执行后续命令
前面介绍的命令列表中,用“;”分隔的两条命令会无条件地依次执行。Shell提供了另外的机制,允许后面命令根据前面命令的执行结果有条件的运行。与C语言中逻辑操作符类似,引入&&和||两个符号:
- 如果是&&连接两条命令,则前一条命令执行成功后才执行后一条命令,否则不执行;
- 如果是||连接两条命令,则前一条命令执行失败才执行后一条命令,否则不执行。
使用&&的例子如下:


使用||的例子如下:

9.3 基本的Shell脚本
9.3.1 第一个Shell脚本
-
使用任何文本编辑器编辑一个文本文件hello.sh
后缀名用.sh以表示这是一个Shell脚本,其实这种命名风格并没有实际作用(不影响脚本的编写和执行),只是便于我们从命名知道文件用途。
#!/bin/bash # first shell script HELLO="Hello World !" echo $HELLO- 第一行中以“#!”开头是一个约定标记,指定此脚本文件需要用哪个解释器运行,本例中要使用bash;
- 第二行以“#”开头是注释,会被解释器忽略,脚本中只能使用单行注释;
- 第三句是对一个Shell变量赋值,
- 第四句将此Shell变量的值显示输出
-
执行脚本文件有两种方法:
-
作为可执行程序执行
让一个脚本文件可执行,需要设置x权限。通过cd命令进入到文件所在目录,依次输入如下命令:
chmod +x ./hello.sh ./hello.sh #执行hello.sh脚本,结果是输出“Hello World !”注意:
- 一定要输入./hello.sh,表示执行当前目录下的hello.sh,
- 而不能直接输入hello.sh,这样Linux系统会去PATH里寻hello.sh,
- 但当前目录通常不在PATH里,所以会找不到命令
-
作为解释器参数
这种运行方式是,直接运行解释器,其参数就是shell脚本的文件名,如:
/bin/sh hello.sh #这种方式运行的脚本,不需要在第一行指定解释器信息,即使写了也不会起作用。
-
9.3.2 使用变量
-
自定义变量:
Shell脚本中可以自定义变量
#语法:【注意:变量名和等号之间不能有空格】 variable_name= variable_value变量名的命名须遵循如下规则:
- 首个字符必须为字母(a-z,A-Z)。
- 中间不能有空格,可以使用下划线(_)。
- 不能使用标点符号。
- 不能使用bash的关键字(可用help命令查看保留关键字)
-
引用变量:
#引用一个定义过的变量,只要在变量名前面加美元符号即可 $variable_name #可以在引号中使用【推荐给所有变量加上花括号,这是个好的编程习惯。】 ${variable_name}如果变量引用可能引起混淆,可以在变量名外面加花括号以帮助解释器识别变量的边界。比如下面这种情况:
for skill in Ada Coffe Action Java; do echo "I am good at ${skill}Script" done #如果不给skill变量加花括号 echo "I am good at $skillScript" #解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。 -
删除变量:
#语法是: 变量被删除后不能再次使用 unset variable_name -
变量分类:
Shell脚本中有三种变量:
- 局部变量:在脚本或命令中定义,仅在当前Shell实例中有效,其他Shell启动的程序不能访问局部变量。
- 环境变量:Shell启动的程序都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
- Shell变量:由Shell程序设置的特殊变量,有一部分是环境变量,有一部分是局部变量,这些变量保证了Shell的正常运行
9.3.3 控制结构
Shell脚本中可以使用各种流程控制语句,也可以自定义函数。此处我们只关心基本的流程控制语句写法。
-
if
#语法格式: if condition then command1 command2 ... fi #末尾的fi就是if倒过来拼写,后面还会遇到类似的。 #如果将if语句写成一行,语法格式: if condition; then command1; command2; ...; fi -
if else
#语法格式: if condition then command1 command2 ... else command fi -
if else-if else
#语法格式: if condition1 then command1 elif condition2 then command2 else commandN fi #实例:判断两个变量是否相等: a=10 b=20 if [ $a = = $b ] then echo "a 等于 b" elif [ $a -gt $b ] then echo "a 大于 b" elif [ $a -lt $b ] then echo "a 小于 b" else echo "没有符合的条件" fi #输出结果: a 小于 b -
for循环
语法格式: for var in item1 item2 ... itemN do command1 command2 ... done 写成一行: for var in item1 item2 ... itemN; do command1; command2… done; 当变量值在列表里,for循环即执行一次所有命令,使用变量名获取列表中的当前取值;命令可为任何有效的shell命令和语句;in列表是可选的,如果不用它,for循环使用命令行的位置参数。 例如,顺序输出当前列表中的数字: for loop in 1 2 3 4 5 do echo "The value is: $loop" done 输出结果: The value is: 1 The value is: 2 The value is: 3 The value is: 4 The value is: 5 -
while循环
while循环用于不断执行一系列命令,也用于从输入文件中读取数据;
#语法格式为: while condition do command done #下面的例子中,输入信息被设置为变量FILM,按<Ctrl-D>结束循环。 echo 'Press <CTRL-D> to exit' echo -n 'Input your favorite movie: ' while read FILM do echo "YES! $FILM is a great movie." done #运行脚本,输出结果类似下面: Press <CTRL-D> to exit Input your favorite movie: 2046 YES! 2026 is a great movie. -
无限循环
无限循环中一般需要调用特定命令以终止该层次的循环。
Shell提供两个命令来实现该功能:break和continue。
- break命令允许跳出所有循环(终止执行后面的所有循环);
- continue命令仅仅跳出当前循环。
- 此外,还可以在循环中使用exit命令直接终止脚本的执行。
#语法格式: while : do command done #或者 while true do command done #或者 for (( ; ; )) do command done -
until循环
until循环与while循环在处理方式上刚好相反,执行一系列命令直至条件为真时停止。
#语法格式: until condition do command done #条件测试发生在循环末尾,因此循环至少执行一次。 -
case语句
case语句为多选择语句,可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
- case语句取值后面必须为单词in,
- 每一模式必须以右括号结束。
- 取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
- 一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。
- 如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
#case语句格式如下: case 值 in 模式1) command1 ;; 模式2) command2 ;; esac
9.4 Shell变量
前一节中介绍了Shell变量的基本用法,除了在自己定义并使用变量,还有一些不需要我们定义的特殊变量可以直接使用,而我们自己定义的Shell变量也可以导出为环境变量从而在Shell脚本之外产生作用。
9.4.1 Bash变量
当Shell脚本被执行时,Bash会自动设置一些变量,这些变量对于脚本程序而言是只读的,但是其中包含的信息可能非常有用,比如可以通过这些变量获得命令行参数信息。常见的Bash变量如下表所示:
| 变量引用名称 | 含义 |
|---|---|
| $# | 传递到脚本的参数个数 |
| \(* | 以一个单字符串显示所有向脚本传递的参数。 如"\)*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。 | |
| $$ | 脚本运行的当前进程ID号 |
| $! | 后台运行的最后一个进程的ID号 |
| \(@ | 与\)*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "\(2" … "\)n" 的形式输出所有参数。 | |
| $- | 显示Shell使用的当前选项 |
| $? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
| $PPID | 当前进程的父进程的ID号 |
| $SHELLOPTS | 冒号分隔的Shell当前选项 |
| $UID | 当前进程用户的userid |
| $n | 获取命令行参数。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……。$0 为执行的文件名。 |
除了上述只读的Bash变量,还有部分变量由Bash初始化,但是在脚本中可以重新赋值,部分此类变量如下表所示:
| 变量名称 | 含义 |
|---|---|
| BASH_VERSION | 当前bash版本号 |
| OLDPWD | 之前的工作目录 |
| PWD | 当前工作目录 |
| RANDOM | 0~32767之间的一个随机数 |
| SECONDS | 当前Shell启动后经过的秒数 |
| PS1 | 基本提示符,默认情况下,root用户#结尾,普通用户$结尾 |
| PS2 | 附属提示符,默认是“>” |
9.4.2 环境变量
Shell变量分为本地变量和环境变量,本地变量在用户现有运行的脚本中使用,环境变量可以在所有的子进程中使用。两者的本质区别在于保存位置不同:
- 本地变量保存在当前Shell进程中,离开当前进程变量就不存在了;
- 环境变量托管给内核作为当前进程内核数据的一部分,当创建子进程时,环境变量会复制给子进程从而可以被子进程访问到。
环境变量:本身只是负载着一些全局性的信息,真正产生作用还是需要进程读取环境变量的值并作出相应的处理。
- 举例来说,很多人都知道Linux下有名为PATH的环境变量,修改变量的值会影响执行文件的搜索路径,但是之所以会产生影响,是因为Shell会读取这个变量并根据其值搜索不同目录寻找是否存在命令文件。
- 再举一个例子:LANG环境变量会影响进程的locale设置,对于国际化的软件其执行结果就会产生影响,如果执行ls -l命令时,列出的时间是中文表示,说明当前locale是中文的,可以通过执行命令 LANG=en_US ls -l 看到不一样的列表;反之如果执行ls -l命令时,列出的时间是英文表示,可以通过执行命令 LANG=zh_CN ls -l 看到不一样的列表;改变的原因就是改变了LANG的值。
本地变量:可以通过export命令导出成环境变量,实际上所有的环境变量都是通过这种方式产生的,只是这些系统的环境变量一般是在某个Shell初始化脚本中使用export导出的,Shell初始化相关脚本后面会有介绍。
部分常见环境变量的名称及含义如下表所示:
| 变量名称 | 含义 |
|---|---|
| PATH | 决定了Shell将到哪些目录中寻找命令或程序 |
| HOME | 当前用户主目录 |
| LOGNAME | 当前用户的登录名 |
| HOSTNAME | 主机的名称 |
| SHELL | 当前用户Shell类型 |
| LANG | 为国际化程序指定语言和区域信息 |
9.4.3 相关命令
下面说明几个与Shell变量相关的命令,命令细节不再赘述。
-
env命令
env 和 printenv这两个命令用于打印所有的环境变量。
-
set命令
用于显示与设置当前环境的所有的变量,它包括环境变量和一些本地变量,还包括一些自定义Shell特性,如Shell函数等。
与set命令相关的还有unset命令,用于清除变量。
-
export
用于把变量变成环境变量,有效期是当前Shell及其子进程,因此退出当前Shell后,它所设定的环境变量就消失了。
10.5 Shell运算符和表达式
Shell 和其他编程语言一样,支持多种运算符,包括:
- 算数运算符、关系运算符、布尔运算符、字符串运算符、文件测试运算符等;
- 另外在表达式求值时需要用到一些求值和测试条件相关命令。
9.5.1 命令替换
命令替换(Command Substitution)是指 Shell 执行指定命令并使用执行该命令后的结果替换原来内容(即命令替换部分)。
命令替换有两种方式:使用反引号“`”和使用$()
- 两种方式的效果基本上是一样的,区别在于不同的Shell对其支持程度,另外在转义字符处理方面有一定区别。
- POSIX规范建议使用$(命令)的形式,因为更易读,而且转义处理比较简单,
- 但是因为最早引入命令替换功能的形式是反引号,所以在不同Shell中更通用,很多人习惯性地使用。
下面是一个简单的例子,可以在命令行直接输入:
echo `pwd`
#或者
echo $(pwd)
#可以观察到两者执行效果是一致的,都是输出了pwd命令的执行结果。
9.5.2 相关命令
-
expr命令:
用于简单的整数计算和字符串操作。
#语法格式: expr expression #整数计算例子: expr 14 % 9 #结果为:5 expr 10 + 10 #结果为:20 expr 1000 - 900 #结果为:100 expr 1000 / 10 #结果为:100 expr 10 \* 100 #结果为:1000 (使用*时,需用\进行转义) #字符串操作例子: expr length "this is a test" #结果为:14 (计算字符串长度) expr substr "this is a test" 3 5 #结果为:is is (抓取子字符串) expr index "sarasara" a #结果为:2(获取第一个字符出现的位置) #当用于整数计算时,Shell提供与expr命令功能类似的另一种写法: $(( expression )) -
eval命令
eval命令用于那些一次扫描无法实现其功能的变量,该命令对变量进行两次扫描。
#语法格式: eval command #两次扫描的例子如下,执行包含下面命令的在脚本文件: eval echo \$$# #执行结果应该是:显示最后一个命令行参数,如果没有命令行参数则显示脚本名。 #其工作原理是: # 第一遍扫描时“\$”替换为$(\作为转义字符),剩下的“$#”替换为命令行参数的个数(假设为2); # 第二次扫描时,变为执行“echo $2”,即显示最后一个参数。 -
test命令和 【 命令
test命令评估一个表达式,然后返回真或假。
- 一般和 if、while 或 until 命令结合使用,可以对程序流进行广泛的控制。
- test命令有一个更常用的别名:[ (左方括号),这两个命令是等价的。
#语法格式: test expression [ expression ]当使用左方括号而非 test 时,其后必须始终跟着一个空格、要评估的条件、一个空格和右方括号。
- 右方括号不是任何东西的别名,而是表示所需评估参数的结束。
- 条件两边的空格是必需的,这表示要调用 test,以区别于同样经常使用方括号的字符/模式匹配操作。
需要说明,新的Shell又引入一个 [ 命令的扩展表示方法,就是使用 [[ 以及对应的 ]] 结束。
- 扩展的测试命令 [[ 允许其中的判定表达式写起来更直观,
- 包括可以使用“>”、“<”符号作为关系运算符、
- 允许比较字符串、可以使用&&、||等逻辑操作符等。
9.5.3 常见运算符
此处所说的常见运算符指与其他编程语言中类似的运算符,包括算数运算符、关系运算符、布尔运算符、逻辑运算符等。因为比较简单,所以只是简单介绍一下相关算符及其含义,不再举例说明。
-
算术运算符
常见的算术运算符包括:+、-、*、/、%等,其含义与C语言中类似。
需要说明,Shell不支持数学运算,需要通过其他命令来实现,最常用的是expr。
-
关系运算符
关系运算符用于比较两个变量或常量之间的数值关系,只支持数字,不支持字符串,除非字符串的值是数字。常见关系运算符及其含义如下:
- -eq 检测两个数是否相等,相等返回 true。
- -ne 检测两个数是否相等,不相等返回 true。
- -gt 检测左边的数是否大于右边的,如果是,则返回 true。
- -lt 检测左边的数是否小于右边的,如果是,则返回 true。
- -ge 检测左边的数是否大等于右边的,如果是,则返回 true。
- -le 检测左边的数是否小于等于右边的,如果是,则返回 true。
-
布尔运算符
布尔运算符及其含义如下:
- ! 非运算,表达式为 true 则返回 false,否则返回 true。
- -o 或运算,有一个表达式为 true 则返回 true。
- -a 与运算,两个表达式都为 true 才返回 true。
-
逻辑运算符
逻辑运算符及其含义如下:
- && 逻辑的 AND
- || 逻辑的 OR
需要说明,逻辑运算符应用于判断表达式中,只能使用 [[ ]] 方式,用test和 [ ] 方式都不支持。
9.5.4 字符串运算符
字符串运算符应用在字符串上,常见运算符及其含义如下:
- = 检测两个字符串是否相等,相等返回 true。
- != 检测两个字符串是否相等,不相等返回 true。
- -z 检测字符串长度是否为0,为0返回 true。
- -n 检测字符串长度是否为0,不为0返回 true。
9.5.5 文件运算符
文件测试运算符用于检测 Unix 文件的各种属性,常见的属性检测如下:
- -b file 检测文件是否是块设备文件,如果是,则返回 true。
- -c file 检测文件是否是字符设备文件,如果是,则返回 true。
- -d file 检测文件是否是目录,如果是,则返回 true。
- -f file 检测文件是否是普通文件,如果是,则返回 true。
- -g file 检测文件是否设置了 SGID 位,如果是,则返回 true。
- -k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。
- -p file 检测文件是否是具名管道,如果是,则返回 true。
- -u file 检测文件是否设置了 SUID 位,如果是,则返回 true。
- -r file 检测文件是否可读,如果是,则返回 true。
- -w file 检测文件是否可写,如果是,则返回 true。
- -x file 检测文件是否可执行,如果是,则返回 true。
- -s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。
- -e file 检测文件(包括目录)是否存在,如果是,则返回 true。
9.5.6 综合性例子
综合利用上面介绍的知识,我们可以编写一些简单的Shell脚本完成一些基本的功能。
-
查看目录信息
#编写如下内容的Shell脚本文件,运行查看效果并分析其功能。 \#!/bin/sh if [ $# != 1 ] then echo "Usage: $0 <dir>" exit fi if [ ! -e $1 ] then echo $1 does not exist! exit fi if [ ! -d $1 ] then echo $1 is not a directory! exit fi echo "There are "$(ls $1 | wc -w)" entries in $1" echo "Size of $1 is "$(du -sh $1 | cut -f1)"." -
查看命令行参数信息
#编写如下内容的Shell脚本文件,运行查看效果并分析其功能。 \#!/bin/sh echo "There are "$#" parameters." i=1 for arg in $* do echo "parameter $i is "$arg"." i=$(expr $i + 1) done -
交互查看Shell变量
#编写如下内容的Shell脚本文件,运行查看效果并分析其功能。 \#!/bin/sh unset var while [ "$var" != "end" ] do echo -n "please input name of variable start with $: " read var if [ "$var" = "end" ] then break fi echo "$var = $(eval echo $var)" done
9.6 重新认识Shell
了解一些Shell脚本相关知识后,对于交互式Shell也会有更深入的认识。
9.6.1 Shell命令行扩展
Shell命令行中有一些特殊符号会先被扩展然后解释执行,之前章节已经涉及部分扩展方式,这些扩展有的与Shell变量相关,有的无关,在此再做一次集中摘要介绍。
-
命令历史扩展
在命令行输入 !n 将会由Shell自动扩展成命令历史记录中的第n条。
-
花括号扩展
Shell会自动把 {} 中的逗号分隔列表扩展成几个不同的项,分别执行命令行指定的命令,这样可以用一条命令实现批量操作。
例如,mkdir dir{1,2,3}命令将会创建dir1、dir2、dir3三个目录。
在一个命令行中运用多个 {} 扩展将会按选项全部组合实现扩展。
-
波浪号扩展
在命令行参数中使用 ~ 符号,Shell会自动扩展为当期用户主目录的路径;
如果使用“~用户名”,则会扩展成相应用户主目录的路径。
-
Shell变量扩展
Shell会把\(variable_name或\){variable_name}扩展成相应Shell变量的值
-
算术扩展
在命令行使用$((表达式)),会计算表达式,扩展成得到的结果值。
-
命令替换
在命令行使用 "反引号"命令“反引号”或 $(命令),Shell会在新建的子Shell中运行相应命令,并扩展成该命令的输出结果。
-
文件名通配符
主要是几种可以在文件名检索时使用的通配符:*、?、[...]、
[^...] -
引号和转义符
命令行中经常会出现需要用引号的情况,例如文件名中有空格。
Shell对于两种引号中间的字符串解释方法有所区别:
- '...' 中间的所有字符Shell都不解释;
- "..." 中间的内容Shell一般不解释,但下面几个字符例外:$ 、 ! 、 ` 、 \,如果希望针对这几个字符也不做扩展,就需要使用转义字符 \ ,转义字符后面的字符Shell将不做解释。
9.6.2 导入执行Shell脚本和Shell初始化
Shell脚本的编写和执行方法,下面要介绍一些特殊的脚本执行。
-
source命令
前面已经说明,直接执行一个脚本的时候,其实是在一个子Shell中运行的,脚本执行完后子Shell自动退出,子Shell中所有操作的结果(例如定义或修改的环境变量)都将随之消失。
如果我们希望脚本的执行结果影响到当前Shell,就需要由当前Shell直接解释执行脚本,而不是新创建一个子Shell去执行脚本。
Shell的内置命令source用于实现这个功能,source命令还有一个别名:“.”(点命令),其格式如下:
source 脚本文件名 . 脚本文件名 #source(或点)命令通常用于重新执行刚修改的初始化脚本。 -
Shell初始化
当任何一个Shell进程创建后都要进行初始化,所谓初始化实际就是用source方式执行一些特定的脚本,但是不同的Shell类型会执行不同的脚本文件,所以首先需要区分两种不同类型的Shell:登录Shell和非登录Shell。
- 登录Shell是当用户成功登录后自动创建的Shell进程(实际是login进程根据/etc/passwd文件中的信息创建),
- 其他方式再创建的Shell进程都是非登录Shell,例如在图形界面下打开一个虚拟终端窗口时创建的交互式Shell,以默认方式执行Shell脚本时创建的非交互子Shell等。
与Shell初始化相关的脚本文件包括:
- /etc/profile :主要用来设置所有用户都需要并一致的环境变量,用户登录成功后执行一次;
- /etc/bashrc :主要用来设置所有用户都需要并一致的环境变量,每次新建Shell进程执行一次;
- ~/.bash_profile :主要用来设置特定用户需要的环境变量,用户登录成功后执行一次;
- ~/.bashrc :主要用来设置特定用户需要的环境变量,每次新建Shell进程执行一次。
上面所列这些文件的位置和名字都是约定好的,特定类型的Shell初始化时会以source方式执行其中部分或全部脚本。所有这些文件都是同样语法的Shell脚本文件,主要的作用就是定义一些Shell变量、导出一些环境变量,之所以要分到这些不同文件中,就是要针对不同情况执行不同集合。
对于登录Shell,初始化的执行分两条线索:
- source方式执行/etc/profile,/etc/profile执行时会自动查找/etc/profile.d目录,然后以source方式执行所有找到的*.sh文件;
- source方式执行/.bash_profile,/.bash_profile执行时会以source方式执行/.bashrc,/.bashrc执行时又会以source方式执行/etc/bashrc。
非登录Shell初始化时:
- 从执行~/.bashrc开始,
- 进而执行/etc/bashrc。
此外:
- 登录Shell退出时会执行~/.bash_logout,
- 非登录Shell退出时不会再执行任何脚本。
了解Shell初始化过程的意义在于,当需要通过修改环境变量改变自己的操作环境时(例如修改语言选项),知道应在在哪个文件中修改才是合适的。
浙公网安备 33010602011771号