前言
SHELL脚本是一种用来自动化执行操作系统命令和任务的脚本语言。它们被广泛用于Linux和其他类Unix操作系统中,可以编写一系列命令和逻辑来完成特定的任务。
以下是SHELL脚本的一些基本特点:
1. 解释性语言:SHELL脚本不需要编译,它们被解释器逐行解释和执行。
2. 命令行界面:SHELL脚本通常在命令行界面中运行,可以直接在终端中执行或者通过脚本文件执行。
3. 可编程性:SHELL脚本支持变量、条件语句、循环和函数等基本的编程结构,使得编写复杂的逻辑和控制流程成为可能。
4. 系统命令的调用:SHELL脚本可以调用系统自带的命令、工具和程序,利用其功能来完成各种任务。
5. 文件处理:SHELL脚本可以读取、写入和处理文本文件,进行文件和目录操作,包括复制、移动、删除等。
6. 自定义脚本和函数:SHELL脚本可以定义自己的函数和脚本,方便代码的复用和模块化。
7. 条件和循环控制:SHELL脚本支持条件判断和循环结构,可以根据条件执行不同的操作,并重复执行一系列命令。
8. 环境变量:SHELL脚本可以设置和使用环境变量,这些变量可以保存程序运行时的状态信息和配置参数。
SHELL脚本可以用于各种用途,比如自动化任务、批量处理、系统管理和配置等。它们提供了一种简单而强大的方式来管理和控制计算机系统。
一、概述
脚本就是将手动一次性执行的命令进行规范且自动化。
方向:
1、表达式
变量: 1、预定义变量 2、位置变量 3、自定义变量 |
运算符: 1、数学运算 2、数值比较 3、字符串比较 4、文件判断 5、布尔运算符 |
2、语句
条件语句 | if |
分支语句 | case |
循环语句 | for while |
3、函数
4、正则表达式
标准正则
扩展正则
5、文件操作四剑客
find
egrep
sed
awk
二、SHELL与内核、硬件、应用之间的关系
SHELL(Shell也是指这个软件)是一种在操作系统中提供用户与操作系统内核进行交互的命令行解释器。它是用户与操作系统之间的接口,负责解析和执行用户输入的命令,并与内核和硬件进行通信来完成相应的操作。
具体而言,SHELL与内核、硬件和应用程序之间的关系如下:
1. 内核:内核是操作系统的核心部分,负责管理计算机的资源和处理底层任务,如进程管理、内存管理、文件系统管理等。SHELL通过与内核进行交互,向其发送用户输入的命令,并将输出结果显示给用户。
2. 硬件:硬件是指计算机的实际物理设备,如处理器、内存、硬盘、网络接口等。SHELL通过与内核通信,向硬件发送指令和请求,以执行相应的操作。例如,通过SHELL可以发送网络请求、读写文件,或者与其他外设进行交互。
3. 应用程序:应用程序是在操作系统上运行的软件。SHELL可以与应用程序进行交互,启动和关闭应用程序,传递命令行参数和输入数据给应用程序,以及从应用程序获得输出结果。
在这个关系中,SHELL充当了用户和操作系统之间的桥梁。它解释和执行用户输入的命令,并与内核和硬件交互来实现所需的操作。通过SHELL,用户可以操作计算机系统的各个方面,包括文件管理、进程控制、系统配置等。
三、SHELL的类型
类型 |
sh |
ash |
bsh |
csh |
bash |
tcsh |
dsh |
zsh |
1、查看系统中支持的shell
[root@localhost ~]# cat /etc/shells /bin/sh /bin/bash /usr/bin/sh /usr/bin/bash /bin/tcsh /bin/csh
2、查看系统中当前使用的shell
1. [root@localhost ~]# echo $SHELL 2. /bin/bash
四、变量
1、什么是变量
变量名是由变量名和变量值组成的,举个简单的例子:比如喝水用的杯子,我可以使用这个杯子装水喝,也可以用这个杯子装可乐、雪碧、牛奶等。在这里杯子属于变量名没有变化,杯子里的液体属于变量值发生了变化,类似下图a的值
[root@localhost ~]# a=1 [root@localhost ~]# echo $a 1 [root@localhost ~]# a=2 [root@localhost ~]# echo $a 2 [root@localhost ~]# a=3 [root@localhost ~]# echo $a 3
2、变量名的声明规范、方法:
声明规范 |
不能是数字或数字开头 以_或字母开头 变量名中不能包含特殊字符 |
声明方法 |
驼峰式: userName 双驼峰:UserName shell写法: user_name username USERNAME |
[root@localhost ~]# _a=1 [root@localhost ~]# Name=1 [root@localhost ~]# NumberInt=1 [root@localhost ~]# User_Name=1 [root@localhost ~]# NAME=1
3、在Linux系统中可用env查看所有的变量
自定义变量:varName=varValue等号两边不能有空格
数字 :var1=1
字符串:shell中可以不使用引号,当包含有空格时,需要使用引号
引号的用法:
1)不会引用变量值: 单引号 '
2)会应用变量值:双引号 "
3)引用命令结果:反撇号 `
$(命令)应用场景较多
[root@localhost ~]# name=hy ####单引号' ':单引号内的内容会被视为字面字符串,不会对其中的变量或命令进行解析,即不会引用它们的值。如: [root@localhost ~]# echo 'my name is $name' my name is $name ####双引号" ":双引号内的内容会被解析,并引用其中的变量和命令结果。如: [root@localhost ~]# echo "my name is $name" my name is hy ####反撇号``和$:这两个用法是等效的,用于引用命令的结果。如: [root@localhost ~]# echo `my name is $name` bash: my: 未找到命令... [root@localhost ~]#files_count=`ls | wc -l` [root@localhost ~]# mkdir test [root@localhost ~]# cd test [root@localhost test]# echo "当前文件夹中的文件数量为:${files_count}" 当前文件夹中的文件数量为:0
4、 位置变量
脚本后参数所在的位置$1... $9
5、预定义变量
$0 | 脚本本身的名称 |
$# | 脚本后参数的个数 |
$* | 脚本运行时参数的内容(整体输出) |
$@ |
脚本运行时参数的内容(逐个输出) |
$? |
脚本运行完毕后的返回值,默认情况:0成功、非0失败 |
###写一个测试脚本 #!/bin/bash #This is test echo "第一个参数$1" echo "第一个参数$2" echo "第一个参数$3" echo "脚本名称 $0" echo "执行的参数: $@" echo "执行数量 $#"
6、作用域
1) 默认变量只在当前shell下生效
打开子标签,这里a=666是没有生效的
2)若要在当前及其子shell下生效,需要声明为全局变量export
7、shell中的字符串:
赋值: str1=foodfornoting.gpg
string
1)获得字符串的长度 语法: ${#StringName} 案例: echo ${#str1} 输出结果:17 |
2)字符串取子串 语法: ${#StringName:position:lenght} 案例: echo ${str1:0:3} 输出结果:foo 注意:lenght没有定义时,一直取到字符串的结尾! |
3)字符串的截取 a)从左至右截取最后一个匹配字符串string之后的所有字符串 语法: ${StringName##*string} 案例: echo ${str1##*fo} 输出结果:rnoting.gpg b)从左至右截取第一个匹配字符串string之后的所有字符串 语法: ${StringName#*string} 案例: echo ${str1#*fo} 输出结果:odfornoting.gpg c)从右至左截取最后一个匹配字符串string之后的所有字符串 语法: ${StringName%%string*} 案例: echo ${str1%%o*} 输出结果:f d)从右至左截取第一个匹配字符串string之后的所有字符串 语法: ${StringName%string*} 案例: echo ${str%o*} 输出结果:foodforn 4)字符串的拼接 语法: StringName3=${StingName1}${StringName2} 案例: str1=Hello str2=,Jack! str3=${str1}${str2} echo ${str3} 输出结果: Hello,Jack! 5)字符串替换: 语法: ${StringName/OldString/NewString} 案例: str1=foodfornoting.gpg echo ${str1/oo/kk} 输出结果:fkkdfornoting.gpg |
[root@localhost ~]# str1=foodfornoting.gpg [root@localhost ~]# echo $str1 foodfornoting.gpg [root@localhost ~]# echo ${#str1} 17 [root@localhost ~]# echo ${str1:0:3} foo [root@localhost ~]# echo ${str1##*fo} rnoting.gpg [root@localhost ~]# echo ${str1#*fo} odfornoting.gpg [root@localhost ~]# echo ${str1%%o*} f [root@localhost ~]# echo ${str1%o*} foodforn [root@localhost ~]# str1=Hello [root@localhost ~]# str2=,Jack! [root@localhost ~]# str3=${str1}${str2} [root@localhost ~]# echo ${str3} Hello,Jack! [root@localhost ~]# str1=foodfornoting.gpg [root@localhost ~]# echo ${str1/oo/kk} fkkdfornoting.gpg
8、数学运算
+ - * / % 当* 作为乘号时需要加转义符\ |
运算方法 expr expr $a + $b $((a+b)) echo $((a+b)) $[a+b] echo $[a+b] 注意 shell不支持浮点数的显示 |
[root@localhost ~]# a=1 [root@localhost ~]# b=2 [root@localhost ~]# expr $a+$b 1+2 [root@localhost ~]# expr $a + $b 3 [root@localhost ~]# echo $((a+b)) 3 [root@localhost ~]# echo $[a+b] 3 [root@localhost ~]# expr $[ a / b ] 0 [root@localhost ~]# expr $[ a - b ] -1 [root@localhost ~]# expr $[ a * b ] 2 [root@localhost ~]# expr $[ a / b ] 0
9、比较运算
条件测试
[ $a -ne $b ] && echo OK | 条件 && 输出结果 条件为真输出 |
[ $a -ne $b ] || echo OK | 条件 || 输出结果 条件为假输出 |
[root@localhost ~]# a=1 [root@localhost ~]# b=2 [root@localhost ~]# [ $a -ne $b ] && echo OK OK [root@localhost ~]# [ $a -eq $b ] || echo OK OK ##1不等于2是真的,所以&&输出 ##1等于2是假的,所以||输出
数值比较
-eq 等于 -ne 不等于 -lt 小于 -le 小于等于 -gt 大于 -ge 大于等于
[root@localhost test]# a=10 [root@localhost test]# b=20 [root@localhost test]# [ $a -eq $b ] [root@localhost test]# echo $? 1 [root@localhost test]# [ $a -ne $b ] [root@localhost test]# echo $? 0 [root@localhost test]# [ $a -lt $b ] [root@localhost test]# echo $? 0 [root@localhost test]# [ $a -le $b ] [root@localhost test]# echo $? 0 [root@localhost test]# [ $a -gt $b ] [root@localhost test]# echo $? 1 [root@localhost test]# [ $a -ge $b ] [root@localhost test]# echo $? 1 #####0为成功、非0为失败
字符串比较
= | 字符串一致 |
!= | 字符串不一致 |
-z | 字符串为空 |
! -z | 字符串不为空 |
[root@localhost test]# str1=hello,word [root@localhost test]# str2=hell,john [root@localhost test]# [ $str1 = $str2 ] [root@localhost test]# echo $? 1 [root@localhost test]# [ $str1 != $str2 ] [root@localhost test]# echo $? 0 [root@localhost test]# [ -z $str1 ] [root@localhost test]# echo $? 1 [root@localhost test]# [ ! -z $str1 ] [root@localhost test]# echo $? 0
文件比较
-e 文件或目录是否存在 -f 是否为文件 -d 是否为目录 -r 判断文件是否可读 -w 判断文件是否可写
-x
判断文件是否可执行
[root@localhost test]# touch 1.txt [root@localhost test]# [ -e 1.txt ] [root@localhost test]# echo $? 0 [root@localhost test]# [ -f 1.txt ] [root@localhost test]# echo $? 0 [root@localhost test]# [ -d 1.txt ] [root@localhost test]# echo $? 1 [root@localhost test]# ll 1.txt -rw-r--r-- 1 root root 0 8月 18 11:45 1.txt [root@localhost test]# [ -r 1.txt ] [root@localhost test]# echo $? 0 [root@localhost test]# [ -w 1.txt ] [root@localhost test]# echo $? 0 [root@localhost test]# [ -x 1.txt ] [root@localhost test]# echo $? 1
10、逻辑运算符
逻辑运算符
&& | -a 并且,有假则假,全真为真 [ -r 111 -a -w 111 -a -x 111 ] [ -r 111 ] && [ -w 111 ] && [ -x 111 ] [ -x /root/file1 -a -d /root/file1 ] [ -x /root/file1 ] && [ -d /root/file1 ] |
|| | -o 或者,有真则真,全假为假 |
! | 取反 有真则假,有假则真 |
五、SHELL脚本规范
shell脚本规范
第一行 | #!/bin/bash |
第二行 | #脚本的说明 |
第三行 | 脚本正文 |
脚本运行规则
没有x权限 | bash 脚本所在路径/脚本文件 source 脚本所在路径/脚本文件 . 脚本所在路径/脚本文件 脚本存在cd 时,会切换到目标目录 |
有x权限 | ./脚本文件 脚本绝对路径/脚本文件 |
shell脚本运行追踪:bash -x 脚本所在路径/脚本文件
$[$RANDOM%100] | 返回100内随机数 |
seq 1 10 | 返回1到10 的连续数字 |
{1..10} | 返回1到10 的连续数字 |
seq 1.1 10.1 | 返回1.1 2.1 3.1.... 10.1 |
read -p "提示语" 变量名 | 读取键盘输入并赋值给变量名 |
六、语句
条件语句
单分支if | if [ ];then fi |
双分支if | if [ ];then else fi |
多分支if | if [ ];then elif [ ];then else fi |
#!/bin/bash #hy #if单分支用来进行简单的测试 read -p "你好,请问你的名字是?" c echo " 很荣幸认识你,$c" b=yes #定义变量 read -p "尊敬的$c,你好~请问你需要查看磁盘情况吗:yes/no " a #这里是一个交互的询问 a是一个变量 if [ "$a" = "$b" ];then #当输入的是yes就会查看磁盘信息 df else #其他情况 exit 0 #退出 fi
#!/bin/bash #hy #test2 ##定义变量 b=1 c=2 d=3 read -p "你需要查看什么信息?磁盘(1)、剩余内存(2)、ip(3)" a if [ "$a" = "$b" ];then #如果输入1就查看磁盘 df elif [ "$a" = "$c" ];then #如果输入的是2就查看剩余内存 free elif [ "$a" = "$d" ];then #如果输入的是3就查看ip地址 ip a else exit 0 #其他情况直接退出 fi
循环语句
for
for 条件(i in 值)|((i=1;i<=10;i++)) do 语句 done while i=1 while 条件 do 语句 let i++ done
#!/bin/bash #hy #for循环 for i in {1..10};do touch $i.txt #创建1~10.txt done
#!/bin/bash #hy #for循环 #for i in {1..10};do #touch $i.txt #done ###下面我使用这个脚本去删除我之前创建的1~10.txt i=1 while [ "$i" -lt "11" ] #当$i小于11时执行 do rm -r $i.txt #删除$i.txt echo "已删除:$i.txt" i=$(($i+1)) #每次$i递增1,并继续循环 done
#!/bin/bash #hy #case开关测试 b=$"`date`" c=$"`ip a`" read -p "您好,查询时间请按1;查询ip请按2;查询天气请按3;听笑话请按4;退出请按5" a case $a in 1)echo "北京时间$b" ;; 2)echo "ip地址$c" ;; 3)echo "当前气温30摄氏度,注意防晒。" ;; 4)echo " 笑话 " ;; 5)echo " 谢谢使用,祝您生活愉快~再见~" esac
七、文件四剑客
1、find
Find命令适用于查找文件、目录
find ./ -type f// 查找当前目录下的文件 find ./ -type f -mtime +30//查找当前目录下30天前创建的文件 find ./ -type f -mtime -1//查找当前目录下今天创建的文件 find ./ -type f -mtime -1|xargs rm -rf {} /;//查找当前目录下今天创建的文件,并删除 find ./ -type d //查找当前目录下的目录 find ./ -type d -mtime +30//查找当前目录下30天前创建的目录 find ./ -type d -mtime -1//查找当前目录下今天创建的目录 find ./ -type d -mtime -1|xargs rm -rf {} /;//查找当前目录下今天创建的目录,并删除 find ./ -name “*.txt” -type d -mtime -1 |xargs rm -rf {} /; //查找当前目录下所今天创建有.txt结尾的目录,并删除 find ./ -name “*.txt” -type d -mtime -1 -exec cp -r {} /tmp\; //查找当前目录下所今天创建有.txt结尾的目录,并拷贝到/tmp下
##-exec是将前面执行的结果调用,更建议使用。因为|xargs有些命令执行不了例如chmod修改权限等。
2、grep
Grep用于查找文件的内容
^表示是以什么开头grep “^root” /etc/passwd 查找/etc/passwd中以root开头的内容
$表示以什么结尾grep “bash$” /etc/passwd 查找/etc/passwd中以bash结尾的内容
过滤注释grep -v “#”
过滤空行grep -v “^$”
拓展:如何使用egrep来匹配ip?
创建一个带有ip的文件ip.txt
下面用egrep命令来筛选出真确的IP格式
egrep “[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$” ip.txt
解释:[0-9]表示0到9的随机数字;{1,3}表示3次;$表示结束
也可以使用
egrep '([0-9]{1,3}\.){3}[0-9]{1,3}$' ip.txt
解释:[0-9]表示0到9的随机数字;{1,3}表示3次;()表示将其视为一个整体。{3}执行3次
3、awk
AWK命令是用于查询文件的列
如果我想要打印一个用户的名字和对应的SHELL?
awk的常用格式
awk ‘{print $1}’ /etc/passwd
打印/etc/passwd的第一列,但是他只有一列所以会全部打出
awk -F: ‘{print $1,$NF}’ /etc/passwd
-F:去除掉:打印/etc/passwd的第一列和最后一列
awk -F: ‘{print $1,”:”$NF}’ /etc/passwd
-F:去除:打印第一列和最后一列并在两者间添加:
如何把本机的IP以 “- - -” 的形式打印出来?
1.ip -a查看IP信息
2.这里运用了大量的管道符来进行处理,不断的过滤前一条命令的处理结果
ip a |grep "192.168.115.129"|awk -Finet '{print $2}'|awk -F/24 '{print $1}'|awk -F. '{print $1"-"$2"-"$3"-"$4}'
3.那如何把主机名修改为此ip呢?
直接用``号调用命令的执行结果
hostnamectl set-hostname `ip a |grep "192.168.115.129"|awk -Finet '{print $2}'|awk -F/24 '{print $1}'|awk -F. '{print $1"-"$2"-"$3"-"$4}'`
4、sed
sed命令用于替换文件的内容
sed基本使用格式sed -i ‘s/ 原来的/要替换的/替换行数’ /文件名
sed -i ‘2s/192/8888/’ ip.txt
仅修改第二行的192
sed -i ‘s/192/8888/g’ ip.txt
把所有的192换成8888
把www.test.com换成www//test//com
sed -i 's/www.test.com/www\/\/test\/\/com/g' ip.txt
其中/\/\表示转意
八、测试
这里我写了一个检测系统状态的脚本
#!/bin/bash #qzh #用于监控系统的性能 cpu_use=$(top -bn1|grep "%Cpu(s)"|awk '{print $2+$4} '|awk '{print}'|awk -F. '{print $1}') free_use=$(free |grep "Mem"|awk '{print ($3/$2) * 100}'|awk -F. '{print $1}') df_use=$(df |awk 'NR==6 {print $5}'|awk -F% '{print $1}') #定义阈值 Cpu=80 Free=80 df=80 #### y="yes" read -p " 是否检测系统当前状况?yes/no " h if [ "$h" = "$y" ];then echo " 当前cpu使用率:" $cpu_use% echo " 当前磁盘使用率:" $df_use% echo " 当前内存使用情况:" $free_use% else echo " 再见~ " exit 0 fi ### if [ "$cpu_use" -gt "$Cpu" ];then echo " 警告: 当前CPU使用率过高,请注意!!! " fi if [ "$free_use" -gt "$Free" ];then echo " 警告:当前内存使用过高,请注意!!! " fi if [ "$df_use" -gt "$df" ];then echo " 警告:当前磁盘使用率过高,请注意!!! " fi while true do cpu_use=$(top -bn1|grep "%Cpu(s)"|awk '{print $2+$4} '|awk '{print}'|awk -F. '{print $1}') free_use=$(free |grep "Mem"|awk '{print ($3/$2) * 100}'|awk -F. '{print $1}') df_use=$(df |awk 'NR==6 {print $5}'|awk -F% '{print $1}') clear echo " 当前cpu使用率:" $cpu_use% echo " 当前磁盘使用率:" $df_use% echo " 当前内存使用情况:" $free_use% if [ "$cpu_use" -gt "$Cpu" ];then echo " 警告: 当前CPU使用率过高,请注意!!! " fi if [ "$free_use" -gt "$Free" ];then echo " 警告:当前内存使用过高,请注意!!! " fi if [ "$df_use" -gt "$df" ];then echo " 警告:当前磁盘使用率过高,请注意!!! " sleep 5 fi done
运行后是这个界面
可以配合命令来检测
touch ~/4.txt && dd if=/dev/zero of=~/4.txt bs=1000M count=100
总结
这里我表达的并不全面,还有很多东西没有列举出来,主要是四剑客,要多多练习,共同进步!加油每一个你!