当前位置: 首页 > news >正文

Linux操作系统Shell脚本概述与命令实战

前言:
       1.Shell脚本是Linux系统管理和自动化任务的利器。
       2.系统梳理Shell脚本核心语法、高级技巧及实战案例,助读者快速提升脚本开发能力。

目录

一、Shell脚本语言的基本结构

1.Shell脚本的用途:

2. Shell脚本基本结构:

3.创建Shell脚本过程​编辑

4.脚本注释规范

5.第一个脚本​编辑

6.执行脚本

7.脚本错误

二、Shell脚本语言的变量用法详解

1.变量

2.变量类型

3.Shell中变量命名法则

4. 变量定义和引用

5.环境变量

6.只读变量

7.位置变量

8.退出状态码变量

9. 展开命令行

(1)展开命令执行顺序

(2)防止扩展

(3) 加引号来防止扩展

(4)变量扩展

三、 Shell字符串详解

1.Shell字符串拼接

2.Shell字符串截取

3.汇总

4.Shell的格式化输出printf

(1)语法格式:

(2)常用格式替换符:

(3)常用转义字符:

四、Shell脚本语言的运算

1.算数运算

2.Bash中的算术运算

(1)实现算术运算

(2)内建的随机数生成器变量:

(3)增强型赋值:

3.逻辑运算

4.短路运算

5.条件测试命令

条件测试命令及其语法

变量测试

文件测试表达式

字符串测试表达式

 整数测试表达式

逻辑操作符

总结


一、Shell脚本语言的基本结构

1.Shell脚本的用途:

自动化常用命令

执行系统管理和故障排除

创建简单的应用程序

处理文本或文件

2. Shell脚本基本结构:

Shell脚本编程:是基于过程式,解释执行的语言

编程语言的基本结构:

各种系统命令的组合

数据存储:变量,数组

表达式:a+b

控制语句:if、case、for、while

shell脚本:包含一些命令或声明,并符合一定格式的文本文件

 #声明后续语句是通过那种语言写的#!/bin/bash#!/usr/bin/python#!/usr/bin/perl

格式要求:首行执行shebang机制

3.创建Shell脚本过程

(1) 使用vim创建文本文件,第一行必须包括shell声明序列

#!/bin/bash

(2)加执行权限,给予执行权限,在命令行上指定脚本的绝对或相对路径

[root@ding ~]# chmod +x script1.sh

(3) 运行脚本,直接运行解释器,将脚本作为解释器程序的参数运行

[root@ding ~]# /root/shellScript/hello.sh 

4.脚本注释规范

  • 第一行一般为调用使用的语言

  • 程序名,避免更改文件名为无法找到正确的文件

  • 版本号

  • 更改后的时间

  • 作者相关信息

  • 该程序的作用,及注意事项

  • 最后是各版本的更新简要说明

5.第一个脚本

[root@ding ~]# chmod +x shell/hostname.sh
[root@ding ~]# shell/hostname.sh    #输出结果
My hostname is ding
Time is 2025-06-05 03:49:54

 6.执行脚本

(1)增加执行权限,执行脚本时会创建一个子shell,不影响现有的shell环境

 chmod +x sh./sh/root/shellScript/sh

(2)使用 . 或者source,执行脚本时不会创建一个子shell,会影响现有的shell环境

 source sh. sh
#注意:尽量不要使用该方式执行脚本!!!

企业级实例:备份脚本
 

[root@ding ~]# mkdir /backup/
[root@ding ~]# vim shell/backup.sh

[root@ding ~]# chmod +x shell/backup.sh
[root@ding ~]# ./shell/backup.sh
'/etc/' -> '/backup/etc2025-06-05/'
'/etc/dnf' -> '/backup/etc2025-06-05/dnf'
'/etc/dnf/aliases.d' -> '/backup/etc2025-06-05/dnf/aliases.d'
'/etc/dnf/dnf.conf' -> '/backup/etc2025-06-05/dnf/dnf.conf'
...
Backup is finished

7.脚本错误

语法错误,会导致后续的命令不继续执行,可以用bash -n shellname检查错误

命令错误,后续的命令还会继续,可以使用bash -x shellname检查

逻辑错误,只能使用bash -x进行观察

二、Shell脚本语言的变量用法详解

1.变量

变量表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据

2.变量类型

变量类型:

  • 内置变量:如PS1,PATH,UID,HOSTNAME,HISTSIZE

  • 用户自定义变量

  • 预定义变量

  • 位置变量

不同的变量存放的数据不同,决定了以下:

  1. 数据存储方式

  2. 参与的计算

  3. 表示的数据范围

变量数据类型:

  • 字符串

  • 数值:整型,浮点型(小数)、bash不支持浮点数

3.Shell中变量命名法则

  • 不能使用程序中的保留字,如:if,for

  • 只能使用数字,字母及下划线,且不能以数字开头

  • 见名思意,用英文名字,并体现真正含义

  • 统一命名规则:驼峰命名法

  • 全局变量名大写

  • 局部变量小写

  • 函数名小写

4. 变量定义和引用

变量的生效范围(变量作用域)

普通变量:生效范围为当前shell进程;对当前shell之外的其他shell进程,包括当前shell的子shell进程均无效
环境变量:生效范围为当前shell进程及其子进程
本地变量:生效范围为当前shell进程中某代码片段,通常指函数

