Shell脚本简介
shell脚本是什么?
shell脚本是由 ++shell命令组成++ 的文本文件。利用shell命令加shell语法,配合正则表达式、管道命令、数据流从定向等写成的纯文本脚本文件。以**.sh**为后缀

为什么要写它?
**1、自动话重复任务:**可以将重复性或复杂的任务自动化处理
如:批量重命名文件、定时备份数据、自动清理日志
2、简化管理操作 :简化管理操作,如定时执行、系统配置、监测等
3、提高效率 :节省手动执行命令的时间,特别是多步操作的时候
4、可重用性 :可以多个项目重用,只需简单修改
5、跨平台兼容:在Linux/Unix/macOS系统中通用(但需注意语法差异)
典型应用场景
1、日常运维
- 定时备份数据库或文件(结合
cron使用)。 - 监控磁盘空间,自动清理旧文件。
- 检查服务状态,异常时发送告警邮件。
2、开发辅助
- 自动编译代码、运行测试用例。
- 批量处理数据(如日志分析、格式转换)。
3、部署与配置
- 一键安装软件(如Docker、Nginx)。
- 初始化服务器环境(配置用户、防火墙等)。
4、数据处理
- 结合
grep、awk、sed处理文本文件。 - 提取日志中的错误信息并生成报告。
shell编写
编写步骤:需求分析、命令测试、脚本编写、测试调优
bash
1 #!/bin/bash #指定解释器。标明使用的shell版本,跟自己用的版本一致,
路径等不能错,否者解释器错误
2
3 echo "hello world!" #打印
执行方式:解释性语言,不需要编译
bash
bash **.sh
source **.sh
./**.sh //要先赋权
Shell脚本变量
变量介绍
变量来源于数学,是计算机语言中能储存计算结果 或能表示值的抽象概念。 变量可以通过变量名访问。在指令式语言中,变量通常是可变的。
在一个脚本周期内,其值可以发生改变的量
变量的作用
存放特定参数:用来存放系统和用户需要使用的特定参数(值)
变量名:使用固定的名称,由系统预设或用户定义
变量值:能够根据用户设置、系统环境的变化而变化
命名要求

分类

变量操作
定义变量

通常使用全大写变量,方便识别
等号左右两侧不能有空格
bash
1 #!/bin/bash
2
3 COUNT=3 #变量创建
4 echo $COUNT
5
6 unset COUNT #变量取消
7 echo $COUNT
set //变量查看
shell中,变量本身只存储字符串,小数,整数都按字符串形式存储,所以对于小数运算需要借助外部工具(如bc、awk)
取值
变量的值如果有空格 ,需要使用单引号 或双引号 包括。如:test="hello world!"。
双引号 : 允许通过$符号引用其他变量值 。实现转义,其中的变量引用会被替换为变量值
单引号 : 单引号括起来的内容都是普通字符。不能转义,$被视为普通字符,其中的变量引用不会被替换为变量值,而保持源字符串
反撇号: 命令替换,提取命令执行后的输出结果(不是运算表达式的结果),` `和$(...)作用相同
bash
1 #!/bin/bash
2
3 COUNT=3
4 echo $COUNT
5
6 echo "$COUNT" #显示" "里运算的结果
7 echo '$COUNT' #原样显示' '里的内容
8
9 #A=echo $COUNT #错误写法
10 A=`echo $COUNT` #将命令执行的结果赋值给变量,这个命令要有确定值
11 B=$(echo $COUNT)
12 echo $A
13 echo $B
bash
[root@localhost ~]$ name=sc
#定义变量name 的值是sc
[root@localhost ~]$ echo '$name'
$name
#如果输出时使用单引号,则$name原封不动的输出
[root@localhost ~]$ echo "$name"
sc
#如果输出时使用双引号,则会输出变量name的值 sc
[root@localhost ~]$ echo `date`
2018年10月21日星期一18:16:33 CST
#反引号括起来的命令会正常执行
[root@localhost ~]$ echo '`date`'
`date`
#但是如果反引号命令被单引号括起来,那么这个命令不会执行,―date会被当成普通字符输出
[root@localhost ~]$ echo "`date`"
2018年10月21日星期一18:14:21 CST
#如果是双引号括起来,那么这个命令又会正常执行
wlw@wlw-virtual-machine:~/text$ echo '"`date`"'
"`date`"
wlw@wlw-virtual-machine:~/text$ echo "'`date`'"
'2026年 04月 17日 星期五 10:44:10 CST'
echo "Value: ${var}" # 用{}来明确变量边界
比如:
var="Hello"
varify="World"
echo "Value: ${var}ify" # 这表示引用 var 的值并加上 "ify"
使用 {} 来明确变量的边界,避免混淆
原文链接:https://blog.csdn.net/w918589859/article/details/108752592
变量值叠加
变量需要用双引号包含"变量名"或用{变量名}包含变量名。
cpp
[root@localhost ~]$ test=123
[root@localhost ~]$ test="$test"456
[root@localhost ~]$ echo $test
123456
#叠加变量test,变量值变成了123456
[root@localhost ~]$ test=${test}789
[root@localhost ~]$ echo $test
123456789
#再叠加变量test,变量值编程了123456789
数组
a=(元素1 元素2 元素3 ...) :创建数组,元素间用空格隔开
echo $a:默认显示数组a的第一个元素
echo ${a[0]} :显示数组中第一个元素,以此类推
echo ${a[-1]}: 显示数组中最后一个元素(a[-2]:倒数第二个)
echo {a\[\*\]} 和echo {a[@]} :显示数组中所有元素
echo ${#a[@]} :显示数组中元素的个数
echo ${a[@]:起始元素id:元素个数]} :显示数组中以起始元素为首的指定个数的元素(注意:这里起始元素id不能为负值
unset a[n] 删除数组中的第n个元素
unset a 删除a这个数组
bash
1 #!/bin/bash
2 #數組
3 a=(1 2 3 4 5) #创建
4 echo $a #默认取第一个元素
5 echo ${a[*]} #取所有元素
6 echo ${a[@]}
7 echo ${#a[*]} #元素个数
8 echo ${#a[@]}
9 echo ${a[2]} #取指定下标的元素
10 a[2]=8 #给指定下标赋值
11 echo ${a[*]}
12 echo ${a[@]:1:3} #取下标为1开始的三个元素
13 unset a[2] #删指定下标的元素
14 echo ${a[@]}
15 unset a #删数组
环境变量
使用export声明的变量即是环境变量

删除

环境变量查询
env与set
set可以查看所有变量,env只能查看环境变量

常用环境变量:
$USER 表示用户名称
$HOME 表示用户的宿主目录
$LANG 表示语言和字符集
$PWD 表示当前所在工作目录
$PATH 表示可执行用户程序的默认路径
只读变量
变量值不允许修改(重新赋值)的情况 , 无法使用 unset删除, 删除的最快方法是重启终端。
用readonly来定义
bash
1 #!/bin/bach
2
3 readonly COUNT=3
4 COUNT=5 #报错
5 echo $COUNT
6 unset COUNT #报错
位置变量和预定义变量
- $0 与键入的命令行一样,包含脚本文件名
- 1,2,......9 {10} 分别包含第一个到第十个命令行参数
- $# 包含命令行参数的个数
- @ 包含所有命令行参数:"1,2,......9"
- \* 包含所有命令行参数,是一个整体:"1,2,......9"
- $? 包含前一个命令的退出状态,正常退出,值为0,非正常退出,值为非0(1~255之间)导致退出的因素不同,值不同
-
包含正在执行进程的ID号
1 #!/bin/bash
2
3 echo 0 4 echo 1
5 echo 2 6 echo 3
7 echo 4 8 echo 5
9 echo 6 10 11 echo # #统计传入的参数
12 echo * #显示全部参数 13 echo @ #显示全部参数,实际上两者有区别
14 echo ? #上一条命令的退出状态,成功是0 15 echo $ #Pid号

## shell语句
Shell 程序由零或多条shell语句(也就是Shell命令)构成。 shell语句包括三类: **说明性语句 功能性语句 结构性语句。**
**说明性语句(注释行)**
以#号开始到该行结束,不被解释执行
### **功能性语句(命令)**
**标准输入:read**
read从标准输入读入一行,阻塞, 并赋值给后面的变量,其语法为:
**read var**
**read var1 var2 var3** //把读入行中的第一个单词(word)赋给var1, 第二个单词赋给var2, ......把其余所有的词赋给最后一个变量.。输入的多个数据由空格隔开
```bash
1 #!/bin/bash
2
3 read -p "提示语句:"val1 val2
4 echo $val1 $val2
-p " " #提示语句
-t 5 #5秒内不输入,自动结束,5内没有输入完(没按回车),也自动结束
-s #输入时不显示输入内容
算术运算命令:expr
主要用于进行简单的整数运算,包括加(+)、减(-)、乘(\*)、整除(/)和求模(%)等操作。
bash
1 #!/bin/bash
2
3 read val1 val2
4 echo $val1 $val2
5
6 ADD=`expr $val1 + $val2`
7 echo $ADD
8 ADD=`expr $val1 - $val2`
9 echo $ADD
10 ADD=`expr $val1 \* $val2`
11 echo $ADD
12 ADD=`expr $val1 / $val2`
13 echo $ADD
等号前后不能有空格,要夹``,运算符号前后要加空格
直接返回计算结果,在命令行中直接打印出来
测试三种对象test
字符串 整数 文件属性 每种测试对象都有若干测试操作符
字符串测试
s1 = s2 测试两个字符串的内容是否完全一样
s1 != s2 测试两个字符串的内容是否有差异
-z s1 测试s1 字符串的长度是否为0
-n s1 测试s1 字符串的长度是否不为0
test命令的退出状态码:
条件成立(正):test返回0
条件不成立(假):test返回非0(通常是1)
bash
1 #!/bin/bash
2
3 val1="hello"
4 val2="hello"
5 val3="world"
6 #成立为0,不成立为1
7 test $val1 = $val2
8 echo $? #0
9
10 test $val1 = $val3
11 echo $? #1
12
13 test $val1 != $val3
14 echo $? #0
15
16 val1=
17 test -z $val1
18 echo $? #0
19
20 val1="haha"
21 test -z $val1
22 echo $? #1
23
24 val1="haha"
25 test -n $val1
26 echo $? #0
27
28 val1=
29 test -n $val1
30 echo $? #0
31
32 val1="haha"
33 test -n "$val1" #-n要加" ",所有都最好加
34 echo $? #0
35
36 val1=
37 test -n "$val1"
38 echo $? #1
整数测试
a -eq b 测试a 与b 是否相等
a -ne b 测试a 与b 是否不相等
a -gt b 测试a 是否大于b
a -ge b 测试a 是否大于等于b
a -lt b 测试a 是否小于b
a -le b 测试a 是否小于等于b
文件测试
-e name 测试一个文件是否存在
-d name 测试name 是否为一个目录
-f name 测试name 是否为普通文件
-L name 测试name 是否为符号链接
-r name 测试name 文件是否存在且为可读
-w name 测试name 文件是否存在且为可写
-x name 测试name 文件是否存在且为可执行
-s name 测试name 文件是否存在且其长度不为0
f1 -nt f2 测试文件f1 是否比文件f2 更新
f1 -ot f2 测试文件f1 是否比文件f2 更旧
test命令测试的条件成立时, 命令返回值为(0),否则返回值为(1).

结构性语句
结构性语句主要根据程序的运行状态、输入数据、变量的取值、控制信号以及运行时间等因素来控制程序的运行流程。 主要包括: 条件测试语句(两路分支) 多路分支语句 循环语句 循环控制语句等
分枝语句
if 表达式
then 命令表 //条件成立
fi
if 表达式then //条件成立
。。。
else //条件不成立
。。。
fi
if 条件语句then
。。。
elif 条件语句
then
。。。
fi
表达式为真,执行命令表,假执行fi后语句。if-fi成对使用
if 语句判断的依据是所跟命令的退出状态码决定分支:
-
状态码为 0 → 条件为"真",执行
then后面的命令表。 -
状态码为 非 0 → 条件为"假",跳过
then部分(或执行else/elif部分)。 -
注意要和C的0为假,非0为真区别
bash
1 #!/bin/bash
2
3 read -p "input a filename: " file
4
5 #test -e $file
6 #echo $? #之前的判断法
7
8 #if test -e $file #这个形式也可以
9 if [ -e $file ] #[ ]与条件语句要有空格
10 then
11 echo "$file exist"
12 echo "***********"
13 echo "***********"
14 echo "***********"
15 else
16 echo "$file is not exist"
17 echo "***********"
18 echo "***********"
19 echo "***********"
20 fi
一个if对应一个then
bash
1 #!/bin/bash
2
3 read -p "input a filename: " file
4
5 if [ -f $file ]
6 then
7 echo "$file is a file"
8 elif [ -d $file ]
9 then
10 echo "$file is a dir"
11 fi
例:判断用户是否存在
用户信息保存在/etc/passwd下,
grep "用户内容" /etc/passwd //查找命令
grep "wlw" /etc/passwd

头一个是用户名,"^wlw"限定头一个

用户不存在就不会打印

通过判断是否有打印来确定用户是否存在
wc //统计命令
wc -l //统计行数
利用管道:grep "^wlw" /etc/passwd | wc -l //统计查找命令输出结果有几行
为1表示找到这个用户,为0表示没有这个用户

通过shell脚本实现:
bash
1 #!/bin/bash
2
3 read -p "input a ures: " U
4 RET=`grep "$U" /etc/passwd | wc -l`
5 if [ $RET -eq 1 ]
6 then
7 echo "exist"
8 else
9 echo "not exist"
10 fi
多路分枝语句
case....esac

模式匹配着字符串变量的可能取值
bash
1 #!/bin/bash
2
3 read -p "input yes or no: " val
4 case $val in
5 yes | y) #输入yes或y都执行同一个语句块
6 echo "input yes"
7 ;;
8 YES | Y)
9 echo "input YES"
10 ;;
11 no)
12 echo "input no"
13 esac
例:成绩分级
bash
1 #!/bin/bash
2
3 read -p "input score: " val
4
5 #if [ $val -gt 100 ] || [ $val -lt 0 ] #两种方式都可以
6 if [ $val -gt 100 -o $val -lt 0 ]
7 then
8 echo "input error val"
9 exit
10 fi
11
12 val=`expr $val / 10`
13 case $val in
14 8 | 9 | 10)
15 echo "A"
16 ;;
17 6 | 7)
18 echo "B"
19 ;;
20 *)
21 echo "C"
22 esac
-gt //大于 -lt //小于 -a 与 -o //或 !//非
循环语句
for的用法
for 变量 in 表 //变量依次从表里面拿值,拿一次循环一次,拿完为止
do
命令块
done
bash
1 #!/bin/bash
2
3 filename=`ls`
4 for file in $filename #所有文件组成的文件表
5 do
6 if [ -f $file ]
7 then
8 echo "$file is a file"
9 elif [ -d $file ]
10 then
11 echo "$file is a dir"
12 else
13 echo "*****"
14 fi
15 done
@ * //命令行传的所有参数表
bash
1 #!/bin/bash
2
3 for n in $@
4 do
5 echo "$n"
6 done

bash
1 #!/bin/bash
2
3 for n in $*
4 do
5 echo "$n"
6 done

bash
1 #!/bin/bash
2
3 for n in "$@"
4 do
5 echo "$n"
6 done

bash
1 #!/bin/bash
2
3 for n in "$*"
4 do
5 echo "$n"
6 done

@和*的区别:
"@"还是挨个参数表,"*"是一个整体
C语言形式
bash
1 #!/bin/bash
2
3 for ((i = 0; i < 5; i++))
4 do
5 echo "$i"
6 done

for里头echo命令会自动换行
while的用法
结构:
while 命令或表达式 //为真便执行命令表
do
命令表
done
bash
1 #!/bin/bash
2
3 i=0
4 while [ $i -lt 5 ] #方式1
5 do
6 echo "$i"
7 i=`expr $i + 1`
8 done
1 #!/bin/bash
2
3 i=0
4 while (( i < 5 )) #方式2
5 do
6 echo "$i"
7 i=`expr $i + 1`
8 done
循环控制语句
break和contine
break //跳出一层循环
break n //跳出n层循环
contine //跳过本次循环的语句去判断下一次循环
contine n //跳过本次以及往后的n-1次循环去判断后面的循环
Shell函数
定义:
function name() //关键字和函数名
{
代码块
return\] //可有可无 }
bash
1 #!/bin/bash
2
3 function fun()
4 {
5 echo "hello woed"
6 return 0
7 }
8
9 fun
函数调用
方式1:value_name=`function_name [arg1 arg2...]`
命令置换是把函数内的标准输出的内容赋给变量
方式2:function_name [arg1 arg2....]
echo $?
$?是获取函数返回值的状态
bash
1 #!/bin/bash
2
3 function fun()
4 {
5 echo "hello woed"
6 grep "wlw" /etc/passwd
7 return 0
8 }
9 fun

bash
1 #!/bin/bash
2
3 function fun()
4 {
5 echo "hello woed"
6 grep "wlw" /etc/passwd
7 return 0
8 }
9 rea=`fun`

bash
1 #!/bin/bash
2
3 function fun()
4 {
5 echo "hello woed"
6 grep "wlw" /etc/passwd
7 return 0
8 }
9 ret=`fun`
10 echo $ret

bash
1 #!/bin/bash
2
3 function fun()
4 {
5 echo "hello woed"
6 grep "wlw" /etc/passwd
7 return 0
8 }
9 fun
10 echo $?

bash
1 #!/bin/bash
2
3 function fun()
4 {
5 echo "hello woed"
6 grep "wlw" /etc/passwd
7 return 2
8 }
9 fun
10 echo $?

函数传参
bash
1 #!/bin/bash
2
3 function fun()
4 {
5 echo "hello woed"
6 grep "$1" /etc/passwd
7 echo $2
8 echo $3
9 return 2
10 }
11 fun wlw 5 9
12 echo $?

1 2 $3 就是参数替换的位置,函数调用后直接跟参数值就行
函数内变量的作用域
bash
1 #!/bin/bash
2
3 function fun()
4 {
5 val=10
6 echo $val
7 return 2
8 }
9 fun
10 echo "**$val**"

含数内的变量是全局变量,要想变局部变量加local修饰或采用命令替换调用


采用命令替换调用,那么val就不是全局变量了
bash
1 #!/bin/bash
2
3 function fun()
4 {
5 val=10
6 echo $val
7 return 2
8 }
9 ret=`fun` #采用命令替换调用,那么val就不是全局变量了
10 echo $ret
11 echo "**$val**"
