3 shell脚本编程

Shell脚本简介

shell脚本是什么?

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


为什么要写它?

**1、自动话重复任务:**可以将重复性或复杂的任务自动化处理

如:批量重命名文件、定时备份数据、自动清理日志

2、简化管理操作 :简化管理操作,如定时执行、系统配置、监测等

3、提高效率 :节省手动执行命令的时间,特别是多步操作的时候

4、可重用性 :可以多个项目重用,只需简单修改

5、跨平台兼容:在Linux/Unix/macOS系统中通用(但需注意语法差异)


典型应用场景

1、日常运维

  • 定时备份数据库或文件(结合cron使用)。
  • 监控磁盘空间,自动清理旧文件。
  • 检查服务状态,异常时发送告警邮件。

2、开发辅助

  • 自动编译代码、运行测试用例。
  • 批量处理数据(如日志分析、格式转换)。

3、部署与配置

  • 一键安装软件(如Docker、Nginx)。
  • 初始化服务器环境(配置用户、防火墙等)。

4、数据处理

  • 结合grepawksed处理文本文件。
  • 提取日志中的错误信息并生成报告。

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号
复制代码
![](https://i-blog.csdnimg.cn/direct/39c0753f19da4022b54d468f71866d47.png)

## 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**"
相关推荐
Evand J2 小时前
【三维轨迹目标定位,CKF+RTS,MATLAB程序】基于CKF与RTS平滑的三维非线性目标跟踪(距离+方位角+俯仰角)
开发语言·matlab·目标跟踪
Lucis__2 小时前
一文读懂TCP通信机制:基于相关API构建可靠性连接
linux·网络·tcp/ip
_深海凉_2 小时前
LeetCode热题100-有效的括号
linux·算法·leetcode
今天又在写代码3 小时前
java-v2
java·开发语言
competes3 小时前
慈善基金投资底层逻辑应用 顶层代码低代码配置平台开发结构方式数据存储模块
java·开发语言·数据库·windows·sql
Ulyanov4 小时前
用Pyglet打造AI数字猎人:从零开始的Python游戏开发与强化学习实践
开发语言·人工智能·python
独自归家的兔4 小时前
OCPP 1.6 协议详解:StatusNotification 状态通知指令
开发语言·数据库·spring boot·物联网
希望永不加班4 小时前
Spring AOP 代理模式:CGLIB 与 JDK 动态代理区别
java·开发语言·后端·spring·代理模式
RNEA ESIO4 小时前
PHP进阶-在Ubuntu上搭建LAMP环境教程
开发语言·ubuntu·php