变量赋值:

name="value"​value可以是以下多种类型直接字符串:name='root'变量引用:name="$USER"命令应用:name=`command`  || name=$(command)通配符:FILE=/etc/* /*表示etc目录下所有的文件名*/

注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存。

变量引用:

 $name${name}

弱引用和强引用:

“$name”:弱引用,其中的变量引用会被替换成为变量值

‘$name’:强引用,其中的变量引用不会被替换成变量值,而保持原字符串

实例:

[root@ding ~]# name='Mike'
[root@ding ~]# NAME="$USER"
[root@ding ~]# hostname=`hostname`
[root@ding ~]# echo "My name is $name"
My name is Mike
[root@ding ~]# echo "My name is $NAME"
My name is root
[root@ding ~]# echo "My hostname is $hostname"
My hostname is ding
[root@ding ~]# NUM=`seq 10`
[root@ding ~]# echo $NUM
1 2 3 4 5 6 7 8 9 10
[root@ding ~]# echo "$NUM"
1
2
3
4
5
6
7
8
9
10

查看已定义的所有变量:

[root@ding ~]# set

删除变量

[root@ding ~]# unset shellname1 shellname2
实战:
[root@ding ~]# echo $name
Mike
[root@ding ~]# unset name
[root@ding ~]# echo $name

实例:"{}"的使用

[root@ding ~]# NAME=mage
[root@ding ~]# AGE=20
[root@ding ~]# echo $NAME
mage
[root@ding ~]# echo $AGE
20
[root@ding ~]# echo $NAME $AGE
mage 20
[root@ding ~]# echo $NAME_$AGE
20
[root@ding ~]# echo ${NAME}_$AGE
mage_20

显示系统信息

[root@ding ~]# vim shell/OS.sh
[root@ding ~]# cat shell/OS.sh
#!/bin/bash
RED="\E[1;31m"
GREEB="\E[1;32m"
END="\E[0m"
echo -e "\E[1;32m----------Host systeminfo----------$END"
echo -e "HOSTNAME: $RED `hostname`$END"
echo -e "IPADDR:   $RED `ifconfig ens160 | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n1` $END"
echo -e "OSVERSION: $RED `cat /etc/redhat-release`$END"
echo -e "KERNEL:   $RED `uname -r`$END"
echo -e "CPU:       $RED `lscpu | grep "型号名称:" | tr -s ' ' ' '|cut -d ' ' -f 2-5` $END"
echo -e "MEMORY:   $RED `free -h | grep Mem | tr -s ' ' ' '|cut -d ' ' -f 4` $END"
echo -e "DISK:     $RED `lsblk | grep '^sda' | tr -s ' ' | cut -d ' ' -f 4` $END"
echo -e "\E[1;32m----------       END     ----------$END"
[root@ding ~]# chmod +x shell/OS.sh
[root@ding ~]# cd shell
[root@ding shell]# ./OS.sh
----------Host systeminfo----------
HOSTNAME:  ding
IPADDR:    192.168.72.8 
OSVERSION:  Rocky Linux release 8.10 (Green Obsidian)
KERNEL:    4.18.0-553.el8_10.x86_64
CPU:        AMD Ryzen 7 5800H 
MEMORY:    1.2Gi 
DISK:       
----------       END     ----------

利用变量实现动态命令

[root@ding ~]# CMD=hostname
[root@ding ~]# $CMD
ding

5.环境变量

环境变量:

可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量。

一旦子进程修改了从父进程继承的变量,将会传递新的值给孙子进程

一般只在配置文件中使用,在脚本中较少使用

普通变量生效的范围与环境变量生效的范围

[root@ding ~]# vim shell/father.sh
[root@ding ~]# cat shell/father.sh
#!/bin/bash
NAME=father
echo "father.sh:NAME=$NAME"
echo "fatther is PID=$BASHPID"
shell/son.sh[root@ding ~]# vim shell/son.sh
[root@ding ~]# cat shell/son.sh 
#!/bin/bash
echo "son.sh:NAME=$NAME"
NAME=son
echo "son.sh:NAME=$NAME"
echo "son.sh PID is $BASHPID"
echo "son.sh father pid is $PPID"
sleep 100
[root@ding ~]# chmod -R +x shell/*
[root@ding ~]# ./shell/father.sh
father.sh:NAME=father
fatther is PID=127349
son.sh:NAME=
son.sh:NAME=son
son.sh PID is 127350
son.sh father pid is 127349#子进程无法使用父进程的变量,需要自己赋值
 变量声明和赋值:
 export name=VALUEdeclare -x name =VALUE​#或者先赋值再声明~​value可以是以下多种类型直接字符串:name='root'变量引用:name="$USER"命令应用:name=`command`  || name=$(command)通配符:FILE=/etc/* /*表示etc目录下所有的文件名*/

declare命令详解

       declare 为 shell 指令,在第一种语法中可用来声明变量并设置变量的属性([rix]即为变量的属性),在第二种语法中可用来显示 shell 函数。若不加上任何参数,则会显示全部的 shell 变量与函数(与执行 set 指令的效果相同)

语法

 declare [+/-][rxi][变量名称=设置值] 或 declare -f

