学会用bash在linux写脚本 (一)

本章主要介绍如何使用bash写脚本。

  • 了解通配符
  • 了解变量
  • 了解返回值和数值运算

grep的用法是"grep 关键字 file",意思是从file中过滤出含有关键字的行。
例如,grep root /var/log/messages,意思是从/var/log/messages 中过滤出含有root
的行。这里很明确的是过滤含有"root"的行。
如果想在/var/log/messages 中过滤出含有IP地址的行呢?IP地址就是一类字符,例如,
1.1.1.1是一个IP,192.168.26.100也是一个IP,那么用什么能表示出来这一类字符呢?
不管是通配符还是正则表达式,都是为了模糊匹配,为了匹配某一类内容,而不是具体的
某个关键字。通配符一般用在shell语言中,正则表达式一般用在其他语言中。
不管是通配符还是正则表达式,主要是理解它们的元字符,然后用元字符来组合成我们想
要的那一类字符,本章主要讲解通配符的使用。
像我们平时说的张某某,这个某就是一个元字符,不是一个定值。指的是姓张,名字含有
2个字。张某某可能匹配到张二狗,也可能匹配到张阿猫,但是无法匹配到李阿三,也匹配
不了张三,因为张某某匹配的是姓名为3个字的,但是张三这个姓名只有2个字。
如果说有一个人姓"张"名"某",那么需要匹配"张某"这个人,而不是要匹配张三、
张四,可以用张\某,某前加个"\"表示转义的意思。
22.1 通配符
通配符一般用在shell语言中,通配符中常见的元字符如下。
(1)\[\]:匹配一个字符,匹配的是出现在中括号中的字符。
(2)abc:匹配一个字符,且只能是a或b或c。
(3)a-z:"-"有特殊意义,表示"到"的意思,这里表示a~z,即匹配任一字母。
(4)0-9:表示匹配任一数字。
如果想去除含有特殊意义的字符,前面加"\"表示转义,即去除此字符的特殊意义。 (5)a\\-z:这里的"-"就没有"到"的意思了,匹配的是"a"或"-"或"z"这三个
中的一个。
如果想表示"除了"的意思,则在第一个中括号后面加"!"或"^"。
(6)!a-z\^a-z:表示除字母外的其他字符。
(7)?:表示一个任意字符,这里强调是一个,不是0个也不是多个,但不能匹配表示隐藏
文件的点。
(8)*:表示任意多个任意字符,可以是0个,也可以是1个或多个,但不能匹配表示隐藏
文件的点。
练习:先创建目录xx并在目录中创建如下几个测试文件,命令如下。

复制代码
[root@pp ~]# mkdir xx
[root@pp ~]# cd xx
[root@pp xx]# touch 1_aa aa11 Aa11 _aaa aa.txt f1aa u_12 flaa
[root@pp xx]# 

找出首字符是字母、第二个字符是数字的文件,命令如下。

复制代码
[root@pp xx]# ls [a-z][0-9]*
f1aa
[root@pp xx]# 

找出首字符是字母、第二个字符不是数字的文件,命令如下。

复制代码
[root@pp xx]# ls [a-z][^0-9]*
aa11  Aa11  aa.txt  flaa  u_12
[root@pp xx]# 

