shell脚本编程
一、shell简介
1.1 shell介绍
Shell是一种脚本语言,又是一种命令语言。可以通俗一点来讲,Shell脚本就是一系列命令的集合,可以在Unix/linux上面直接使用,并且直接调用大量系统内部的功能来解释执行程序把一些重复性工作交给shell做,来实现自动化运维
Shell 虽然没有C/C++、Java、Python等强大,但也支持了基本的编程元素。例如:if、for、while等循环,还有变量、数组、字符串、注释、加减乘除逻辑运算等
1.2 shell的应用场景
应用场景:
Shell 主要用来开发一些实用的、自动化的小工具,而不是用来开发具有复杂业务逻辑的中大型软件
- 第一方面:监控linux系统环境的使用情况
- 第二方面:数据的处理。eg:日志的切割、分析、统计等
- 第三方面:与数据库的交互,对数据库进行增,删,改,查等操作
- 第四方面:监控进程,自动化启停服务进程
- 第五方面:完成一些重复性的工作。eg:创建100个新用户;到服务器集群配置某个文件等
1.3 Linux的基础命令
-
cd 命令:
功能:切换目录
案例:切换到根目录 cd / -
ls命令
功能:查看目录信息
案例:查看etc目录下的文件信息 ls -lrt -
cat命令
功能:查看小文件内容
案例:查看test.txt内容 cat test.py -
chmod命令
功能:修改文件或目录权限
案例:修改test.txt 为自己可执行 chmod u+x test.txt -
chown命令
功能:改变目录拥有者或所属群组
案例:修改test.txt 属主为mysql chown mysql:mysql test.txt -
cp命令
功能:拷贝文件
案例::拷贝文件test.sh 为 test.sh_bak cp test.sh test.sh_bak -
diff命令
功能:对比文件差异
案例:对比文件test.sh test.sh_bak 是否有差异 diff test.sh test.sh_bak -
find命令
功能:查询文件
案例:查询本目录下面的test.txt find ./ -name test.txt -
mv命令
功能:移动命令
案例:移动test.txt文件目录 mv test.sh /bin/ -
rm命令
功能:删除命令
案例::删除文件test.sh rm test.sh -
touch命令
功能:创建一个空文件夹
案例:创建一个空的test.txt文件 touch test.txt -
which命令
功能:在环境变量$PATH设置的目录里查找符合条件的文件
案例:查询find命令在那个目录下面 which find -
ssh命令
功能:远程安全登录方式
案例:登录到远程主机:ssh ${IP} -
grep命令
功能:查找文件里符合条件的字符串
案例:从test.txt文件中查询test的内容 grep test test.txt -
wc命令
功能:统计行
案例:统计test.txt文件有多少行 wc -l test.txt -
data命令
功能:查询当前主机时间
案例:查询主机当前时间:date -
exit命令
功能:退出当前命令
案例:exit -
kiss命令
功能:杀进程
案例:杀掉test用户下面的所有进程:ps -ef | awk ‘$1==”test” {print $2}’ |xargs kill -9 -
id命令
功能:查看用户
案例:查看当前用户:id ;查询主机是否有test用户:id test -
ps命令
功能:查询进程情况
案例:查询test.sh进程:ps -ef | grep test.sh -
sleep命令
功能:休眠
案例:休眠60秒 :sleep 60 -
uname命令
功能:查询主机信息
案例:查询主机信息:uname -a -
passwd命令
功能:修改用户密码
案例:使用root修改test用户的密码:passwd test -
ping命令
功能:查看网络能不能连通
案例:查询本主机到远程IP的网络是否通:ping ${IP} -
df命令
功能:查看磁盘空间情况
案例:查看主机的空间使用情况 :df -h -
echo命令
功能:输出命令
案例:对变量test进行输出:echo $test -
pwd命令
功能:查看所在目录路径
案例:查询当前所在目录:pwd -
head命令
功能:查看文件前面行
案例:查看test.txt的前10行:head -10 test.txt -
tail命令
功能:查看文件后面行
案例:查看test.txt的后10行:tail -10 test.txt -
mkdir命令
功能:创建目录
案例:创建test目录:mkdir test
1.4 shell脚本编写步骤
- 建立shell文件
- 赋予shell文件可执行程序权限(使用chmod命令修改权限)
- 执行shell文件(直接运行赋予权限后的二进制文件)
二、shell脚本使用技巧
2.1 shell解释器
解释器:是一种命令解释器,主要作用是对命令进行运行和解释,将需要执行的操作传递给操作系统内核并执行
# !/bin/bash(默认)
# !/bin/ksh
# !/bin/bsh
# !/bin/sh
2.2 第一个shell脚本
- 创建一个shell.sh 文件
- 输入vi shell.sh 并输入以下内容且保存
#!/bin/bash
# 这是我的第一个shell脚本
echo 'this is my first shell !!!'
- 输入执行命令:sh shell.sh
2.3 shell脚本文件权限与脚本执行
文件权限:- rw- r-- r–
目录权限:d rw- r-- r–
分三列:每三个为一列,分别是所有者(owner),所属组(group),其他(others)
rwx r:4 w:2 x:1
7 5 5
执行方法
方法1:添加执行权限 chmod +x shell.sh
方法1:./shell.sh
方法2:sh shell.sh 或者bash shell.sh
方法3:source shell.sh
三、shell的变量
3.1 命名变量
在Shell脚本中,命名变量是一个简单的过程。变量名可以包含字母(a-z, A-Z)、数字(0-9)和下划线(_),但不能以数字开头。变量名是区分大小写的,因此myVar和myvar被视为两个不同的变量
myVar=“Hello World” #定义变量
count=1 #定义变量
echo $myVar #取变量值
echo $count #取变量值
3.2 常见变量
$? #判断上一条命令执行的是否成功
$0 #返回脚本的文件名称
$1-$9 #返回对应的参数值
$* #返回所有的参数值是什么
$# #返回参数的个数和
案例:
#!/bin/bash
echo "脚本:$第一个参数是:0"
echo "第一个参数是:$1"
echo "第二个参数是:$一共有多少参数2"
echo "一共有多少参数:$#"
echo "这些参数是什么:$*"
3.3 引用变量
使用变量时,要在变量名前加上美元符号 ,或者使用 ,或者使用 ,或者使用{}来明确界定变量名的边界
#!/bin/bash
myVar="Hello World"
echo $myVar
echo ${myVar}
3.3 使用变量注意点
- 变量名不能包含空格或特殊字符:
如果你试图创建一个带有空格或特殊字符的变量名,将会导致语法错误 - 内置变量:
Shell也有许多预定义的内置变量,例如$?
表示上一个命令的退出状态,$$
表示当前shell的进程ID等 - 未设置的变量:
如果尝试引用一个未设置的变量,它将被当作空字符串处理。你可以使用参数扩展来提供默认值 - 只读变量:
使用readonly命令可以将变量设置为只读,这样之后就不能修改这个变量了 - 局部变量:
在函数内部,你可以使用local关键字来定义局部变量,这样它们就不会影响到全局环境 - 删除变量:
使用unset命令可以删除变量,这将从环境中移除该变量
#!/bin/bash
# 定义变量
greeting="Hello"
# 引用变量
echo $greeting # 输出: Hello
echo "${greeting}, World!" # 输出: Hello, World!
# 定义只读变量
readonly pi=3.14
# 尝试更改只读变量会导致错误
pi=3.14159 # 这一行会报错
# 定义局部变量
myFunction() {
local localVar="I exist only inside this function."
echo $localVar
}
myFunction # 输出: I exist only inside this function.
echo $localVar # 输出为空,因为localVar是局部变量
# 删除变量
unset greeting
echo $greeting # 没有输出,因为变量已经被删除
四、shell符号
1. > #会覆盖原有的内容
2. >> #不会覆盖原有的内容
3. ; #执行多条命令
4. | #管道符
5. && #前面的命令执行成功,后面的才可以执行
6. || #前面的命令执行失败,后面的才可以执行
7. "" #会输出变量值
8. '' #输出本身
9. `` #输出命令结果 eg:a=`date`;echo $a
10. 2>/dev/null #错误输出到无底洞
11. 1>/dev/null #正确输出到无底洞
五、shell运算符
5.1 算术运算符
用于执行数学计算。需要注意的是,在Bash中直接使用这些运算符时需要将表达式放在双括号((…))或命令如expr、$[…]、let中
+:加法
-:减法
*:乘法
/:除法(整数除法)
%:取模(求余)
**:幂运算(Bash 3.0+)
# 使用双括号
result=$((5 + 3))
echo $result # 输出8
# 使用 expr
result=$(expr 5 + 3)
echo $result # 输出8
# 使用 let
let result=5+3
echo $result # 输出8
# 使用 $[]
result=$[5 + 3]
echo $result # 输出8
5.2 关系运算符
用于比较两个数值。返回的结果是0(真)或1(假)。注意,在条件语句中,0通常代表真,非零值代表假
-eq:等于
-ne:不等于
-lt:小于
-le:小于等于
-gt:大于
-ge:大于等于
if [ 5 -eq 5 ]; then
echo "Equal"
fi
if [ 5 -gt 3 ]; then
echo "Greater"
fi
5.3 布尔运算符
用于逻辑判断
&&:逻辑与
||:逻辑或
!:逻辑非
if [ "$a" -gt 0 ] && [ "$b" -lt 10 ]; then
echo "Both conditions are true."
fi
if [ "$a" -gt 0 ] || [ "$b" -lt 10 ]; then
echo "At least one condition is true."
fi
if ! [ "$a" -eq 0 ]; then
echo "a is not equal to 0."
fi
5.4 文件测试运算符
用于检查文件的状态
-e FILE:文件存在
-f FILE:文件存在且为普通文件
-d DIR:目录存在
-r FILE:文件可读
-w FILE:文件可写
-x FILE:文件可执行
-s FILE:文件存在且大小不为0
if [ -f "example.txt" ]; then
echo "File exists and is a regular file."
fi
if [ -d "mydir" ]; then
echo "Directory exists."
fi
5.5 字符串运算符
用于处理和比较字符串
=:字符串相等
!=:字符串不相等
-z STRING:字符串长度为0(空)
-n STRING:字符串长度非0(非空)
str="hello"
if [ "$str" = "hello" ]; then
echo "Strings are equal."
fi
if [ -n "$str" ]; then
echo "String is not empty."
fi
六、shell脚本输入和输出
6.1 read 内置命令
read命令可以从标准输入读取一行或多行,并将它们存储到变量中
- -p 提示符:显示提示信息
- -s 隐藏输入:适用于密码输入
- -t 超时:指定等待输入的时间(秒)
- -n 字符数:指定读取的字符数量
- -r 禁用反斜杠转义:防止反斜杠被解释为转义字符
#!/bin/bash
read -p "Enter your name: " name
echo "Hello, $name!"
6.2 文件作为输入
可以从文件中读取数据,通常使用cat, while read line循环,或者重定向符号<
#!/bin/bash
while IFS= read -r line; do
echo "$line"
done < input.txt
6.3 输出
echo 和 printf
-
这是两种常用的输出命令
-
echo:简单地打印文本到标准输出
-
printf:提供更灵活的格式化输出,类似于C语言中的printf函数
#!/bin/bash
echo "Hello, World!"
printf "Hello, %s!\n" "World"
七、shell脚本输出上色
语法;echo -e "\033[字背景颜色;字体颜色;特效字符串\033[关闭属性"
#字体色范围:30-37
echo -e "\033[30m 黑色字 \033[0m"
echo -e "\033[31m 红色字 \033[0m"
echo -e "\033[32m 绿色字 \033[0m"
echo -e "\033[33m 黄色字 \033[0m"
echo -e "\033[34m 蓝色字 \033[0m"
echo -e "\033[35m 紫色字 \033[0m"
echo -e "\033[36m 天蓝字 \033[0m"
echo -e "\033[37m 白色字 \033[0m"
#字背景颜色范围:40-47
echo -e "\033[40;37m 黑底白字 \033[0m"
echo -e "\033[41;30m 红底黑字 \033[0m"
echo -e "\033[42;34m 绿底蓝字 \033[0m"
echo -e "\033[43;34m 黄底蓝字 \033[0m"
echo -e "\033[44;30m 蓝底黑字 \033[0m"
echo -e "\033[45;30m 紫底黑字 \033[0m"
echo -e "\033[46;30m 天蓝底黑字 \033[0m"
echo -e "\033[47;34m 白底蓝字 \033[0m"
# 特效范围
echo -e "\033[0m 无任何特效 \033[0m"
echo -e "\033[1m 高亮度 \033[0m"
echo -e "\033[4m 下划线 \033[0m"
echo -e "\033[5m 闪烁 \033[0m"
八、 处理海量数据的grep、cut、awk、sed 命令
8.1 处理海量数据之grep命令
grep应用场景:通常对数据进行 行的提取
语法:grep [选项]…[内容]…[file]
-v #对内容进行取反提取
-n #对提取的内容显示行号
-w #精确匹配
-i #忽略大小写
^ #匹配开头行首
-E #正则匹配
假设我们有一个名为 example.log 的日志文件,内容如下:
[INFO] 2023-10-01 User login successful
[ERROR] 2023-10-01 Failed to connect to database
[WARNING] 2023-10-01 Low disk space
[INFO] 2023-10-02 System shutdown initiated
[ERROR] 2023-10-02 Database connection lost
[INFO] 2023-10-03 User logout successful
[WARNING] 2023-10-03 Disk cleanup initiated
提取包含特定单词的行(精确匹配)
grep -w "ERROR" example.log
#输出
[ERROR] 2023-10-01 Failed to connect to database
[ERROR] 2023-10-02 Database connection lost
忽略大小写并显示行号
grep -in "warning" example.log
#输出
3:[WARNING] 2023-10-01 Low disk space
7:[WARNING] 2023-10-03 Disk cleanup initiated
匹配以特定字符串开头的行
grep "^\$ERROR\$" example.log
#输出
[ERROR] 2023-10-01 Failed to connect to database
[ERROR] 2023-10-02 Database connection lost
使用正则表达式进行复杂匹配
grep -E "ERROR|WARNING" example.log
#输出
[ERROR] 2023-10-01 Failed to connect to database
[WARNING] 2023-10-
8.2 处理海量数据之cut命令
cut应用场景:通常对数据进行列的提取
语法:cut [选项]…[file]
- -d #指定分割符
- -f #指定截取区域
- -c #以字符为单位进行分割
注意:不加-d选项,默认为制表符,不是空格
-d 指定分割符
假设有一个CSV文件 data.csv,内容如下:
name,age,city
Alice,30,New York
Bob,24,Los Angeles
Charlie,29,Chicago
要以逗号为分隔符提取数据
cut -d',' -f1,3 data.csv
#输出
name,city
Alice,New York
Bob,Los Angeles
Charlie,Chicago
-f 指定截取区域
#继续使用上面的CSV文件,只提取第二列(年龄):
cut -d',' -f2 data.csv
#输出
age
30
24
29
# 提取第一和第三列:
cut -d',' -f1-3 data.csv
#输出
name,age,city
Alice,30,New York
Bob,24,Los Angeles
Charlie,29,Chicago
-c 以字符为单位进行分割
假设有一个文本文件 fixed_width.txt,每行有5个字符的名字后面跟着两位数的年龄
Alice 30
Bob 24
Charlie29
# 提取名字(前5个字符):
cut -c1-5 fixed_width.txt
#输出
Alice
Bob
Charl
# 提取年龄(第7到第8个字符)
cut -c7-8 fixed_width.txt
#输出
30
24
29
8.3 处理海量数据之awk命令
awk的应用场景:通常对数据进行列的提取
语法:
-
awk '条件 {执行动作}'文件名
-
awk ‘条件1 {执行动作} 条件2 {执行动作} …’ 文件名
-
或awk [选项] ‘条件1 {执行动作} 条件2 {执行动作} …’ 文件名
特殊要点与举例说明:
printf #格式化输出,不会自动换行。
(%ns:字符串型,n代表有多少个字符;
%ni:整型,n代表输出几个数字;
%.nf:浮点型,n代表的是小数点后有多少个小数
)
print #打印出内容,默认会自动换行
\t #制表符
\n #换行符
eg:df -h | grep /dev/sda2 | awk '{printf "/dev/sda2的使用率是:"} {print $5}
小数:echo "scale=2; 0.13 + 0.1" | bc | awk '{printf "%.2f\n", $0}'
$1 #代表第一列
$2 #代表第二列
$0 #代表一整行
eg: df -h | grep /dev/vda1 | awk '{print $5}'
-F #指定分割符
eg:cat /etc/passwd | awk -F":" '{print $1}'
BEGIN #在读取所有行内容前就开始执行,常常被用于修改内置变量的值
FS #BEGIN时定义分割符
eg:cat /etc/passwd | awk 'BEGIN {FS=":"} {print $1}'
END #结束的时候 执行
NR #行号
查看磁盘容量
查询/dev/sda2磁盘已用
假设我们有一个名为 data.csv 的CSV文件,内容如下:
name,age,city
Alice,30,New York
Bob,24,Los Angeles
Charlie,29,Chicago
场景 1: 提取特定列
提取所有行的第二列(年龄)
awk -F',' '{print $2}' data.csv
# 输出
age
30
24
29
解释:
-F’,’ 指定逗号作为字段分隔符。
{print $2} 表示打印每行的第二个字段
根据条件筛选行
只打印年龄大于25的行
awk -F',' '$2 > 25 {print $0}' data.csv
#输出
Alice,30,New York
Charlie,29,Chicago
$2 > 25 表示当第二列(年龄)大于25时执行后面的打印操作
{print $0} 表示打印整行
打印带有行号的输出
awk -F',' '{print NR ": " $0}' data.csv
#输出
1: name,age,city
2: Alice,30,New York
3: Bob,24,Los Angeles
4: Charlie,29,Chicago
计算年龄总和
awk -F',' 'NR > 1 {sum += $2} END {print "Total age:", sum}' data.csv
#输出
Total age: 83
NR > 1 确保跳过标题行
sum += $2 累加第二列(年龄)
END {print “Total age:”, sum} 在所有行处理完毕后输出总和
8.4 处理海量数据之sed命令
sed的应用场景:主要对数据进行处理(选取,新增,替换,删除,搜索)
sed语法:sed [选项] [动作] 文件名
常见的选项与参数:
-n #把匹配到的行输出打印到屏幕
p #以行为单位进行查询,通常与-n一起使用
eg:df -h | sed -n '2p'
d #删除
eg: sed '2d' df.txt
a #在行的下面插入新的内容
eg: sed '2a 1234567890' df.txt
i #在行的上面插入新的内容
eg: sed '2i 1234567890' df.txt
c #替换
eg: sed '2c 1234567890' df.txt
s/要被取代的内容/新的字符串/g #指定内容进行替换
eg: sed 's/0%/100%/g' df.txt
-i #对源文件进行修改(高危操作,慎用,用之前需要备份源文件)
九、shell流程控制
在Shell脚本中,流程控制结构是编写逻辑复杂、功能强大的脚本的关键。通过使用条件语句、循环结构和函数等,可以实现对程序执行流程的精细控制
9.1 if 循环控制
if 语句用于根据条件判断来执行不同的代码块。它支持单一条件、多条件以及嵌套条件
语法
if [ condition ]; then
# 当条件为真时执行的代码
elif [ another_condition ]; then
# 当前一个条件为假且此条件为真时执行的代码
else
# 当所有条件都为假时执行的代码
fi
多个判断(多分支循环):
if [条件判断];
then
执行动作
elif [条件判断];
then
执行动作
elif [条件判断];
then
执行动作
fi
案例
#!/bin/bash
read -p "Enter a number: " num
if [ "$num" -gt 10 ]; then
echo "Number is greater than 10."
elif [ "$num" -eq 10 ]; then
echo "Number is equal to 10."
else
echo "Number is less than 10."
fi
9.2 case循环控制
应用场景:case循环常使用于多重分支,与if不同的是,if可以判断多个条件,case一次只能判断一种条件
case $variable in
pattern1)
# 当变量匹配pattern1时执行的代码
;;
pattern2)
# 当变量匹配pattern2时执行的代码
;;
*)
# 默认情况下执行的代码(可选)
;;
esac
案例
#!/bin/bash
read -p "Enter a fruit name: " fruit
case $fruit in
apple|orange)
echo "You entered an orange or an apple."
;;
banana)
echo "You entered a banana."
;;
*)
echo "Unknown fruit."
;;
esac
9.3 for循环控制
for 循环用于遍历一系列值或文件列表
语法
for variable in list; do
# 执行的代码
done
案例
#!/bin/bash
for i in 1 2 3 4 5; do
echo "Number: $i"
done
或者遍历文件:
for file in *.txt; do
echo "Processing $file"
done
9.4 while 循环
应用场景:while循环是条件循环也是不定循环,只要条件判断式成立,循环就会一直进行着,直到判断式不成立 或者 选择跳出循环 才会结束
while [ condition ]; do
# 执行的代码
done
案例
#!/bin/bash
count=0
while [ $count -lt 5 ]; do
echo "Count: $count"
((count++))
done