参数说明

  • +/-  "-“可用来指定变量的属性,”+"则是取消变量所设的属性。

  • -f   仅显示函数

  • r   将变量声明为只读变量。注意,一旦设置为只读变量,既不能修改变量的值也不能删除变量,甚至不能通过+r取消只 读属性

  • x   指定的变量会成为环境变量,可供shell以外的程序来使用

  • i   将变量声明为整数型(integer)

  • p 显示指定变量的被声明类型。

实例

1.声明整数型变量

 #!/bin/bashdeclare -i ab //声明整数型变量ab=56 //改变变量内容echo $ab //显示变量内容​--->56

改变变量属性

 #!/bin/bash#声明整数型变量declare -i ef ef=1echo $ef#变量赋值,赋予文本值ef="wer"echo $ef#取消变量属性declare +i efef="wer"echo $ef

3.设置变量只读

 #!/bin/bashdeclare -r ab #设置变量为只读ab=88 #改变变量内容echo $ab #显示变量内容​--->declare.sh:行3: ab: 只读变量

4.声明数组变量

 #!/bin/bash#声明数组变量declare -a cdcd[0]=acd[1]=bcd[2]=c#输出数组的指定内容echo ${cd[1]}#显示整个数组变量内容echo ${cd[@]}

5.显示函数

 #!/bin/bash#声明函数declare -f function command_not_found_handle(){if [ -x /usr/lib/command_not_found_handle ];then/usr/bin/python /usr/lib/command_not_found_handle -- $1;return $?;elseif [ -x /usr/share/command_not_found_handle ];then/usr/bin/python /usr/share/command_not_found_handle --$1;return $?;elsereturn 127;fi;fi;}

变量引用:

 $name${name}

完善(普通变量生效的范围与环境变量生效的范围)实战:

[root@ding ~]# vim shell/father.sh #!/bin/bashNAME=fatherexport NAMEecho "father.sh:NAME=$NAME"echo "fatther is PID=$BASHPID"shell/son.sh​
[root@ding ~]#vim shell/son.sh#!/bin/bashecho "son.sh:NAME=$NAME"NAME=sonecho "son.sh:NAME=$NAME"echo "son.sh PID is $BASHPID"echo "son.sh father pid is $PPID"sleep 100​
[root@ding ~]# chmod -R +x shell/*
[root@ding ~]# ./shell/father.sh
father.sh:NAME=father
fatther is PID=128429
son.sh:NAME=father
son.sh:NAME=son
son.sh PID is 128430
son.sh father pid is 128429
^C
[root@ding ~]# ./shell/father.sh
father.sh:NAME=father
fatther is PID=128457
son.sh:NAME=father
son.sh:NAME=son
son.sh PID is 128458
son.sh father pid is 128457
#父进程定义了一个环境变量,在子进程上可以进行调用
#父进程无法使用子进程的变量
#子进程自己定义了一个同名变量,就覆盖环境变量
 

显示所有环境变量:

 [root@ding ~]# env[root@ding ~]# printenv[root@ding ~]# export[root@ding ~]# declare -x

删除变量

 [root@ding ~]#unset shellname1 shellname2

Bash内建的环境变量

 PATHSHELLUSERUIDPWDSHLVL #shell的嵌套层数,即深度LANGMAILHOSTNAMEHISTSIZE_      #下划线,表示前一命令的最后一个参数

6.只读变量

只读变量:只能声明定义,但后续不能修改和删除

声明只读变量:

 readonly namedeclare -r name

查看只读变量:

 readonly [-p]declare -r

7.位置变量

位置变量:在Bash Shell中内置的变量,在脚本代码中调用命令行传递给脚本的参数

 $1,$2,... 对应第一个,第二个等参数,shift[n]换位置,最多9个#预定义变量$0  命令本身,包括路径$*  传递给脚本的所有参数,全部参数合成一个字符串$@  传递给脚本的所有参数,每个参数为独立字符串$#  传递给脚本的参数的个数$?  上个命令的退出状态,或函数的返回值$$  当前shell进程ID。对于Shell脚本,就是这些脚本所在的进程ID​注意:$@,$*只有被双引号括起来的时候才会有差异

清空所有位置变量

 set --//写在脚本内部

实战演示1:

[root@ding ~]# vim shell/ARG.sh
[root@ding ~]# cat shell/ARG.sh 
#!/bin/bash
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "4st arg is $4"
echo "The number of are is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@ding ~]# chmod +x shell/ARG.sh 
[root@ding ~]# shell/ARG.sh {1..10}
1st arg is 1
2st arg is 2
3st arg is 3
4st arg is 4
The number of are is 10
All args are 1 2 3 4 5 6 7 8 9 10
All args are 1 2 3 4 5 6 7 8 9 10
The scriptname is ARG.sh

实战演示2:编写一个移动文件脚本

 [root@ding ~]# vim shell/move.sh#!/bin/bashWANGING_COLOR="echo -e \E[1;31m"END="\E[0m"DIR=/tmp/`date +%F_%H-%M-%S`mkdir $DIRmv $*  $DIR${WANGING_COLOR} MOVE $* to $DIR $END[root@ding ~]# chmod +x shell/move.sh[root@ding ~]# touch {a,b,c}[root@ding ~]# lsa  anaconda-ks.cfg  b  c  shell[root@ding ~]# shell/move.sh a b cMOVE a b c to /tmp/2022-08-16_10-07-55 [root@ding ~]# tree /tmp//tmp/└── 2022-08-16_10-07-55├── a├── b└── c​1 directory, 3 files

