Linux笔记--Shell编程入门
-
查看当前环境下使用的shell,可以使用“echo $SHELL”
-
运行shell:shell脚本是纯文本文件,通常以.sh作为后缀名,方便系统识别文件类型,但不是必需的。脚本文件中的第一行要指明系统使用哪种shell解释该shell文件,如#!/bin/bash、#!/bin/sh。运行shell脚本有两种方法:一种是赋予执行权限,直接运行;另一种是使用bash命令直接运行脚本(这种方式会忽略脚本文件是否有执行权限。)
shell变量
概述
- shell变量主要有用户自定义变量、环境变量、位置变量和预定义变量。
- shell变量无须声明,可以直接使用或赋值。清除变量可以使用“unset 变量名”命令来实现。
- shell变量没有数据类型的概念,不管向变量赋什么值,都会被作为字符串。
- 注意变量名区分大小写。变量名通过“变量名=值”的形式来定义一个变量的值,等号“=”左右两边不能有空格。
- 1.值如果用单引号‘’括起来,则单引号内部的全部内容都会作为普通字符输出; 2.如果用双引号"“括起来,则双引号内部会解析$(美元符号)、\(反斜杠)、` 反引号)和”(双引号)这些特殊字符; 3. 如果用反引号(在键盘上Esc键下边,1的左边)括起来,则反引号内部的内容会作为可以执行的命令赋值给该变量。
- 使用shell变量时,需要在变量名前面加美元符号“$”,也可以把变量名用花括号{}括起来帮助解释器识别变量的边界。
环境变量
环境变量一般是指定操作系统运行环境的一些参数,通常由系统在启动时设置,一般用大写字母表示。常见的环境变量如下:
PATH:系统路径。
HOME:当前用户的主目录。
HOSTNAME:主机名称。
LOGNAME:当前登录的用户名。
SHELL:当前使用的shell类型。
HISTSIZE:保存历史命令记录个数
可以使用env命令查看所有环境变量,也可以通过echo命令查看某个环境变量
位置变量和预定义变量是特殊的变量,有特定的含义。
位置变量
在运行shell脚本文件时,可以使用位置变量在脚本文件内部接收命令行传递的参数,也可以在调用函数时向函数内部传递参数。这种在脚本文件或函数内部接收参数使用的$n称为位置变量,即执行shell脚本时获取命令参数信息的变量。
例如,$1是接收第一个参数的位置变量,$2是接收第二个参数的位置变量,依次类推,n表示参数接收顺序。注意,$0表示命令本身或脚本文件名,无论脚本是否有参数, 0 均 可 用 ; 如 果 n 大 于 9 , 则 需 要 用 一 对 花 括 号 括 起 来 , 如 0均可用;如果n大于9,则需要用一对花括号{}括起来,如 0均可用;如果n大于9,则需要用一对花括号括起来,如{10}。
预定义变量
预定义变量是在shell一开始时就定义好的变量,不能重新定义,用户只能根据shell的定义来使用这些变量。预定义变量的表示方法和意义如下:
-
列表可以用 ∗ 和 *和 ∗和@等变量来表示,即把在执行脚本文件时向文件传递的多个参数内容作为列表。
-
如果 ∗ 和 *和 ∗和@不加双引号,循环结果相同,把每一个参数作为独立的元素输出,
-
$@加双引号,循环结果和不加双引号时的结果一致,即每个参数独立,有几个参数,循环体就循环几次;如果给$*加双引号,则会把多个参数作为一个整体输出,即循环体只循环一次。
变量替换
变量替换是一种为变量赋值的方式,根据变量的状态来改变它的值。如果变量已经存在并不为空,则不改变变量的值;如果变量不存在或值为空,则给该变量赋新值。变量替换的一般形式为:变量2=${变量1op新值}
其中,op是:-、:+、:=、:?4个操作符中的一个,注意操作符前后没有空格。
变量2=${变量1:-新值}:如果变量1不存在或为空,则给变量2赋新值,但不改变变量1的值。
变量2=${变量1:+新值}:如果变量1不空,则给变量2赋新值,但不改变变量1的值。
变量2=${变量1:=新值}:如果变量1不存在或为空,则给变量2赋新值,并将变量1的值设置为新值。
变量2=${变量1:?新值}:如果变量1不存在或为空,则将新值发送到标准错误输出,可以用来检测变量1是否可以被正常赋值。这种替换出现在shell脚本中,脚本将停止运行。
“:=”在变量1不存在或为空时,要被赋值为新值。
宏观上的两点:
1)变量2的值可能来自新值,也可能来自变量1的值;
2)变量1的值可能被修改为新值。
输入/输出
read命令用来接收标准输入(键盘)的输入,为变量赋值。read的命令格式如下:
read 选项 变量名
部分选项如下:
-p:后跟一条提示信息,即在输入前打印提示信息。
-t:后跟数字,用来指定等待用户输入的秒数。
-n:后跟数字,用来指定接收的字符个数。
-s:隐藏输入的数据,即输入数据时不在屏幕上显示,通常用于输入密码等机密信息。
echo命令用于变量值或字符串的输出,并在最后默认加上换行符。echo命令格式如下:
echo 选项 输出内容
选项:
-e:支持反斜线的控制字符的转换,部分控制字符见下表。
-n:取消输出后行末的换行符号,即内容输出后不换行。
shell计算
shell的算术运算符也主要包括加(+)、减(-)、乘(*)、除(\)、求余(%)和幂运算(**)等,由于shell中每一个变量的值都是字符串,因此并不能直接进行算术运算。在shell中需要使用数学计算命令实现算术运算。shell中常见的数学计算命令如下:
![](https://img-blog.csdnimg.cn/c450fba3591d4074b38f653bd78bde7f.png)
其中,bc可以处理浮点型数据,其余的命令均可处理整数;expr还可以处理字符串,但有很多格式要求;declare -i虽然可以进行整数运算,但仅支持最基本的加、减、乘、除和取余运算,并不支持逻辑运算。(())、let和$[]相似,都可以用于整数运算。
双小括号(())
是shell中专门用来进行整数运算的命令,写法灵活。(())命令格式:((表达式))
- 表达式可以有一个,也可以有多个;
- 若有多个表达式,表达式之间用逗号“,”分隔,以最后一个表达式的值作为整个命令的执行结果。
- 在双小括号前需要加$,才可以获取(())命令的计算结果,结果可以赋值给变量,也可以使用输出命令echo直接打印出来。
- 在双小括号里面使用变量,变量前面不需要加$,(())会自动解析变量名。
[root@localhost cheney]# ((1+2))
[root@localhost cheney]# echo ((1+2))
bash: 未预期的符号 `(' 附近有语法错误
[root@localhost cheney]# echo $((1+2))
3
[root@localhost cheney]# echo $((1+2,8+9))
17
bc命令
bash shell内置对整数运算的支持,但不支持浮点运算。bc是一种支持任意精度的计算器语言,能够很方便地进行浮点运算。
交互模式:
在shell命令行中通过bc命令进入交互式计算模式,会显示bc的版本信息和简单介绍,若想进入交互模式而不显示这些内容,则可以添加“-q”选项,即使用“bc -q”进入交互模式。输入quit可以退出bc的交互模式。
- 直接输入要计算的表达式 (如3.14*2)并按Enter键,会立即返回计算结果(6.28)
- 在做除法或求余运算时,需要用bc的内置变量scale指定小数位数,默认为0。
- bc还支持使用变量实现计算,变量一旦被定义,就可以在当前会话中被使用,使用bc中定义的变量不需要在变量名前加$符号。
非交互模式
bc计算命令可以在shell命令行或脚本中使用,基本格式如下:
变量=$(echo "[定义变量];表达式"|bc)
# 表达式就是希望计算的数学表达式
# 若需要指定小数位数或需要使用变量,则要在表达式前指定scale的值或定义变量
# 多个表达式之间用分号“;”隔开。
# 使用shell命令行中的变量,需要用$符号,使用bc中定义的变量,不需要$符号。
例如:
[root@localhost cheney]# echo "1+2"
1+2
[root@localhost cheney]# echo "1+2"|bc
3
[root@localhost cheney]# b=3
[root@localhost cheney]# echo "scale=3;a=8;a/$b"|bc
2.666
流程控制
shell具有一般高级程序设计语言所具有的流程控制结构。当用户需要根据不同情况选择执行不同的命令时,可以使用if和case选择结构实现分支控制。当用户需要重复执行相同的命令时,使用for、while和until等循环结构实现循环控制。若循环次数已知或确定,可以使用for循环;若不知道循环次数,需要根据判断条件是否为真决定是否继续循环,使用while语句和until循环。当需要在未达到循环结束条件时强制跳出循环,通过break和continue实现。
测试命令
为了正确处理程序在执行过程中的各种情况,shell提供条件测试命令,可以实现字符串、整数和文件测试,用来测试指定条件的真假。
当条件为真时,整个条件测试的返回值为0;反之,则返回非0值。条件测试命令是test命令,其语法格式如下:
test 条件表达式
# 或者
[ 条件表达式 ]
- 这里的条件表达式和左右的方括号之间必须有一个空格。
- test和[]都可以实现条件测试,在条件测试命令中使用变量名需要用双引号括起来。
几类运算符的使用
字符串运算符
![](https://img-blog.csdnimg.cn/02de75925a614a21ad18b13b26f792aa.png)
例如,测试字符串是否为空。
[root@localhost cheney]# [ abc ]
[root@localhost cheney]# echo $?
0
[root@localhost cheney]# a=
[root@localhost cheney]# [ "$a" ]
[root@localhost cheney]# echo $?
1
- [ abc ]测试abc字符串是否为空。echo $?输出上一条命令的退出状态,命令正确执行则输出0,反之则输出非0值。很显然测试结果是字符串不为空,因此返回0。变量a为空字符串,[ “$a” ]测试a变量是否为空,echo $?返回1,说明该变量为空。
又例如:
[root@localhost cheney]# echo $?
1
[root@localhost cheney]# [ a=b ]
[root@localhost cheney]# echo $?
0
- 可以发现“=”左右两边是否有空格的测试结果是不同的。[ a = b ]中“=”两边有空格,这时判断字符串1“a”和字符串2“b”是否相等,结果返回1,显然是不相等的;而[ a=b ]中“=”两边没有空格,这时判断字符串“a=b”是否为空,结果返回0,显然该字符串不为空。因此,运算符“=”和“!=”左右两边要有空格,这是由于shell要求运算符的左右两边必须保留空格。
整数运算符
![](https://img-blog.csdnimg.cn/8ae686e49206499e82bff90d896e501c.png)
例如:
[root@localhost cheney]# [ 12 -eq 24 ]
[root@localhost cheney]# echo $?
1
[root@localhost cheney]#
- 操作符与操作数中间应该有空格
- 返回1表示错误,0表示正确
文件操作符
![](https://img-blog.csdnimg.cn/9e7765682ed341b5b639257520ac5049.png)
例如:
[root@localhost cheney]# [ -a test.txt ]
[root@localhost cheney]# echo $?
1
- 结果为1表示没有该文件
逻辑操作符
![](https://img-blog.csdnimg.cn/b668419a43e34cc2a7d9d0d25df19318.png)
- 逻辑非中的“!”与后面的表达式之间空一格。
例如:
测试表达式“a = b”(a与b是否相等)和表达式“12 -lt 24”(12是否小于24)的逻辑与和逻辑或的测试结果。通过前面的测试已知,表达式“a = b”为假,表达式“12 -lt 24”为真。
[root@localhost cheney]# [ a = b -a 12 -lt 24 ]
[root@localhost cheney]# echo $?
1
[root@localhost cheney]# [ a = b -o 12 -lt 24 ]
[root@localhost cheney]# echo $?
0
- 可以看到这两个表达式的逻辑与的结果返回1 (为假),逻辑或的结果返回0 (为真)。
if选择结构
if选择结构可以根据条件的真假进行分支控制。格式如下:
if 命令1
then
命令(命令组)
elif 命令2
then
命令(命令组)
... ...
else
then
命令(命令组)
fi
- 其中if、then、elif和fi是关键字。
- 这里的分支控制条件,即if后面的shell命令,多用条件测试命令。
- 在shell中,每个命令都会有一个退出状态码。若命令正常退出,则命令退出状态码为0,则执行then后面的命令或命令组;若执行错误,则命令退出状态码为非0值,则不执行then后面的命令。
- 注意最后必须以fi闭合,因此,then后面可以有多条命令,无须用大括号{}括起来。
- 为了使代码更紧凑,也可以将then子句和if子句放在一行上,但需要在then前面加分号,表示if子句结束,后面是then子句。
即 if 命令;then
case选择结构
当对两种情况进行处理时,可以使用if-else结构;当对多种情况进行处理时,可以使用if-elif结构。如果情况较多,if-elif将会有很多elif分支,变得很长,不容易理解,因此shell也提供了一个专门处理多分支情况的结构,即case选择结构。基本格式如下:
case 变量 in
模式1)
命令(命令组)
;;
模式2)
命令(命令组)
;;
... ...
*)
命令(命令组)
;;
esac
- 其中,case、in和esac是关键字。
- case结构中的模式可以是一个数字、一个字符串或一个正则表达式。
关于正则表达式:
- *:匹配任意字符
- [ ]:匹配方括号内的任意字符,可用连字符-指定连续字符的范围(如[a-z]表示所有小写字母)
- | :匹配符号前面或者后面的字符
- case选择结构会将变量与模式1、模式2、模式3等模式逐个匹配,如果与某个模式匹配成功,则执行该模式后面对应的命令,直到双分号“;;”停止,结束case选择结构,即程序跳出case语句,开始执行case后的其他语句。
- 如果变量的值与任何一个模式都不匹配,则执行“)”后面的命令,直到双分号“;;”或“esac”停止。“”表示其他所有值,“*)”是当变量没有匹配到任何一个模式时的分支,当然也可以没有这个分支
for循环结构
当循环次数已知或确定时,可以使用for循环语句来多次执行一条或一组命令,循环体由语句括号do和done来限定。for循环通常用于遍历整个对象或数字列表。
for循环结构可以分为带列表的for循环和不带列表的for循环两种结构。
不带列表的for循环结构
for ((表达式1;表达式2;表达式3))
do
命令(命令组)
done
- 该for循环结构和C语言的for循环结构类似,执行条件被双小括号(())括起来,表达式1通常是循环变量初始化,表达式2通常是决定是否执行for循环体的条件,表达式3通常是改变循环变量的值,如递增或递减,表达式之间用“;”隔开。
- 执行过程和C语言的for循环执行过程是一致的
带列表的for循环结构
for 循环变量 in 列表
do
命令(命令组)
done
- 其中,for、do和done是关键字。
- 列表可以是一系列数字或字符串,元素之间用空格隔开。
- do和done之间的命令为循环体,即循环结构中重复执行的部分,循环体执行的次数与列表中元素的个数有关。该for循环结构在执行时,会将列表的第一个元素赋值给循环变量,然后执行循环体,当循环体执行完毕后,将列表中的第二个元素赋值给循环变量,然后执行循环体,直到列表中所有的元素都被访问后,for循环结构终止,程序继续执行done后面的其他语句。
- 如果循环变量的取值是连续的,也可以用一个范围来代替列出所有的元素。具体格式为{start…end…step},start是起始数字或字母,end是终止数字或字母,step是步长
for i in 10 8 6 4 2 0
等价于
for i in {10…0…2}
- 除了列表直接指定之外,也可以通过反引号或$()获取命令的执行结果得到列表的值。例如:
列出当前目录下所有文件的名称:
for filename in $(ls)
do
echo $filename
done
- 列表还可以用 ∗ 和 *和 ∗和@等变量来表示,即把执行脚本文件时向文件传递的多个参数内容作为列表。如果 ∗ 和 *和 ∗和@不加双引号,循环结果相同,把每一个参数作为独立的元素输出,$@ 加双引号,循环结果和不加双引号时的结果一致,即每个参数独立,有几个参数,循环体就循环几次;如果给$*加双引号,则会把多个参数作为一个整体输出,即循环体只循环一次
while循环结构
while循环是当型循环,是一种简单的循环结构,当满足循环条件时,执行循环体,直到不满足循环条件时退出循环。其基本格式如下:
while 命令
do
命令(命令组)
done
- 其中,while、do和done是关键字。
- while后面的循环条件可以是任意合法的shell命令,通常使用条件测试命令。
- do和done之间的命令为循环体。while循环的执行过程是先判断while后命令的退出状态,退出状态是0时,执行do后的循环体,到done结束后,while后的命令会再次执行,如果退出状态仍为0,则循环继续执行。直到while后的命令退出状态是非0值时,退出while循环,执行done后面的语句。若第一次执行while后的命令退出状态就是非0值,则直接退出while循环,即一次循环体也不执行。
until循环结构
until循环是直到型循环。当条件不满足时,执行循环体,不断循环,直到条件满足,退出循环。其基本格式如下:
until 命令
do
命令(命令组)
done
- 其中,until、do和done是关键字,do和done之间的命令为循环体。
- until循环结构和while循环结构相似,在执行循环时,都是先判断循环条件的值,然后决定是否执行循环体。
- until循环结构和while循环结构二者的区别在于while循环是当型循环,只有当循环条件满足时才执行循环体,否则退出循环;until是直到型循环,当循环条件不满足时执行循环体,直到条件满足时退出循环。
函数
和其他程序设计语言一样,在shell中也存在函数。函数本质是一个函数名到某个代码块的映射,即用户定义了函数之后,可以通过函数名来调用其所对应的一组代码。尽管在shell中,函数并不是必需的编程元素,但通过使用函数可以将一些相对独立的代码变成函数,提高程序的可读性和重用性,如果程序的其他地方需要相同的功能,只需要调用该函数就可以了,无须重复编写相同的代码。
函数定义的基本语法格式如下:
function 函数名 ()
{
命令(命令组)
}
# 其中function关键字和函数名后面的小括号均可省略
- (1)函数名的命名规则和变量的命名规则基本相同,可以使用数字、字母或下划线,但只能以字母或下划线开头。
- (2)函数体的左花括号“{”和后面的命令之间必须有空格或换行,如果最后一条命令和右花括号“}”写在同一行,则最后一条命令后要有分号“;”。
- (3)函数定义中没有返回值,也没有参数列表,但可以在调用函数时传递参数。
函数必须先定义,然后通过函数名称调用该函数。函数调用的基本语法如下:
函数名 参数1 参数2……
- 这里的参数和参数之间用空格隔开。当函数里无须传递参数时,只需要函数名称即可调用该函数。
- 可以在调用函数时给函数传递参数,并在函数内部通过$1、$2和$3等位置变量接收参数。
- 定义函数时需要使用小括号“( )”,调用函数时无须用小括号。如果某个参数中含有空格,则用引号将其引用起来。
- 函数中可以使用return命令返回到脚本中调用该函数的地方,如果return后面跟一个数字,则表示函数的退出状态码,这里的数字只能是0~255的整数值,该数字会赋值给变量$?。如果需要返回其他数据,可以使用echo或全局变量来实现。
其它
详解shell脚本的运行方式
shell脚本主要有source、sh、bash、./几种执行方式
source命令执行
用法:
source FileName
# 或者
.filename
source(或点)命令通常用于重新执行刚修改的初始化文档。
sh和bash命令执行
用法:
sh FileName
bash FileName
- 在当前bash环境下读取并执行FileName中的命令。该filename文件可以无"执行权限"
- 两者在执行文件时的不同,是分别用自己的shell程序来跑文件
./命令执行
用法:
./FileName
- 打开一个子shell来读取并执行FileName中命令。
- 运行一个shell脚本时会启动另一个命令解释器.每个shell脚本有效地运行在父shell(parent shell)的一个子进程里.
区别
-
./.sh的执行方式等价于sh ./.sh或者bash ./*.sh,此三种执行脚本的方式都是重新启动一个子shell,在子shell中执行此脚本。
-
source ./.sh和 ./.sh的执行方式是等价的,即两种执行方式都是在当前shell进程中执行此脚本,而不是重新启动一个shell 而在子shell进程中执行此脚本。