Linux用bash写脚本
1.通配符
通配符一般用在shell语言中,通配符中常见的元字符如下。
(1)[]:匹配一个字符,匹配的是出现在中括号中的字符。
(2)[abc]:匹配一个字符,且只能是a或b或c。
(3)[a-z]:“-”有特殊意义,表示“到”的意思,这里表示a~z,即匹配任一字母。
(4)[0-9]:表示匹配任一数字。
如果想去除含有特殊意义的字符,前面加“\”表示转义,即去除此字符的特殊意义。
(5)[!a-z]:这里的“-”就没有“到”的意思了,匹配的是“a”或“-”或“z”这三个
中的一个。
如果想表示“除了”的意思,则在第一个中括号后面加“!”或“^”。
(6)[!a-z]:表示除字母外的其他字符
(7)?:表示一个任意字符,这里强调是一个,不是0个也不是多个,但不能匹配表示隐藏
文件的点。
(8)*:表示任意多个任意字符,可以是0个,也可以是1个或多个,但不能匹配表示隐藏
文件的点。
先创建目录xx并在目录中创建如下几个测试文件,命令如下。
[root@rh1 ~]# mkdir xx
[root@rh1 ~]# cd xx
[root@rh1 xx]# touch 1_aa aa11 Aa11 _aaa aa.txt f1aa u_12
[root@rh1 xx]# ls
1_aa aa11 Aa11 _aaa aa.txt f1aa u_12
找出首字符是字母、第二个字符是数字的文件,命令如下。
[root@rh1 xx]# ls [a-z][0-9]*
f1aa
找出首字符是字母、第二个字符不是数字的文件,命令如下。
[root@rh1 xx]# ls [a-z][^0-9]*
aa11 Aa11 aa.txt u_12
找出首字符不是字母、第二个字符不是数字的文件,命令如下。
[root@rh1 xx]# ls [^a-z][^0-9]*
1_aa _aaa
可以看到,找出来的文件完全符合我们的需求。
下面找出首字符是大写字母、第二个字符是非数字的文件,命令如下。
[root@rh1 xx]# ls [A-Z][!0-9]*
Aa11 u_12
可以看到,首字符是大写字母的文件列出来了,首字符是小写字母的文件有的列出来了,有的没有列出来,所以[a-z]或[A-Z]有时并不精确。如果要更精确,可以用如下元字符
(1)[[:upper:]]:纯大写。
(2)[[:lower:]]:小写。
(3)[[:alpha:]]:字母。
(4)[[:alnum:]:字母和数字。
(5)[[:digit:]]:数字。
列出首字符是小写字母、第二个字符是数字的文件,命令如下。
[root@rh1 xx]# ls [[:lower:]][0-9]*
f1aa
列出首字符是大写字母、第二个字符是数字或字母的文件,命令如下。
[root@rh1 xx]# ls [[:upper:]][[:alnum:]]*
Aa11
2.变量
所谓变量,指的是可变的值,并非具体的值。例如,我自己嘴中发出的“我”,指的是我自己,张三嘴中发出的“我”,指的是张三,那么这个“我”就是一个变量。变量可以分为本地变量、环境变量、位置变量和预定义变量。
2.1本地变量
定义本地变量的格式如下。
变量名=值
定义变量有以下几点需要注意。
(1)变量名可以包含_、数字、大小写字母,但不能以数字开头。
(2)“=”两边不要有空格。
(3)“值”如果含有空格,要使用单引号’'或双引号""引起来。
(4)定义变量时,变量名前是不需要加
的,引用变量时需要在变量名前加
的,引用变量时需要在变量名前加
的,引用变量时需要在变量名前加。
变量名不能以数字开头,变量名只能是字母、数字、下划线的组合
创建一个测试用的文件夹
[root@rh1 xx]# mkdir yy ; cd yy
[root@rh1 yy]#
下面开始练习定义变量,命令如下。
[root@rh1 yy]# aa=123
在使用本地变量时,变量名前需要加$,命令如下。
[root@rh1 yy]# echo $aa
123
[root@rh1 yy]#
本地变量的特点是只能影响当前shell,不能影响子shell。
[root@rh1 yy]# echo $aa
123
[root@rh1 yy]# echo $$
1945
当前shell的PID是1945。下面打开一个子shell。
[root@rh1 yy]# bash
[root@rh1 yy]# echo $$
2424
这个子shell 的PID是11374。
[root@rh1 yy]# echo $aa
[root@rh1 yy]#
可以看到,没有aa变量。
[root@rh1 yy]# exit
exit
[root@rh1 yy]# echo $$
1945
[root@rh1 yy]# echo $aa
123
[root@rh1 yy]#
再次退回到原来的bash,又有了aa变量,
定义变量除刚才显式的定义外,还可以使用如下两
种方法。
(1)把一个命令的结果赋值给一个变量,这个变量要使用$()括起来,或者用反引号“引起来。这里是反引号,与波浪号~。
例如,定义一个名称是ip的变量,对应的值是ens160的IP,命令如下。
[root@rh1 yy]# ip=$(ifconfig ens160 | awk '/inet /{print $2}')
[root@rh1 yy]# echo $ip
192.168.25.170
(2)通过read命令来获取变量
read的用法如下。
read -p "提示信息" 变量
当遇到read命令时,系统会等待用户输入,用户所输入的值会赋值给read后面的变量,命令如下。
[root@rh1 yy]# read -p "请输入您的名字:" a
请输入您的名字:tom
[root@rh1 yy]# echo $a
tom
[root@rh1 yy]#
当执行read这条命令时,系统会提示用户输人一些内容,所输入的内容会赋值给aa变量。这里我们输入的是 tom,所以打印aa变量时,看到的值是tom。
这样的用法比较适合写需要和用户交互的脚本。
2.2环境变量
定义环境变量的注意点和本地变量是一样的。在定义环境变量时,前面加上export 即可,命令如下。
[root@rh1 yy]# export bb=123
[root@rh1 yy]#
或者先定义为本地变量,然后再通过export转变为环境变量,命令如下。
[root@rh1 yy]# bb=123
[root@rh1 yy]# export bb
要想查看所有的环境变量,可以执行env命令。
环境变量的特点是可以影响子shell,这里强调的是子shell,不能影响父shell。
[root@rh1 yy]# echo $$
1945
[root@rh1 yy]# echo $bb
123
当前shell的PID是1945,里面有一个环境变量 bb。
[root@rh1 yy]# bash
[root@rh1 yy]# echo $$
2679
[root@rh1 yy]# echo $bb
123
打开一个子shell,PID为2679,里面可以看到bb变量的值,说明环境变量已经影响到子shell 了。
[root@rh1 yy]# export bb=456
[root@rh1 yy]# exit
exit
在子 shell中重新给bb赋值为456,然后退回到父shell。
[root@rh1 yy]# echo $$
1945
[root@rh1 yy]# echo $bb
123
可以看到,在父shell 中,bb的值仍然是123,说明在子shell 中定义的变量不会影响到父shell
系统中默认已经存在很多个变量,如下所示。
(1)UID:表示当前用户的uid。
(2)USER:表示当前用户名。
(3)HOME:表示当前用户的家目录。
分别显示这些变量的值,命令如下。
[root@rh1 yy]# echo $UID
0
[root@rh1 yy]# echo $USER
root
[root@rh1 yy]# echo $HOME
/root
有一个很重要的环境变量PATH,当我们执行命令时,一定要指定这个命令的路径,如果没有写路径,则会到PATH变量所指定的路径中进行查询。先查看当前用户的PATH变量,命令如下。
[root@rh1 yy]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
PATH变量由多个目录组成,每个目录之间用冒号“:”分隔,我们把写好的脚本放在PATH变量指定的目录中之后,运行此脚本时就不需要指定路径了。
查看bdqn用户的PATH变量,命令如下。
[bdqn@rh1 ~]$ echo $PATH
/home/bdqn/.local/bin:/home/bdqn/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
这里展示了bdqn用户的PATH 变量,如果tom写了一个脚本之后,就可以把这个脚本放在自己家目录下的.local/bin或bin目录下(/.local/bin或/bin)。如果这两个目录不存在,创建出来即可。
以上定义的环境变量也只是在当前终端中生效,关闭终端之后这个变量也就消失了。如果想让定义的变量永久生效,可以写人家目录的.bash_profile中。因为打开终端时,首先会运行家目录下的一个隐藏文件.bash_profile。
2.3位置变量和预定义变量
运行脚本时,有时后面是需要加上参数的。但是我们在写脚本时并不能预知后期在脚本后面跟上什么参数,这时就能用到位置变量了,位置变量如下。
$0:表示脚本的名称。
$1:表示第1个参数。
$2:表示第2个参数。
…
${10}:表示第10个参数。
这里$后面的数字如果不是个位数,则要用{}括起来。
系统中还内置了一些预定义变量。
$#:表示参数的个数。
$*:表示所有的参数。
写一个带参数的脚本,内容如下。
[root@rh1 yy]# cat scl.yaml
#/bin/bash
echo "这是我第一个脚本,脚本名称是 $0"
echo "第 1 个参数是:$1"
echo "第 2 个参数是:$2"
echo "第 3 个参数是:$3"
echo "此脚本一共有 $# 个参数,它们分别是:$*"
给这个脚本加上可执行权限,并加参数运行,命令如下。
[root@rh1 yy]# chmod +x scl.yaml
[root@rh1 yy]# ./scl.yaml tom bob mary
这是我第一个脚本,脚本名称是 ./scl.yaml
第 1 个参数是:tom
第 2 个参数是:bob
第 3 个参数是:mary
此脚本一共有 3 个参数,它们分别是:tom bob mary
运行这个脚本时,共指定了3个参数:tom、bob、mary,它们分别赋值给了$1、$2、KaTeX parse error: Expected 'EOF', got '#' at position 6: 3。这里S#̲被自动赋值为3,因为总共有3个…*。
运行如下命令
[root@rh1 yy]# set a b c d e f g h i j k
查看此命令的第1个和第9个参数,命令如下。
[root@rh1 yy]# echo $1
a
[root@rh1 yy]# echo $9
i
第1个参数是a,第9个参数是i。下面查看第10个参数,命令如下。
[root@rh1 yy]# echo $10
a0
第9个参数是i,那么第10个参数应该是j才对,这里显示为a0,为什么呢?因为这里先把$10当成了$1+0,$1的值是a,所以$10的值为a0。
所以,在位置变量中数字超过10时,要用{}括起来,下面的命令才是正确的。
[root@rh1 yy]# echo ${10}
j
另外,在引用变量时,双引号和单引号是有区别的,直接看一个例子。
[root@rh1 yy]# xx=tom
[root@rh1 yy]# echo "my name is $xx"
my name is tom
[root@rh1 yy]# echo 'my name is $xx '
my name is $xx
这里先定义一个变量xx=tom,如果变量在双引号中引用,则会被解析成具体的值;如果变量出现在单引号中,则不被解析。
2.3返回值
执行某命令之后,结果不是正确的就是错误的。命令正确执行了,返回值为0,如果没有正确执行则返回值为非零。返回值为非零,不一定是语法错误,执行结果如果有“否定”的意思,返回值也为非零。例如, ping 192.168.26.3,语法没有错误,但是没有ping通,返回值也为非零。
返回值记录在
?
中,且
?中,且
?中,且?只记录刚刚执行过命令的返回值。因为$?的值会被新执行命令的返回值覆盖。
先执行一个 xxx命令,命令如下。
[root@rh1 yy]# xxx
bash: xxx: 未找到命令...
[root@rh1 yy]# echo $?
127
[root@rh1 yy]# echo $?
0
先执行一个xxx命令,这个命令是错误的命令, ? 记录的是刚刚执行过 x x x 命令的返回值。所以,查看 ?记录的是刚刚执行过xxx命令的返回值。所以,查看 ?记录的是刚刚执行过xxx命令的返回值。所以,查看?的值是127,是一个非零的值。再次查看 ? 的值时,却变成了 0 ,因为这个 ?的值时,却变成了0,因为这个 ?的值时,却变成了0,因为这个?记录的不再是xxx命令的返回值,而是它前面执行过的echo $?命令的返回值。
逻辑上“否定”的意思也是可以体现出来的。例如,下面的例子。
[root@rh1 yy]# grep ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@rh1 yy]# echo $?
0
这里在/etc/passwd过滤行开头为root的行,结果找到了,所以返回值为0。
[root@rh1 yy]# grep ^rootxxxxxx /etc/passwd
[root@rh1 yy]# echo $?
1
里在/etc/passwd过滤行开头为rootxxxxxxxx的行,结果没有找到,即使语法没有错误,但是逻辑上有“否定”的意思,所以返回值为非零。
2.4数值运算
在写脚本时,有时我们经常要做一些数学运算。数学运算的符号如下。
(1)+:表示加。
(2)-:表示减。
(3)*:表示乘。
(4)/:表示除。
(5)**:表示次方。
进行数学运算的表达式有
(
(
)
)
、
(())、
(())、[]、let等,命令如下。
[root@rh1 yy]# echo $((2+3))
5
[root@rh1 yy]# echo $((2*3))
6
[root@rh1 yy]# echo $((2**3))
8
[root@rh1 yy]# echo $[2**3]
其中 ( O ) 和 (O)和 (O)和[]的用法是一样的,如果不用这样的表达式,看如下代码。
[root@rh1 yy]# echo 2**3
2**3
这里并不是计算的2的3次方,而是直接把这4个字符打印出来了。
let也可以用于数学运算,命令如下。
[root@rh1 yy]# let aa=1+2
[root@rh1 yy]# echo $aa
3
这里aa的值就是为3。
下面来看不使用let的情况,命令如下。
[root@rh1 yy]# aa=1+2
[root@rh1 yy]# echo $aa
1+2
这里并没有把aa的值1+2当成数字,而是当成了3个字符:“1”“+”“2”,所以结果显示的也是1+2。
可以实现定义aa为整数类型,然后再做数学运算,命令如下。
[root@rh1 yy]# declare -i aa
[root@rh1 yy]# aa=1+2
[root@rh1 yy]# echo $aa
3
首先declare -i aa把aa定义为一个整数,所以1+2等于3,然后赋值给aa.所以aa的值为3。
以上表达式不能求得小数,如果要得到小数需要使用 bc 命令,用法如下。
echo "scale=N ; 算法 | bc"
这里N是一个数字,表示小数点后面保留几位。
计算2/3,小数点后面保留3位,命令如下。
[root@rh1 yy]# echo "scale=3 ; 2/3" | bc
.666
这里得到的结果是0.666,整数部分的0没有显示。
计算7/6,小数点后面保留3位,命令如下。
[root@rh1 yy]# echo "scale=3 ; 7/6" | bc
1.166