如果要更精确,可以用如下元字符。
(1)\[:upper:]:纯大写。
(2)\[:lower:]:小写。
(3)\[:alpha:]:字母。 (4)\[:alnum::字母和数字。
(5)\[:digit:]:数字。
列出首字符是小写字母、第二个字符是数字的文件,命令如下。

复制代码
[root@pp xx]# ls [[:lower:]][0-9]*
f1aa
[root@pp xx]# 

列出首字符是大写字母、第二个字符是数字或字母的文件,命令如下。

复制代码
[root@pp xx]# ls [[:upper:]][[:alnum:]]*
Aa11
[root@pp xx]# 

22.2 变量
所谓变量,指的是可变的值,并非具体的值。例如,我自己嘴中发出的"我",指的是我
自己,张三嘴中发出的"我",指的是张三,那么这个"我"就是一个变量。
变量可以分为本地变量、环境变量、位置变量和预定义变量。
22.2.1 本地变量
定义本地变量的格式如下。

复制代码
1 变量名=值

定义变量有以下几点需要注意。
(1)变量名可以包含_、数字、大小写字母,但不能以数字开头。
(2)"="两边不要有空格。
(3)"值"如果含有空格,要使用单引号''或双引号""引起来。
(4)定义变量时,变量名前是不需要加的,引用变量时需要在变量名前加
本章实验都放在~/yy中练习,命令如下。

复制代码
[root@pp ~]# mkdir yy ; cd yy
[root@pp yy]# 

下面开始练习定义变量,命令如下。

复制代码
[root@pp yy]# 1aa=123
bash: 1aa=123: 未找到命令..

这里定义变量不正确,因为变量名不能以数字开头,这里定义变量不正确,因为变量名不能以数字开头
这里正确地定义了一个变量。
在使用本地变量时,变量名前需要加$,命令如下。

复制代码
[root@pp yy]# aa=123
[root@pp yy]# echo $aa
123
[root@pp yy]# 

本地变量的特点是只能影响当前shell,不能影响子shell。

复制代码
[root@pp yy]# echo $aa
123
[root@pp yy]# echo $$
3070
[root@pp yy]# 

当前shell的PID是3070。下面打开一个子shell。

复制代码
[root@pp yy]# bash
[root@pp yy]# echo $$
3372
[root@pp yy]# 

这个子shell 的PID是3372。
可以看到,没有aa变量。

复制代码
[root@pp yy]# echo $aa

[root@pp yy]# exit
[root@pp yy]# echo $aa
123
[root@pp yy]# 

再次退回到原来的bash,又有了aa变量,情形如图

定义变量除刚才显式的定义外,还可以使用如下两
种方法。
方法1:把一个命令的结果赋值给一个变量,这个
变量要使用$()括起来,或者用反引号"引起来。这里是反引号,与波浪号~是同--个键,不是
单引号。
例如,定义一个名称是ip的变量,对应的值是ens160的IP,命令如下。

复制代码
[root@pp yy]# ip=$(ifconfig ens160 | awk '/inet /{print $2}')
[root@pp yy]# echo $ip
192.168.248.45
[root@pp yy]# 

方法2:通过read命令来获取变量。
read的用法如下。

复制代码
1 read ‐p "提示信息" 变量

当遇到read命令时,系统会等待用户输入,用户所输入的值会赋值给read后面的变量,
命令如下。

复制代码
[root@pp yy]# read -p "请输入你的名字" aa
请输入你的名字iu
[root@pp yy]# echo $aa
iu
[root@pp yy]# 

当执行read这条命令时,系统会提示用户输人一些内容,所输入的内容会赋值给aa变
量。这里我们输入的是 tom,所以打印aa变量时,看到的值是tom。
这样的用法比较适合写需要和用户交互的脚本。
22.2.2 环境变量
定义环境变量的注意点和本地变量是一样的。在定义环境变量时,前面加上export 即可,
命令如下。

复制代码
[root@pp yy]# export bb=123
[root@pp yy]# 

要想查看所有的环境变量,可以执行env命令。
环境变量的特点是可以影响子shell,这里强调的是子shell,不能影响父shell。

复制代码
[root@pp yy]# echo $$
3070
[root@pp yy]# echo $bb
123
[root@pp yy]# 

当前shell的PID是3828,里面有一个环境变量 bb。

复制代码
[root@pp yy]#  bash
[root@pp yy]# echo $$
3828
[root@pp yy]# echo $bb
123
[root@pp yy]# 

打开一个子shell,里面可以看到bb变量的值,说明环境变量已经影响到
子shell 了。
在子 shell中重新给bb赋值为456,然后退回到父shell。

复制代码
[root@pp yy]# export bb=456
[root@pp yy]# exit
exit
[root@pp yy]# echo $bb
123
[root@pp yy]# 

可以看到,在父shell 中,bb的值仍然是123,说明在子shell 中定义的变量不会影响到父
shell,如图22-2所示。

系统中默认已经存在很多个变量,如下所示。
(1)UID:表示当前用户的uid。
(2)USER:表示当前用户名。
(3)HOME:表示当前用户的家目录。 分别显示这些变量的值,命令如下。

复制代码
[root@pp yy]# echo $UID
0
[root@pp yy]# echo $USER
root
[root@pp yy]# 

22.2.3 位置变量和预定义变量
运行脚本时,有时后面是需要加上参数的。但是我们在写脚本时并不能预知后期在脚本后
面跟上什么参数,这时就能用到位置变量了,位置变量如下。
0:表示脚本的名称。 1:表示第1个参数。
2:表示第2个参数。 ...... {10}:表示第10个参数。
......
这里后面的数字如果不是个位数,则要用{}括起来。 系统中还内置了一些预定义变量。 #:表示参数的个数。
$*:表示所有的参数。
例1:写一个带参数的脚本,内容如下。

复制代码
[root@pp yy]# cat scl.yaml 
#/bin/bash
echo "这是我第一个脚本,脚本名称是 $0"
echo "第 1 个参数是:$1"
echo "第 2 个参数是:$2"
echo "第 3 个参数是:$3"
echo "此脚本一共有 $# 个参数,它们分别是:$*"
[root@pp yy]# 

给这个脚本加上可执行权限,并加参数运行,命令如下。

复制代码
[root@pp yy]# chmod +x scl.yaml 
[root@pp yy]# ./scl.yaml tom bob mary
这是我第一个脚本,脚本名称是 ./scl.yaml
第 1 个参数是:tom
第 2 个参数是:bob
第 3 个参数是:mary
此脚本一共有 3 个参数,它们分别是:tom bob mary
[root@pp yy]# 

运行这个脚本时,共指定了3个参数:tom、bob、mary,它们分别赋值给了
1、2、3。这里S#被自动赋值为3,因为总共有3个参数,所有的参数被赋值给*。
22.3 返回值
执行某命令之后,结果不是正确的就是错误的。命令正确执行了,返回值为0,如果没有
正确执行则返回值为非零。返回值为非零,不一定是语法错误,执行结果如果有"否定"的
意思,返回值也为非零。例如, ping 192.168.26.3,语法没有错误,但是没有ping通,返回值
也为非零。
返回值记录在?中,且?只记录刚刚执行过命令的返回值。因为$?的值会被新执行命令
的返回值覆盖。
练习:先执行一个 xxx命令,命令如下。

复制代码
[root@pp yy]# xxx
bash: xxx: 未找到命令...
[root@pp yy]# echo $?
127
[root@pp yy]# echo $?
0
[root@pp yy]# 

先执行一个xxx命令,这个命令是错误的命令,?记录的是刚刚执行过xxx命令的返回值。 所以,查看?的值是127,是一个非零的值。再次查看?的值时,却变成了0,因为这个?
记录的不再是xxx命令的返回值,而是它前面执行过的echo $?命令的返回值。 逻辑上"否定"的意思也是可以体现出来的。例如,下面的例子。

复制代码
[root@pp yy]# grep ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@pp yy]# echo $?
0
[root@pp yy]# 

这里在/etc/passwd过滤行开头为root的行,结果找到了,所以返回值为0。

复制代码
[root@pp yy]# grep ^rootxxx /etc/passwd
[root@pp yy]# echo $?
1
[root@pp yy]# 

这里在/etc/passwd过滤行开头为rootxxx的行,结果没有找到,即使语法没有错误,但
是逻辑上有"否定"的意思,所以返回值为非零。
22.4数值运算
在写脚本时,有时我们经常要做一些数学运算。数学运算的符号如下。
(1)+:表示加。
(2)-:表示减。
(3)*:表示乘。
(4)/:表示除。
(5)**:表示次方。
进行数学运算的表达式有(())、\[\]、let等,命令如下。

复制代码
[root@pp yy]# echo $((2+3))
5
[root@pp yy]# 

其中(O)和\[\]的用法是一样的,如果不用这样的表达式,看如下代码。

复制代码
[root@pp yy]# echo 2**3
2**3
[root@pp yy]# 

可以实现定义aa为整数类型,然后再做数学运算,命令如下。

复制代码
[root@pp yy]# declare -i aa
[root@pp yy]# aa=1+2
[root@pp yy]# echo $aa
3
[root@pp yy]# 

首先declare -i aa把aa定义为一个整数,所以1+2等于3,然后赋值给aa.所以aa的值为3。
以上表达式不能求得小数,如果要得到小数需要使用 bc 命令,用法如下。

复制代码
1 echo "scale=N ; 算法 | bc"

这里N是一个数字,表示小数点后面保留几位。
计算2/3,小数点后面保留3位,命令如下。

复制代码
[root@pp yy]# echo "scale=3 ; 2/3" | bc
.666
[root@pp yy]# 

这里得到的结果是0.666,整数部分的0没有显示。
计算7/6,小数点后面保留3位,命令如下。

复制代码
[root@pp yy]# echo "scale=3 ; 7/6" | bc
1.166
[root@pp yy]# 
相关推荐
A小辣椒20 小时前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式