8.退出状态码变量

进程执行后,将使用变量 ? 保存状态码的相关数字,不同的值反应成功与失败, ?保存状态码的相关数字,不同的值反应成功与失败, ?保存状态码的相关数字,不同的值反应成功与失败,的取值范围为[0,255]

 $?的值为0 代表成功$?的值不为0 代表失败

用户可以在脚本中使用以下命令自定义退出状态码

 exit [n]

实战:

[root@ding ~]# ping -c 2 www.baidu.com > /dev/null
[root@ding ~]# echo $?
1
[root@ding ~]# cmd
bash: cmd: 未找到命令...
相似命令是: 'mcd'
[root@ding ~]# echo $?
127

注意:

  • 脚本中一旦遇到了exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字

  • 如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

实例1:$?获取上一个命令的退出状态

 #!/bin/bashif [ "$1"==100 ];thenexit 0 #参数正确,退出状态为0elseexit 1 #参数错误,退出状态为1fi

exit表示退出当前 Shell 进程,我们必须在新进程中运行 test.sh,否则当前 Shell 会话(终端窗口)会被关闭,我们就无法取得它的退出状态了

 [root@localhost re_study]# bash test.sh 100[root@localhost re_study]# echo $?0[root@localhost re_study]# bash test.sh 99[root@localhost re_study]# echo $?1

实例2:$?获取函数的返回值

 #!/bin/bash#得到两个数相加的和function add(){return `expr $1 + $2`}add 23 50 #调用函数echo $? #获取函数返回值# 运行结果:[root@localhost re_study]# bash test.sh73

9. 展开命令行

(1)展开命令执行顺序
 把命令行分成单个命令词展开别名展开大括号的声明{}展开波浪符声明(~)命令替换$()和``再次把命令行分成命令词展开文件通配(*,?,[abc]等)准备I/O重导向(<,>)运行命令
(2)防止扩展
 反斜线(\)会使随后的字符按原意解释

实例:

 [root@localhost ~]# echo Your cost: \$5.00Your cost: $5.00[root@localhost ~]#
(3) 加引号来防止扩展
 单引号(' ')防止所有扩展双引号(" ")可防止扩展,但是以下清空例外:$(美元符号)
(4)变量扩展
 ``:反引号,命令替换\:反斜线,禁止单个字符扩展!:叹号,历史命令替换

三、 Shell字符串详解

字符串(String)就是一系列字符的组合。字符串是Shell编程中最常用的数据类型之一

字符串可以由单引号''包围,也可以由""包围,也可以不用引号,三种方式的区别

通过代码演示一下三种形式的区别

 #!/bin/bashn=74str1=c.biancheng.net$n str2="shell \"Script\" $n"str3='C语言中文网 $n'echo $str1echo $str2echo $str3​​# 运行结果c.biancheng.net74shell "Script" 74C语言中文网 $n

str1 中包含了$n,它被解析为变量 n 的引用。$n后边有空格,紧随空格的是 str2;Shell 将 str2 解释为一个新的变量名,而不是作为字符串 str1 的一部分

str2 中包含了引号,但是被转义了(由反斜杠\开头的表示转义字符)。str2 中也包含了$n,它也被解析为变量 n 的引用

str3 中也包含了$n,但是仅仅是作为普通字符,并没有解析为变量 n 的引用

获取字符串长度

在Shell中获取字符串长度很简单,具体方法如下:

 ${#string_name}string_name:表示字符串名字

1.Shell字符串拼接

在脚本语言中,字符串的拼接(也称为字符串连接或者字符串合并)往往都非常简单,例如:

  • PHP中使用.即可连接两个字符串

  • JavaScript中使用+即可将两个字符串合并为一个

然而,在Shell中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接

 #!/bin/bashname="shell"url="http://c.biancheng.net/shell/"str1=$name$url #中间不能有空格str1=$name":"$urlstr2="$name $url" #如果被双引号包围,那么中间可以有空格,也可以出现别的字符串str3="$name:$url" str4="${name}Script:${url}Index.html" #在变量后加上字符串,需要给变量名加上大括号

2.Shell字符串截取

Shell截取字符串通常有两种方式,从指定位置开始截取和从指定字符(子字符串)开始截取

从指定位置开始截取

这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串

既然需要指定起始位置,那么就要涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数?答案是:Shell同时支持两种计数方式

(1)从字符串左边开始计数

如果想从字符串的左边开始计数,那么截取字符串的具体格式如下:

${string:start:length}`其中,Sting是要截取的字符串,start是起始位置(从左边开始,从0开始计数),length是要截取的长度(省略的话表示直到字符串的末尾)

例如:

 url="c.biancheng.net"echo ${url:2:9}​>结果为:biancheng​url="c.biancheng.net"echo ${url:2} #省略length,截取到字符串末尾​>结果为:biancheng.net

(2)从右边开始计数

如果想从字符串的右边开始计数,那么截取字符串的具体格式如下:

 ${string:0-start:length}​`同第 1) 种格式相比,第 2) 种格式仅仅多了0-,这是固定的写法,专门用来表示从字符串右边开始计数。​这里需要强调两点:从左边开始计数时,起始数字是 0(这符合程序员思维);从右边开始计数时,起始数字是 1(这符合常人思维)计数方向不同,起始数字也不同。不管从哪边开始计数,截取方向都是从左到右。

