Linux学习(四)Shell编程
Shell概述
Shell是一个命令行解释器,它能接收应用程序/用户命令,然后调用操作系统内核。
同时Shell还是一门功能相当强大的编程语言,易编写、易调试、灵活性强。
-
Linux 提供的Shell解析器如下:
-
bash和sh的关系
-
Centos默认的解析器是bash
Shell脚本入门
- 脚本格式
脚本以#!/bin/bash开头(指定解析器)
- 第一个Shell脚本:helloworld.sh
需求:创建一个Shell脚本,输出helloworld
案例实操:
在helloworld.sh中第一行输入#!/bin/bash
,第二行输入echo "hello,world"
脚本的常用执行方式
第一种:采用bash或者sh加上脚本的绝对路径或者相对路径
使用命令 sh 脚本绝对路径
使用命令sh 脚本相对路径
使用命令bash 脚本绝对路径
使用命令bash 脚本相对路径
第二种:采用输入脚本的绝对路径或相对路径执行脚本(必须具有可执行权限+x)
使用相对路径
使用绝对路径
注意:第一种执行方法,本质上是bash解释器帮你执行脚本,所以脚本本身不需要执行权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限。
第三种:在脚本路径前加上" . " 或者 source
使用绝对路径
使用相对路径
前两种方式都是在当前 shell 中打开一个子 shell 来执行脚本内容,当脚本内容结束,则子 shell 关闭,回到父 shell 中。
第三种,也就是使用在脚本路径前加“.”或者 source 的方式,可以使脚本内容在当前shell 里执行,而无需打开子 shell!这也是为什么我们每次要修改完/etc/profile 文件以后,需要 source 一下的原因。
开子 shell 与不开子 shell 的区别就在于,环境变量的继承关系,如在子shell 中设置的当前变量,父 shell 是不可见的。
变量
变量存放在内存中,随着变量个数的增加所占用的内存就越大。可以使用unset 变量名
删除变量。
当终端关闭时,由于变量存放在内存中,当前终端下自定义变量也会被删除。
系统预定义变量
- 常用系统变量
$HOME、$PWD、$SHELL、$USER
等等
- 案例实操
使用命令echo $USER
查看系统变量的值
使用命令set
显示当前Shell中所有变量
使用命令env
查看当前Shell中的系统全局变量
使用命令printenv 环境变量名称
查看环境变量。如果不加环境变量名称效果和env一样。
自定义变量
- 基本语法
定义变量:变量名=变量值,注意:等号=前后不能有空格
撤销变量:unset 变量名
申明静态变量:readonly 变量,注意:不能unset
- 变量定义规则
1.变量名可以由字母、数字和下划线组成,但不能以数字开头,环境变量名建议大写。
2.等号两侧不能有空格
3.在bash中,变量默认类型都是字符串类型,无法直接进行数值运算
4.变量的值如果有空格,需要使用双引号或者单引号括起来
- 案例实操
1.定义变量A
2.给变量A重新赋值
3.撤销变量A
申明readonly变量B=2,不能被unset
在bash中,变量默认类型都是字符串类型,无法直接进行数值运算
变量的值如果有空格,需要使用双引号或单引号括起来
可以把局部变量提升为全局环境变量,可供其他Shell程序使用
在helloworld.sh文件中增加echo $B
将变量B提升为全局环境变量然后运行helloworld.sh文件
特殊变量
$n
基本语法
$n 功能描述,n为数字,$0代表该脚本的名称,$1-$9代表第一个到第九个参数,两位数以上的参数,需要用大括号括起来,比如 ${15}
表示第十五个参数。
案例实操
使用命令vim helloworld.sh
编辑脚本内容如下:
#!/bin/bash
echo "hello,wolrd"
echo $B
echo '#######$n的使用#######'
echo $0
echo $1
echo $2
执行脚本,查看结果
$#
基本语法
$# 可以获取输入参数的个数,擦汗给你用于循环,判断参数的个数是否正确以及加强脚本的健壮性。
案例实操
$*
和$@
基本语法
$* 这个变量代表命令行中所有的参数,它把所有变量看作一个整体
$@ 这个变量也是代表命令行中的所有的参数,不过它把每个参数分开对待。
案例实操
$?
基本语法
$? 是最后一次执行命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0,则证明上一条命令执行出错。
案例实操
运算符
基本语法
使用命令$((运算式))
或者$[运算式]
案例实操
计算(2+3)*4
条件判断
基本语法
使用命令test 条件
或者 [ 条件 ]
注意:1.中括号和条件之间一定要有空格!!!2.条件非空即为true ,比如[ itheima ]返回为true,[ ]返回为false
常用判断条件
- 两个整数之间的比较
参数 | 含义 |
---|---|
-eq | 等于 |
-lt | 小于 |
-gt | 大于 |
-ue | 不等于 |
-le | 小于等于 |
-ge | 大于等于 |
- 按照文件权限进行判断
参数 | 含义 |
---|---|
-r | 有读的权限 |
-w | 有写的权限 |
-x | 有执行的权限 |
- 按照文件类型进行判断
参数 | 含义 |
---|---|
-e | 文件存在 |
-f | 文件存在并且是一个常规的文件 |
-d | 文件存在并且是一个目录 |
案例实操
-
23是否大于等于22
-
helloworld.sh是否具有写权限
-
/home/atguigu/目录是否存在
-
多条件判断(短路与和短路或,可以实现类似java中的三元表达式)
流程控制(重点)
if判断
基本语法
1.单分支
if [ 条件判断式 ];then
程序
fi
或者
if [ 条件判断式 ]
then
程序
fi
2.多分枝
if [ 条件判断式 ]
then
程序
elif [ 条件判断式 ]
then
程序
else
程序
fi
案例实操
输入一个数字,如果是1,则输出:1! 5!;如果是2,则输出:L!T!C!;既不是1也不是2则输出:理塘猛虎!
if [ $1 -eq 1 ]
then
echo -e "1!\t5!"
elif [ $1 -eq 2 ]
then
echo -e "L!T!C!"
else
echo "理塘最强猛虎"
fi
case语句
基本语法
case $变量名 in
"值1")
如果变量的值等于值1,执行该部分
;;
"值2")
如果变量的值等于值2,执行该部分
;;
*)
如果变量的值都不是以上的值,则执行此部分
;;
esac
注意事项:
1.case行尾必须为单词in
,每一个模式匹配必须以右括号)
结束。
2.双分号::
表示命令序列化结束,相当于java中的break
3.最后的*)
表示默认情况,相当于java中的default
案例实操
输入一个数字,如果是1,则输出:1! 5!;如果是2,则输出:L!T!C!;既不是1也不是2则输出:理塘最强猛虎!
for循环
基本语法1
for (( 初始值;循环控制条件;变量变化 ))
do
程序
done
案例实操
从1加到100
sum=0
for ((i=1;i<=100;i++))
do
sum=$[$sum+$i]
done
echo $sum
基本语法2
for 变量 in 值1 值2 值3...
do
程序
done
案例实操
打印所有输入的参数
for i in $1 $2 $3
do
echo $i
done
比较$*
和$@
的区别
当它们(指$*
和$@
)不被双引号包括时:
两者没有区别。
当它们(指$*
和$@
)被双引号包括时:
$* 会将所有的参数作为一个整体,如果使用for循环来循环参数,只会执行一次。
$@会将每个参数当成是独立的个体,如果使用for循环,那么循环的次数会等于参数个数。
while循环
基本语法
while [ 条件判断式 ]
do
程序
done
案例实操
从1加到100
i=1
sum=0
while [ $i -le 100 ]
do
sum=$[$sum+$i];i=$[$i+1]
done
echo $sum
read读取控制台输入(附$()命令替换 )
基本语法
read (选项) (参数)
选项说明
选项 | 功能 |
---|---|
-p | 指定读取值时的提示符 |
-t | 指定读取值时等待的时间(秒),如果-t不加则一直等待 |
参数说明
指定读取值的变量名
案例实操
提示7秒内,读取控制台输入的名字
#!/bin/bash
read -t 7 -p "请输入你的姓名:" username
echo $username
- $()命令替换
echo $(echo 123)
函数
系统函数
- basename
基本语法
basename [string/pathname] [suffix]
功能:basename命令会删掉所有的前缀包括最后一个"/"字符,然后将字符串显示出来
可以理解为取路径里的文件名称。
选项说明
suffix为后缀,如果suffix被指定了,basename会将pathname或者string中的suffix去掉。
案例实操
截取/root/helloworld.sh路径中的文件名称
basename /root/read_test.sh
截取/root/helloworld.sh路径中的文件名称,同时截去文件类型
basename /root/read_test.sh .sh
- dirname
基本语法
使用命令dirname 文件绝对路径
从欸顶的包含绝对路径的文件名中除去文件名,然后返回剩下的路径
dirname 可以理解成取文件路径的绝对路径名称。
案例实操
获取helloworld.sh文件的路径
自定义函数
基本语法
[function] funname[()]
{
Action;
[return int;]
}
经验技巧
- 必须在调用函数之前,先声明函数,shell脚本是逐行运行。不会像其他语言那么先编译后运行。
- 函数返回值,只能通过$?系统变量获得,如果有return返回,则以return作为返回值,返回值必须为0~255中的一个数字。如果没有return,则以最后一条命令运行结果作为返回值。
案例实操
计算两个输入参数的值。
#!/bin/bash
function sum()
{
s=0
s=$[$1+$2]
echo "$s"
}
read -p "Please input the number1: " n1;
read -p "Please input the number2: " n2;
sum $n1 $n2;
正则表达式匹配
正则表达式使用单个字符串,匹配一系列符合某个语法规则的字符串。在很多文本编辑器里。正则表达式通常被用来检索,替换那些符合某个模式的文本。在Linux中,grep、sed、awk等文本处理工具都支持通过正则表达式进行模式匹配。
- 常规匹配
一串不包含特殊字符的正则表达式匹配它自己。例如
使用命令cat /etc/passwd | grep root
会匹配所有包含root的行
- 常用特殊字符
特殊字符 | 功能及例子 |
---|---|
^ | 匹配一行的开头,比如grep ^a 会匹配所有a开头的行 |
$ | 匹配一行的结束,例如grep a$ 会匹配所有a结束的行 |
. | 匹配一个任意的字符,例如grep r..t 可以匹配root,rbbt,ract等等 |
* | 星号不单独使用,他表示匹配一个字符串0次或多次,grep .* 表示一切 |
[ ] | 中括号表示匹配某个范围的一个字符,比如[1-5]表示匹配1到5中的一个数字 |
\ | 表示转义,比如有时我们想匹配星号,美元符号之类的可以使用\* 或者\$ |
{n} | 配合其他特殊字符使用,n表达字符的个数,使用grep需要添加参数-E |
注意:正则表达式是按行匹配!!!
案例实操
文本处理工具
cut
cut的功能就是在文件中负责剪切数据用。cut命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段输出。
- 基本用法
cut [选项参数] filename
说明:默认分隔符是制表符(tab键)
- 选项参数说明
选项参数 | 功能 |
---|---|
-f | 列号,提取第几列 |
-d | 分隔符,按照指定分隔符分割列,默认是制表符(tab) |
-c | 按字符进行切割 后面加n表示取第几列 比如-c 1 |
- 案例实操
数据准备
使用vim编辑器在名为cut.txt的文件中添加如下内容
wen zhe
zhou jiang
wo wo
lai lai
le le
切割cut.txt第一列
使用命令cut -f 1 -d " " cut.txt
切割cut.txt第二列,第三列
使用命令cut -f 2,3 -d " " cut.txt
在cut.txt文件中切割出zhou
使用命令cat cut.txt | grep zhou | cut -f 1 -d " "
选取系统PATH变量值,第2个":"开始后的路径
使用命令echo $PATH | cut -d ":" -f 3
awk
awk是一款强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。
- 基本用法
使用命令 awk [选项参数] '/pattern1/{action1} /pattern2/{action2}... ' filename
pattern:表示awk在数据中查找的内容,就是匹配模式(正则表达式)
action:在找到匹配内容时所执行的一系列命令
- 选项参数说明
选项参数 | 功能 |
---|---|
-F | 指定输入文件分隔符 |
-v | 赋值一个用户定义变量 |
- 案例实操
1.数据准备
使用命令cp /etc/passwd ./passwd
2.搜索passwd文件以root关键字开头的所有行,并输出该行的第7列。
使用命令awk -F ":" '/^root/{print $7}' passwd
3.搜索passwd文件以root关键字开头的所有行,并输出该行的第1列和第7列,中间以","号分割。
使用命令awk -F ":" '/^root/{print $1","$7}' passwd
注意!只有匹配了pattern的行才会执行action
4.只显示/etc/passwd的第1列和第7列,以逗号分隔,且在所有行前面添加列名user,shell在最后一行添加"dahaige,/bin/zuishuai"。
使用命令awk -F ":" 'BEGIN{print "user,shell"} {print $1","$7} END{print "dahaige,/bin/zuishuai"}' passwd
注意!BEGIN 在所在数据读取行之前执行;END在所在数据执行之后执行。
5.将passwd文件中的用户id增加数值1并输出
- awk的内置变量
变量 | 说明 |
---|---|
FILENAME | 文件名 |
NR | 已读的记录数(当前行号) |
NF | 浏览记录的域的个数 |
(1)统计 passwd 文件名,每行的行号,每行的列数
[atguigu@hadoop101 shells]$ awk -F : '{print "filename:" FILENAME ",linenum:"NR ",col:"NF}' passwd
filename:passwd,linenum:1,col:7
filename:passwd,linenum:2,col:7
filename:passwd,linenum:3,col:7
(2)查询 ifconfig 命令输出结果中的空行所在的行号
[atguigu@hadoop101 shells]$ ifconfig | awk '/^$/{print NR}'
9
18
26
(3)切割 IP
[atguigu@hadoop101 shells]$ ifconfig ens33 | awk '/netmask/ {print $2}'
192.168.6.101