例如:

 url="c.biancheng.net"echo ${url:0-13:9}​>结果为:biancheng 从右边数:b是第13个字符​url="c.biancheng.net"echo ${url:0-13} #省略length,直接截取到字符串末尾>结果为:biancheng.net

从指定字符(子字符串)开始截取

这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符

1)使用#号截取右边字符

使用#号可以截取指定字符(或子字符串)右边的所有字符,具体格式如下:

 ${string#*chars}​#其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取)

例如:

 url="http://c.biancheng.net/index.html"echo ${url#*:}​>结果为://c.biancheng.net/index.html​echo ${url#*p:}echo ${url#*ttp:}​`如果不需要忽略chars左边的字符,那么也可以不写*url="http://c.biancheng.net/index.html"echo ${url#http://}>结果为:c.biancheng.net/index.html​`注意:以上写法遇到第一个匹配的字符(子字符串)就结束了url="http://c.biancheng.net/index.html"echo ${url#*/}>结果为:/c.biancheng.net/index.html。url 字符串中有三个/,输出结果表明,Shell 遇到第一个/就匹配结束了

使用##可以直到最后一个指定字符(子字符串)再匹配结束

 ${string##*chars}

例如:

 #!/bin/bashurl="http://c.biancheng.net/index.html"echo ${url#*/}# 结果为:/c.biancheng.net/index.htmlecho ${url##*/}# 结果为:index.html​str="-----aa+++aa@@@"echo ${str#*aa}# 结果为:+++aa@@@echo ${str##*aa}# 结果为:@@@

2)使用%截取左边字符

使用%号可以截取指定字符(或者子字符串)左边的所有字符

 ${string%chars*}`请注意*的位置,因为要截取 chars 左边的字符,而忽略 chars 右边的字符,所以*应该位于 chars 的右侧。其他方面%和#的用法相同​#!/bin/bashurl="http://c.biancheng.net/index.html"echo ${url%/*}  #结果为 http://c.biancheng.netecho ${url%%/*}  #结果为 http:str="---aa+++aa@@@"echo ${str%aa*}  #结果为 ---aa+++echo ${str%%aa*}  #结果为 ---

3.汇总

格式说明
${string:start :length}从string字符串的左边第start个字符开始,向右截取length个字符。
${string:start}从string字符串的左边第start个字符开始截取,直到最后。
${string:0-start:length}从string字符串的右边第start个字符开始,向右截取length个字符。
${string:0-start}从string字符串的右边第start个字符开始截取,直到最后。
${string#*chars}从string字符串第一次出现chars的位置开始,截取chars右边的所有字符。
${string##*chars}从string字符串最后一次出现chars的位置开始,截取chars右边的所有字符。
${string%chars*}从string字符串第一次出现chars的位置开始,截取chars左边的所有字符。
${string%%chars*}从string字符串最后一次出现chars的位置开始,截取chars左边的所有字符。

4.Shell的格式化输出printf

(1)语法格式:
 printf "指定的格式" "文本1" "文本2" .....
(2)常用格式替换符:
替换符功能
%s字符串
%f浮点格式,保留小数点位数%.nf,n为数字
%d,%i十进制整数
%%表示%本身

说明:%s中的数字代表此替换符中的输出字符宽度,不足补空格,默认是右对齐,%-10s表示10个字符宽,-表示左对齐

(3)常用转义字符:
转义符功能
\a警告字符,通常为ASCII的BEL字符
\b后退
\f换页
\n换行
\r回车
\t水平制表符
\v垂直制表符
\\表示\本身

实例

 [root@localhost ~]# printf "%s\n" 1 2 3 41234[root@localhost ~]# printf "%f\n" 1 2 3 41.0000002.0000003.0000004.000000[root@localhost ~]# printf "%.2f\n" 1 2 3 4 #.2f表示保留两位小数1.002.003.004.00 [root@localhost ~]# printf "(%s)" 1 2 3 4;echo " "(1)(2)(3)(4) [root@localhost ~]# printf " (%s) " 1 2 3 4;echo " "(1)  (2)  (3)  (4)  [root@localhost ~]# printf " (%s) (%s)\n" 1 2 3 4;echo " "(1) (2)(3) (4)[root@localhost ~]# printf " %s %s\n" 1 2 3 4;echo " "1 23 4[root@localhost ~]# printf "%s %s %s\n" 1 2 3 41 2 34  [root@localhost ~]# ​#%-10s表示宽度10个字符,左对齐[root@localhost ~]# printf "%-10s %-10s %-4s %s \n" 姓名 性别 年龄 体重 小明 男性 20岁 70KG 小红 女性 18岁 50KG 姓名     性别     年龄 体重 小明     男性     20岁 70KG 小红     女性     18岁 50KG [root@localhost ~]# ​​#将十进制的1000转换为16进制数[root@localhost ~]# printf "%X\n" 10003E8[root@localhost ~]# printf "%x\n" 10003e8[root@localhost ~]# ​#将十六进制的C转换为十进制[root@localhost ~]# printf "%d\n" 0xc12[root@localhost ~]# ​​[root@localhost ~]# var="welcome to study";printf "\033[31m%s\033[0m\n" $varwelcometostudy[root@localhost ~]# var="welcome to study";printf "\033[31m%s\033[0m\n" "$var
 -- + 加法运算-- - 减法运算-- * 乘法运算-- / 除法运算-- % 取模,即取余数-- ** 乘方 ​#乘法符号在有些场景需要转义"welcome to study[root@localhost ~]#
 

四、Shell脚本语言的运算

1.算数运算

shell支持算术运算,但只支持整数,不支持小数

2.Bash中的算术运算

(1)实现算术运算
 1. let var=算术表达式2. var=$[算术表达式]3. var=$((算术表达式))4. var=$(expr arg1 arg2 arg3 ...)5. declare -i var = 数值6. echo '算术表达式' | bc  (支持浮点数)

实例:使用bc计算小数和declare -i计算

 [root@localhost ~]# echo "scale=3;20/3"|bc6.666[root@localhost ~]# echo "scale=3;2/3"|bc.666[root@localhost ~]# i=20[root@localhost ~]# j=20[root@localhost ~]# declare -i sum=i*j[root@localhost ~]# echo $sum400[root@localhost ~]# 
(2)内建的随机数生成器变量:
 $RANDOM  取值范围:0-32767

实例:生成0-49之间的随机数

 [root@localhost ~]# echo $[$[$RANDOM%50]+1]40[root@localhost ~]# echo $[$RANDOM%50]44[root@localhost ~]# echo $[$[$RANDOM%50]+1]  #生成1~50之间的随机数[root@localhost ~]# echo $[RANDOM % 100 + 1]

实例:生成随机颜色字符串

 [root@localhost ~]# echo -e "\033[1;$[RANDOM%7+31]mStudy\033[0m"Study[root@localhost ~]# echo -e "\033[1;$[RANDOM%7+31]mStudy\033[0m"Study[root@localhost ~]#
(3)增强型赋值:
 += i+=10 <==> i=1+10-=i-=j  <==> i=i-j*=/=%=++ i++,++1 <==> i=i+1 (自增)--i--,--i <==> i=i-1 (自减)

格式:

 let varOPERvalue

实例:自增,自减

 
[root@localhost ~]# let var+=1[root@localhost ~]# echo $var1[root@localhost ~]# let var++[root@localhost ~]# echo $var2[root@localhost ~]# let var-=1[root@localhost ~]# echo $var1[root@localhost ~]# let var--[root@localhost ~]# echo $var0[root@localhost ~]#​[root@localhost ~]# unset i j ;i=1;let j=i++;echo "i=$i,j=$j" i=2,j=1[root@localhost ~]# unset i j ;i=1;let j=++i;echo "i=$i,j=$j"i=2,j=2[root@localhost ~]# ​# i++ 与 ++i的区别:i++ 先赋值再运算++i 先运算再赋值
 

实例:鸡兔同笼问题

[root@localhost ~]# vim shell/chicken.sh#!/bin/bashHEAD=35FOOT=94​RABBIT=$(((FOOT-HEAD-HEAD)/2))CHOOK=$[35-RABBIT]echo "兔子的数量为:"$RABBITecho "鸡的数量为: "$CHOOK[root@localhost ~]# chmod +x shell/chicken.sh [root@localhost ~]# shell/chicken.sh 兔子的数量为::12鸡的数量为:23[root@localhost ~]# ​# 在脚本中写入变量,让用户在命令行写入需要计算的数值[root@localhost ~]# vim shell/chicken.sh#!/bin/bashHEAD=$1FOOT=$2​RABBIT=$(((FOOT-HEAD-HEAD)/2))CHOOK=$[35-RABBIT]echo "兔子的数量为:"$RABBITecho "鸡的数量为: "$CHOOK[root@ansible-salve1 ~]# ./chicken.sh 30 80兔子的数量为:10鸡的数量为: 25[root@ansible-salve1 ~]# 

面试题:计算出所有人的年龄总和

 [root@ansible-salve1 shell]# vim nianling.txt[root@ansible-salve1 shell]# cat nianling.txta=20b=18c=22[root@ansible-salve1 shell]# cut -d"=" -f2 nianling.txt 201822[root@ansible-salve1 shell]# cut -d"=" -f2 nianling.txt | tr '\n' + | grep -Eo ".*[0-9]" | bc60[root@ansible-salve1 shell]# grep -Eo "[0-9]+" nianling.txt 201822[root@ansible-salve1 shell]# grep -Eo "[0-9]+" nianling.txt | tr '\n' +| grep -Eo ".*[0-9]" | bc60

3.逻辑运算

True用数字表示1,False用数字表示0

  • 与:&

     1 与 1 = 1   1 与 0 = 00 与 1 = 00 与 0 = 0
  • 或:|

     1 或 1 = 11 或 0 = 10 或 1 = 10 或 0 = 0
  • 非:!

     !1 = 0  !True=False!0 = 1  !False=True
  • 异或:^

     #异或的两个值,相同为假,不同为真1 ^ 1 =01 ^ 0 =10 ^ 1 =10 ^ 0 =0

4.短路运算

  • 短路与

     CMD1 短路与 CMD2--第一个CMD1结果为0(假),总的结果必定为0,所以不需要执行CMD2--第二个CMD1结果为1(真),第二个CMD2必须要参与计算,才能得到最终的结果
  • 短路或

     CMD1 短路或 CMD2--第一个CMD1结果为1(真),总的结果必定为1,因此不需要执行CMD2--第一个CMD1结果为0(假),第二个CMD2必须要参与运算,才能得到最终的结果

5.条件测试命令

条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程,实现评估布尔声明,以便在条件性环境下进行执行。

  • 若真,则状态码变量$?返回0

  • 若假,则状态码变量$?返回1

扩展: [ ] 与 [[ ]] 的区别

 `区别1:[ ]是符合POSIX标准的测试语句,兼容性强,几乎可以运行在所有的Shell解释器中[[ ]]仅可运行在特定的几个Shell解释器中(如Bash)​`区别2:> < 可以在[[ ]]中使用ASCII码顺序进行排序,而[]不支持​`区别3:在[ ]中使用-a 和 -o 表示逻辑与和逻辑或,[[ ]]使用&& 和 || 表示,[[ ]]不支持-a​`区别4:在[ ]中==是字符匹配,在[[ ]]中==是模式匹配​`区别5:[ ]不支持正则匹配,[[ ]]支持用=~进行正则匹配​`区别6:[ ]仅在部分Shell中支持用()进行分组,[[ ]]均支持​`区别7:在[ ]中如果变量没有定义,那么需要用双引号引起来,在[[ ]]中不需要
条件测试命令及其语法

语法1:test <测试表达式> 说明:test命令和<测试表达式>之间至少有一个空格

 # 在shell中,大于用 -gt 表示,小于用 -lt 表示,大于或等于用 -ge 表示,小于或等于用 -le表示 ,不相等用-ne 表示[root@ansible-salve1 ~]# test 1 -lt 2[root@ansible-salve1 ~]# echo $?0[root@ansible-salve1 ~]# test 2 -lt 1[root@ansible-salve1 ~]# echo $?1[root@ansible-salve1 ~]# 

语法2:[<测试表达式>] 说明:该方法和test命令的用法一样,[]的两边和内容之间至少有一个空格

 [root@ansible-salve1 ~]# [ 1 -gt 3 ][root@ansible-salve1 ~]# echo $?1[root@ansible-salve1 ~]# [ 1 -lt 3 ][root@ansible-salve1 ~]# echo $?0[root@ansible-salve1 ~]# 

语法3:[[ <测试表达式> ]] 说明:比test和[]更新的语法格式。[[]]的边界和内容之间至少有一个空格。[[]]中可以使用通配符等进行模式匹配

 [root@ansible-salve1 ~]# [[ 1 > 3 ]][root@ansible-salve1 ~]# echo $?1[root@ansible-salve1 ~]# [[ 1 < 3 ]][root@ansible-salve1 ~]# echo $?0[root@ansible-salve1 ~]# 

语法4:((<测试表达式>)) 说明:一般用于if语句里,双小括号两端不需要有空格,测试对象只能是整数

 [root@ansible-salve1 ~]# ((1>2))[root@ansible-salve1 ~]# echo $?1[root@ansible-salve1 ~]# ((1<2))[root@ansible-salve1 ~]# echo $?0[root@ansible-salve1 ~]#
变量测试

语法规则:-v VAR 变量var是否被定义

示例:判断NAME变量是否被定义

 [root@ansible-salve1 shell]# [[ -v NAME ]][root@ansible-salve1 shell]# echo $?1[root@ansible-salve1 shell]# NAME=1[root@ansible-salve1 shell]# [[ -v NAME ]][root@ansible-salve1 shell]# echo $?0[root@ansible-salve1 shell]#

语法规则: -R VAR 变量VAR是否被引用

示例:判断NAME变量是否被引用

 [root@ansible-salve1 shell]# NAME=10[root@ansible-salve1 shell]# test -v NAME[root@ansible-salve1 shell]# echo $?0[root@ansible-salve1 shell]# test -R NAME[root@ansible-salve1 shell]# echo $?1[root@ansible-salve1 shell]# 
文件测试表达式
常用的文件测试操作符说明
-a/-e 文件文件是否存在
-d 文件文件存在且为目录则为真,即测试表达式成立
-f 文件文件存在且为普通文件则为真,即测试表达式成立
-r 文件文件存在且可读为真
-w 文件文件存在且可写为真
-x 文件文件存在且可执行则为真

测试文件

 [root@ansible-salve1 ~]# test -a test.txt[root@ansible-salve1 ~]# echo $?1[root@ansible-salve1 ~]# test -d shell[root@ansible-salve1 ~]# echo $?0[root@ansible-salve1 ~]# touch test.txt[root@ansible-salve1 ~]# test -w test.txt &&echo  "true"true[root@ansible-salve1 ~]# test -r test.txt &&echo  "true"true[root@ansible-salve1 ~]# test -x test.txt &&echo  "true"[root@ansible-salve1 ~]# 
字符串测试表达式
常用字符串测试操作符说明
-n ”字符串“若字符串的长度不为0,则为真,即测试表达式成立,n可以理解为nozero
-z ”字符串“若字符串的长度为0,则为真,z可以理解为zero
>Ascii码是否大于Ascii码
“字符串1” == ”字符串2“若字符串1长度等于字符串2长度,则为真
“字符串1” != ”字符串2“若字符串1长度不等于字符串2长度,则为真
“字符串1” =~ “字符串2”左侧字符串是否能被右侧的PATTERN所匹配。注意:此表达式用于[[ ]]中:扩展的正则表达式

测试字符串

 [root@ansible-salve1 ~]# var_test='Mike' [root@ansible-salve1 ~]# echo $var_test Mike[root@ansible-salve1 ~]# [[ -n var_test ]] && echo "True"True​# 通配符[root@ansible-salve1 ~]# FILE=test.log[root@ansible-salve1 ~]# [[ "$FILE" == *.log ]] && echo "True"True[root@ansible-salve1 ~]# FILE=test.txt[root@ansible-salve1 ~]# [[ "$FILE" == *.log ]] && echo "True"[root@ansible-salve1 ~]# [[ "$FILE" != *.log ]] && echo "True"True[root@ansible-salve1 ~]# ​# 扩展的正则表达式[root@ansible-salve1 ~]# FILE=test.log[root@ansible-salve1 ~]# [[ "$FILE" =~  \.log$ ]] && echo "True"True[root@ansible-salve1 ~]# N=100[root@ansible-salve1 ~]# [[ "$N" =~ ^[0-9]+$ ]] && echo "True"True[root@ansible-salve1 ~]# N=A10[root@ansible-salve1 ~]# [[ "$N" =~ ^[0-9]+$ ]] && echo "True"[root@ansible-salve1 ~]# IP=1.2.3.4[root@ansible-salve1 ~]# [[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]][root@ansible-salve1 ~]# echo $?0[root@ansible-salve1 ~]# # 使用正则表达式判断IP是否合法[root@ansible-salve1 ~]# IP=1.2.3.333[root@ansible-salve1 ~]# [[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]][root@ansible-salve1 ~]# echo $?1[root@ansible-salve1 ~]# IP=255.255.255.255[root@ansible-salve1 ~]# [[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]][root@ansible-salve1 ~]# echo $?0[root@ansible-salve1 ~]# 
 整数测试表达式
在[ ] 或 test中使用的比较符号在(()) 或 [[ ]]中使用的比较符号(不用这个做数字比较)说明
-eq== 或 =相等,equal
-ne!=不相等,not equal
-gt>大于,greater than
-ge> =大于等于,greater equal
-lt<小于,less than
-le< =小于等于,less equal
逻辑操作符
在[ ] 中使用的操作符在test, [[ ]] , (( ))中使用的逻辑操作符说明
-a&&and,与,两边都为真,则结果为真
-o||or,或,有真则真,同假则假
not,非,两端相反,则结果相反

示例

 [root@ansible-salve1 ~]# var_test=1[root@ansible-salve1 ~]# var_t=2[root@ansible-salve1 ~]# [ $var_test -lt 0 -a $var_t -gt 0 ][root@ansible-salve1 ~]# echo $?1[root@ansible-salve1 ~]# [ $var_test -lt 0 -o $var_t -gt 0 ][root@ansible-salve1 ~]# echo $?0[root@ansible-salve1 ~]# 

总结:

       在Linux操作系统中的Shell脚本,不仅是命令的集合,更是实现自动化、批处理和系统管理的强大工具。通过编写脚本,第一点能效率提升:将重复性操作(如日志清理、备份)固化到脚本中,避免人工干预,第二点流程标准化:确保关键任务(如服务部署)每次执行逻辑一致。

相关文章:

  • 【使用JAVA调用deepseek】实现自能回复
  • ffmpeg(三):处理原始数据命令
  • Quipus系统的视频知识库的构建原理及使用
  • 编译一个Mac M系列可以用的yuview
  • JAVA国际版一对一视频交友视频聊天系统源码支持H5+APP
  • Canal
  • L1-019 谁先倒 (15 分)
  • 每日算法 -【Swift 算法】三数之和
  • Fréchet Inception Distance(FID)
  • 自驾总结_Routing
  • Vue全局事件总线
  • Java编程常见错误与最佳实践
  • Numpy——结构化数组和Numpy文件
  • Hadoop企业级高可用与自愈机制源码深度剖析
  • Qt Quick快速入门笔记
  • 【Java】使用VarHandler实现无锁Stack
  • 具备强大的数据处理和分析能力的智慧地产开源了
  • 测试开发笔试题 Python 字符串中提取数字
  • C++ 使用 ffmpeg 解码 rtsp 流并获取每帧的YUV数据
  • [特殊字符] FFmpeg 学习笔记
  • 网站建设 上海珍岛/域名注册查询网站
  • wordpress 文本小工具栏/南宁百度seo价格
  • 定制网站建设多少钱/小程序制作
  • 汨罗住房和城乡建设局网站/百度一下官网首页登录
  • 不懂代码用cms做网站/湖南网站建设平台
  • 什么网站做任务能挣/微信营销的方法和技巧