第十五章 Linux Shell 编程

news/2024/12/18 17:22:50/

15.1 Shell 变量

  1. 了解:Shell的功能

  2. 了解:Shell的种类

  3. 了解:Shell的调用

  4. 了解:Shell变量的概念

  5. 了解:Shell变量的定义

  6. 了解:Shell数组变量

  7. 了解:Shell内置变量

  8. 了解:双引号 和 单引号

  9. 了解:Shell变量作用域

15.1.1 shell的概念

Shell 的英文含义是<外壳>

Shell 是<命令解释器程序>的统称,专门用于对<用户命令>进行解释,从而让内核可以理解并执行,因此 Shell就是<用户>与<内核>交互的<桥梁>

Shell 功能如下:

● 接收 用户命令

● 调用 相应的应用程序

● 解释并交给 内核去处理

● 返还 内核处理结果

人机交互程序,命令解释器,翻译官,让内核理解我们输入的命令

在这里插入图片描述

15.1.2 shell 的种类

Shell 的种类有多种:

​ MS-DOS(本身就是一个Shell)

​ Windows的Shell:Windows Explorer(图形化)、cmd(命令行)

​ UNIX的Shell:sh、bash、csh、ksh等等(平时我们所说的Shell,多指UNIX的Shell)

sh程序:

​ <sh程序>是shell的一种,它遵循<Unix POSIX 标准>,从而具备<UNIX可移植性>。

​ <sh程序>是<其他 UNIX Shell>的基础,各类<UNIX系统>均可很好的支持。大部分shell都是有sh 改进而来,最强的是/bin/bash也是默认的

bash程序:

​ <bash程序>是<sh程序>的增强版,也是<大多数Linux系统>的<默认Shell>。

15.1.3 shell 的调用

chsh -l					## 查看:系统可用的 Shell
echo $SHELL 			## 查看默认的shell  也是放内嵌命令的地方 一般是 /bin/bash永久更改:用户的登录Shell							
useradd user01
chsh -s /bin/sh user01	## 永久更改user01用户的登录 Shell
cat /etc/passwd			## 查看更改结果		显示调用shell
bash -c date			## 显式调用bash,来执行date命令
sh -c date				## 显式调用sh,来执行date命令隐式调用shell	用的最多
date   					## 隐式调用<用户默认的Shell>,来执行date命令
vi test.sh				
#!/bin/bash				## 在Shell脚本中,申明本脚本所调用的Shell
date

15.1.4 Shell 变量赋值与引用

变量:就是一个<进程>中用于<存储数据>的<内存实体>,它对应着一个<内存存储空间>。

<变量名>只能包括:<字母>、<数字>和<_下划线>,并且,<变量名>不能以<数字>开头。

定义Shell变量

var01="123"				## 直接赋值
var02="${var01}456"		## 引用<其他变量值>来赋值
var03="`date`			## 引用<命令屏显输出>来赋值
echo "$var01"			## 显示<变量值>  123 
echo "$var02"			## 显示<变量值>  123456
echo "$var03"			## 显示<变量值>  显示时间,注意显示的是定义变量的时间,而不是echo 这里的时间

15.1.5 Shell 数组变量

Shell 数组变量:就是一组同姓不同名的<变量集合>,彼此用<索引下标>加以区分。

Shell 数组变量:分为两类

第一类:普通数组,即:数字下标,有序数组 (最常用)

第二类:关联数组,即:命名下标,无序数组 (不常用)

定义:Shell数组变量(普通数组)

a=()					## 只定义不赋值
a=("a" "b" "c") 		## 赋值需要空格隔开,下标从0开始
a=(
a
b
c
)						## 同上
a[0]="a"
a[1]="b"
a[5]="c"				## 赋值单个数组

引用:Shell数组变量(普通数组),里面的都是数组而不是单个变量

echo $a					## 读取第一个数组元素值,即下标0
echo ${a[n]}			## 读取下标n的数组元素值,n是数字
echo ${a[*]}			## 读取全部数组元素值
echo ${a[@]}			## 读取全部数组元素值 常用于循环脚本echo ${a[*]:n}			## 从下标n开始,读取后面全部数组元素值
echo ${a[*]:n:m}	    ## 从下标n开始,读取m个数组元素值
例如:
echo ${a[*]:0:3}		## 显示下标 0 1 2 的值
echo ${a[*]:1:3}		## 显示下标 1 2 3 的值
echo ${a[*]:2:4}		## 从下标2开始,读取4个数据的值,2 3 4 5 echo ${!a[*]}			## 列出所有元素的下标(因为定义数组时候可不一定按下标顺序来定义)
echo ${#a[*]}			## 显示数组有多少元素数量,相当于有多少下标 有10个元素则 输出10
echo ${#a[1]}			## 显示a[1]里面的值有多少位数,比如a[1]=100,则输出 3
echo ${#a[*]} 		## 在判断某不确定命令有多少数组时,去抓取做循环很好

${a[@]} 使用例子,数组一个个打印出来

#!/bin/bash
Service="1,2,3,4,5"
# 使用逗号分割字符串为数组 赋值给service
IFS=',' read -r -a service <<< "$Service"# 将数组每个值赋值给 test
for test in "${service[@]}"; doecho "$test"
done

15.1.6 删除 shell 变量

语法:unset 变量名

比如 :unset a 删除a 或者 删除a数组

15.1.7 执行shell 脚本

共享<父进程>执行法:source test.sh 或者 . test.sh (可使用 export 刷新环境变量)

独立<子进程>执行法:bash test.sh 或者 sh test.sh

​ ./test.sh(需要x权限) 或者 /root/test.sh(需要x权限)

置换<父进程>执行法:exec ./test.sh(需要x权限)或者 exec /root/test.sh(需要x权限)

15.1.8 shell 内置变量

(命令行参数)

Shell 内置变量:就是<Shell命令解释器程序>固有的<变量>,无需定义便可以直接引用。(系统自带变量)

常用的Shell内置变量:

$0			## Shell脚本自身的<文件名>和<调用路径>
$1 -- $n	## Shell脚本的<命令行参数>,$1是第1参数、$2是第2参数,以此类推## 从${10}开始参数需要强制使用花括号{}括起来
$*   $@		## Shell脚本的<所有命令行参数>的<列表>## 如Shell脚本接收了$1 $2两个参数,则 $* 等于$1 $2
$#			## Shell脚本的<命令行参数>的<总个数>
$?			## 保存上一个命令的return状态返回值 0表示成功运行,1表示有问题
$!			## 保存的是最近一个后台进程的 PID (可存储pid遍历判断) <<命令 & pid=$!>>
$$			## Shell脚本自身的<PID进程号>

例子:

vim test.sh
#!/bin/bash  
echo "$* ## 表示这个程序的所有参数 " 
echo "$# ## 表示这个程序的参数个数" 
echo "$$ ## 表示程序的进程 ID " 
echo "执行命令 ls & pid=\$!"
ls & pid=$!	 ## pid=$!这里 pid 是一个变量
echo "${pid} ## 这里的\${pid}则是\$!" 
echo "$! ## 执行上一个后台指令的 PID"
echo "$? ## 表示上一个程序执行返回结果 "
echo -e "## 显示<本Shell脚本>=$0"
echo -e "## 格式输出:指定的<命令行参数>=$1\t$2\t$3"## \t 是正则表达式,表示水平制表符bash test.sh aa bb cc dd ee		## 执行脚本且写5个参数 终端如下显示
[root@dj tmp]# bash test.sh aa bb cc dd ee
aa bb cc dd ee ## 表示这个程序的所有参数 
5 	 ## 表示这个程序的参数个数
1143 ## 表示程序的进程 ID 
执行命令 ls & pid=$!
1144 ## 这里的${pid}则是$!
1144 ## 执行上一个后台指令的 PID
0 	 ## 表示上一个程序执行返回结果 
## 显示<本Shell脚本>=test.sh
## 格式输出:指定的<命令行参数>=aa	bb	cc## 注意 aa bb cc dd ee 是新写的五个变量,可以用五个下标表示

$! 使用例子

检查k8s部署状态

export service=$(echo $service | base64 -d)
export Test_Version=$(echo $Test_Version | base64 -d)# 执行 kubectl rollout status 命令并等待100秒
kubectl rollout status deployment/${service}-rpc${Test_Version} -n go & pid=$!# 等待命令执行完毕或超时(100秒)
sleep 100# 检查命令执行状态
if kill -0 $pid > /dev/null 2>&1; thenecho "-------------部署未完成,超时打印日志-------------"# 获取部署的所有 Pod 名称pod_names=$(kubectl get pods -l app=${service}-rpc${Test_Version} -n go -o=jsonpath='{.items[*].metadata.name}')# 打印每个 Pod 的日志for pod in ${pod_names}; doecho "Logs for pod ${pod}:"kubectl logs ${pod} -n godoneexit 1
elseecho "部署成功"
fi

15.1.9 双引号 和 单引号

<""双引号> 是:<部分引用>,可以识别<特殊符号>

<''单引号> 是:<完全引用>,不能识别<特殊符号>

举例:

终端显示如下
[root@dj tmp]# a=abc
[root@dj tmp]# echo "${a}"
abc
[root@dj tmp]# echo '${a}'	 ## 单引号 不能识别特殊符号 比如 $ 符
${a}
[root@dj tmp]# echo ${a}
abc

15.1.10 `` 或 $() 命令替换

$() 或 `` 命令替换:将<字符串>中的<命令>,置换为<命令>的<屏显输出>

`` 命令替换 :是sh的语法,兼容性最高

$()命令置换 :是bash语法,sh不支持 所以一般使用 反引号 ``

a="ls -l"
echo $a							会输出 ls -la=`ls -l`	或者  a=$(ls -l)
echo $a 						会输出 ls -l命令列出的内容

15.1.11 shell 变量作用域 export

Shell 变量作用域:指的是一个<Shell变量>的<有效范围>。

按<Shell 变量作用域>分类:

Shell 本地变量:仅在<本进程>中生效的<Shell变量>。

Shell 环境变量:仅在<本进程>中生效,又可被<子进程>继承并生效的<Shell变量>。

也就是说如果开两个远程连接,在第一个远程连接定义本地变量,另外一个远程连接用不了该本地变量,但是如果是环境变量,可以通过继承去使用该环境变量。

注意:<Shell 数组变量>不可定义为<Shell 环境变量>。

本地变量定义方法:

a=“123”

环境变量定义方法:

export a="123"        ## 定义变量a为Shell 环境变量   一般使用这个declare -x b="456"    ## 定义变量b为Shell 环境变量

例子证明

a=123				## 定义本地变量
vim test.sh 		## 写一个脚本证明## 脚本相当于一个子进程,因为脚本也可以使用另外一个远程连接来写#!/bin/bash	
echo $a				   在脚本里面运行bash test.sh			
## 发现并无 123 输出
## 说明本地变量不能给子进程使用,只能给创建a的进程使用
export b=123		## 定义环境变量
vim test.sh 		## 写一个脚本证明## 脚本相当于一个子进程,因为脚本也可以使用另外一个远程连接来写#!/bin/bash	
echo $b					在脚本里面运行bash test.sh			发现有123显示在终端上,说明环境变量可在脚本里面也可运行## 注意:因为前面用 a 设置本地变量,所以设置环境变量不能使用 a ,否则会出错,因为环境变量虽然会覆盖本地变量的a,但是其实在配置文件里面还会存在本地变量a为123
## 这时就会出现 在脚本里面运行的时候,会运行本地变量的 a 而不是环境变量的 a 从而导致一样没有123显示,这样就无法判断

Linux 系统中设置环境变量例子 .env文件

export LOCAL_DEBUG=0
export NACOS_HOST=120.79.2.53
export NACOS_PASSWD=nacos
export NACOS_USERNAME=nacos
export NACOS_NAMESPACEID=datahub
export NACOS_DATAID=BehaviorDataReceiver
export NACOS_LOGLEVEL=debug
export NACOS_GROUP=DEFAULT_GROUP
export NACOS_LOGDIR=nacos_log
export NACOS_CACHEDIR=nacos_cache

执行 source .env 加载该配置文件

注意:这是一个包含环境变量的配置文件,不是脚本

一般环境变量可以在运行应用程序时被读取,以便配置应用程序的行为或连接数据库、服务等

.env 也可以加载 也可以写在 k8s yaml 的env 里面,一般使用该配置文件用于快速测试能否连接

15.2 echo 标准错误输出 重定向符 管道符

15.2.1 echo 命令

功能:将<参数>写到<标准输出>。

语法:echo [-neE] [参数 …]

选项参数:

-n   ## 不追加换行符  ☚ 默认情况下,两条命令结合起来会追加换行符
-e   ## 启用:反斜杠转义 

echo 支持的常用<反斜杠字符>如下:

\n   	## 换行
\t   	## 横向制表符
\r	 	## 用于表示回车符   一般用于循环覆盖前面的内容比如 echo -e "\r" 表示输出一个回车符
回车符会将光标移动到当前行的开头位置,使得后续输出会覆盖当前行的内容。这在一些需要实现文本动态更新或进度条效果的场景中特别有用。
比如 倒数计时
[root@server ~]# a=`df -h`
[root@server ~]# echo $a
文件系统 容量 已用 可用 已用% 挂载点 devtmpfs 475M 0 475M 0% /dev tmpfs 487M 0 487M 0% /dev/shm tmpfs 487M 7.7M 479M 2% /run tmpfs 487M 0 487M 0% /sys/fs/cgroup /dev/mapper/centos-root 18G 1.5G 17G 9% / /dev/sda2 1014M 138M 877M 14% /boot tmpfs 98M 0 98M 0% /run/user/0[root@server ~]# echo "$a"		## 加上双引号
文件系统                 容量  已用  可用 已用% 挂载点
devtmpfs                 475M     0  475M    0% /dev
tmpfs                    487M     0  487M    0% /dev/shm
tmpfs                    487M  7.7M  479M    2% /run
tmpfs                    487M     0  487M    0% /sys/fs/cgroup
/dev/mapper/centos-root   18G  1.5G   17G    9% /
/dev/sda2               1014M  138M  877M   14% /boot
tmpfs                     98M     0   98M    0% /run/user/0## 侧面说明了在赋予变量的时候,还是按照命令的格式,而不是变成空格一行输出,变成一行是echo的原因

-n选项:

[root@dj ~]# echo "1"; echo "2"; echo "3"	## 会换行
1
2
3
[root@dj ~]# echo -n "1"; echo -n "2"; echo "3"
123与之相似还有 命令|tr -d   还有 命令| sed ':y;N;s/\n//g;b y'
比如 echo `ifconfig` 

-e 和 \t 选项

[root@dj tmp]# echo  "123\t456\n789"
123\t456\n789
[root@dj tmp]# echo -e "123\t456\n789"
123	456
789[root@dj ~]# echo -e "1\t2\t3"
1	2	3
[root@dj ~]# echo -e 1\t2\t3			
1t2t3								## 注意:不加双引号不行,-e 不起作用
echo 1.txt | rm -rf			## 这样不行,因为echo只是输出给 rm 接收,它不一定会接收## 不行就添加 xargs

倒计时覆盖

#!/bin/bash
for i in {5..1}
do		echo  -n  "还有$i秒,幸运儿究竟是谁"	## 不换行输出echo  -ne "\r\r"     			   ## 覆盖输出 不加的话会显示全部 一个\r就可以sleep 1							   ## 不加的话运行会显示[root@dj tmp]# 究竟是谁,(还有$i秒) 被覆盖了
done

15.2.2 标准输入 标准输出 标准错误输出

标准输入:就是通过<I/O字符终端设备,如:键盘)输入的<信息>。(输入是没错的) (0 表示)

标准输出:命令执行成功之后,在屏幕上输出的<命令结果显示信息>。(1 表示)

标准错误输出:命令执行失败之后,在屏幕上输出的<命令错误提示信息>。(2 表示)

(& 表示全部,无论正确还是错误信息)

ls  1>> 1.txt			## 把ls 展现的正确输出结果 追加到 1.txt
sksoefsl  2>> 1.txt		## 把展示出来的错误信息 追加到 1.txt
ls  &>> 1.txt			## 无论是正确还是错误的,该命令的输出全部追加到 1.txt

对于不需要的信息,我们可以写入 黑洞 ,不占内存,黑洞路径是 /dev/null ,比如

yum -y install tree &> /dev/null
echo $?					## 查看上条命令成功与否,0为成功,1为失败

省略不写相当于是标准输出1

常用于丢弃输出,然后自定义显示

命令 > /dev/null 2>&1

ls -waadaw > /dev/null 2>&1			## 标准输出1重定向到/dev/null## 标准错误输出2重定向到标准输出1ls -waadaw &> /dev/null 			## 将标准输出和标准错误都重定向到/dev/null	

15.2.3 >、>> 标准错误输出重定向

重定向:改变<命令>的<标准输出/标准错误输出>的<输出方向>,不输出到<屏幕>,而是输出到<文件>。

★ > 重定向符:覆盖式重定向

★ >> 重定向符:追加式重定向

<标准输出>重定向,主要用于保存<命令执行成功的结果信息>,便于后期引用。

echo "123" > 1.txt	     	## 覆盖式重定向
echo "456" >> 1.txt      	## 追加式重定向

<标准错误输出>重定向,主要用于保存<命令执行失败的错误信息>,便于查阅分析

yum instll wget -y 2> 1.txt     	## 覆盖式重定向
yum updata -y 2>> 1.txt       		## 追加式重定向

<标准输出/标准错误输出>重定向,静默执行命令 不输出内容

yum update -y &> /dev/null          ## 方法1:&代表<标准输出/标准错误输出>
yum update -y 1> /dev/null 2>&1     ## 方法2:2代表标准错误输出;1代表标准输出## 2给1,1给/dev/null,这个世界就安静了

15.2.4 <、<< 标准重定向输出

★ < 输入重定向:从<指定文件>中,读取数据,输入到<指定文件>中

★ << 输入重定向:从<标准输入>中,读取数据,输入到<指定文件>或<指定命令>中

< 输入重定向例子:

覆盖式输入

echo "aaa" > 1.txt
cat > 3.txt < 1.txt				## 将1.txt里面的内容写进3.txt并覆盖

追加式输入

cat >> 3.txt < 1.txt

<< 输入重定向例子

cat << 
EOF
This is a test.
Hello, world!
EOF## 会直接在终端输出
This is a test.
Hello, world!

将111222追加到 3.txt

cat << EOF >> 3.txt
111222
EOF

15.2.5 | 管道符

管道符:像流水线一样,将一个<命令>的<标准输出>,作为另一个<命令>的<标准输入>。

echo "1.txt" | touch -        	## 创建 1.txt文件  不一定成功
echo "1.txt" | xargs touch		## 创建 1.txt

15.2.6 生成随机数 $RANDOM

使用 R A N D O M 变量: RANDOM变量: RANDOM变量:RANDOM 是一个环境变量,它可以在每次被调用时生成一个 0 到 32767(2^15-1)之间的随机整数。可以通过以下脚本来生成一个随机数:

最简单命令:echo $RANDOM

原理:

#!/bin/bash
random_number=$RANDOM
echo "随机数:$random_number"
##随机数 生成 0 到 32767 的某一个数

例子1:随机生成 0 到 2的随机数

#!/bin/bash
echo $[RANDOM%3]
相当于对 3 取余,只有 0  1  2  产生
##运行脚本 生成  0  1   2  三个随机数,可拿来做数组下标,做一个随机生成名字喊人回答问题的代码

随机字符 /dev/urandom 生成随机字符

[root@dj tmp]# cat /dev/urandom |tr -dc [:alnum:] |head -c 8nRSCSps2
[root@dj tmp]# cat /dev/urandom |tr -dc [:alnum:] |head -c 8
yCGqVAzq[root@dj tmp]#

例子2 随机生成石头剪刀布,赢了退出,输了继续比

思想:做一个死循环,如果满足条件就 break 退出该循环,while true 也可以

[root@dj tmp]# cat 2.sh

#!/bin/bash
echo "我们一起来玩石头剪刀布吧"
xt=1
while (($xt<3))
doxt=$[RANDOM%3]read -e -p "请输入您的选择(0:石头 1:剪刀 2:布):" aif [[ $a == 0 ]]thenecho "您出的是石头"if (($xt==1))thenecho "我出的是剪刀,恭喜宁赢了,欢迎下次再来pk"breakelif (($xt==0))thenecho "不好意思我出的也是石头,平局喔,请继续"elseecho "不好意思我出的是布,您输了,请继续"fielif [[ $a == 1 ]]thenecho "您出的是剪刀"if (($xt==2))thenecho "我出的是布,恭喜宁赢了,欢迎下次再来pk"breakelif (($xt==1))thenecho "不好意思我出的也是剪刀,平局喔,请继续"elseecho "不好意思我出的是石头,您输了,请继续"fielif [[ $a == 2 ]]thenecho "您出的是布"if (($xt==0))thenecho "我出的是石头,恭喜宁赢了,欢迎下次再来pk"breakelif (($xt==2))thenecho "不好意思我出的也是布,平局喔,请继续"elseecho "不好意思我出的是石头,您输了,请继续"fielseecho "输入错误,请重新输入"fi
done
##注意:输入那里的 if 判断必须使用 [[ ]] ,如果是 (( )),如果输入大于3的数字会正常,但是输入字母会默认成石头

例子3 随机点名

#!/bin/bash
for i in {2..1}
doecho  -n  还有$i秒,幸运儿究竟是谁 !!echo  -ne "\r\r"     sleep 1
doneecho  " "clearname=(`cat /root/name.txt`)name_list=${#name[*]}			## 数组有多少元素赋值给它,假如有5个元素则为5name_index=$[RANDOM%$name_list]echo -n "${name[$name_index]}" 	## 根据下标找人echo  ""## 注意:name.txt 里面放的是名单 一行一个名字,还有 name_list 是里面存放是数字,而不是名字
## xt=$[RANDOM%3]可以换成 ((xt=RANDOM%3)) 或者 let "xt=RANDOM%3"

例子4 猜1到1000的随机数

#!/bin/bash
target=$[$RANDOM%1000+1]while true
doread -p "请输入一个数字(1到1000): " n# 检查输入是否为整数if ! [[ $n =~ ^[0-9]+$ ]]; thenecho "请输入有效的数字!"continuefi# 判断猜测结果if [ $guess -eq $n ]; thenecho "恭喜你,猜对了!"breakelif [ $guess -lt $n ]; thenecho "猜的数字太小了,请继续猜!"elseecho "猜的数字太大了,请继续猜!"fidone

15.3 通配符、正则、grep

15.3.1 通配符

通配符:是Shell自身的一种特殊语句,实现<路径名展开 pathname expansion>展开功能。

用于:模糊搜索<文件>。

* 		## 表示0或多个的<任意字符>  (相当于正则里面  .*)
?		## 表示单个<任意字符>	   (相当于正则  .)
[ ] 	## 表示匹配<字符列表或范围>中的<单个字符>(和正则里面一样)
[xyz]  	## 设置可选的<字符列表>
[x-y]  	## 设置可选的<字符范围>
[^xyz]  ## 表示不选择x、y、z;
^ 		## 表示不匹配,^ 必须是第一个字符

举例:

cd /tmp; rm -rf *; mkdir dir1 dir2 dir3; touch file1 file2 file3
## 通过<路径名展开 pathname expansion>的<通配符>来列出<目录或文件>ls -l -d dir[1-3]
ls -l file[1-3]rm -rf ???          ## 删除当前目录下所有的任意3个字符命名的文件

15.3.2 正则表达式

1 正则表达式(Regular Expression)的概念

正则表达式:描述了<目标字符串>的<匹配模式(pattern)>。

注意:<正则表达式>和<Shell 通配符>不是一回事!

生活比喻:

我们要在一群人中找一个人,但是我只知道他是湖北武汉人,姓催,因此我们只能这样说:“那个"姓催的武汉人”,请出来,我们有事找你"。在这段话中,"姓催的武汉人"就是一个生活中的<正则表达式>。

2 正则表达式的种类

​ 基本正则:是<标准的元字符集>,是其他<正则>的<子集>。

​ 扩展正则:是<扩展的元字符集>,包含着<标准元字符+扩展元字符>。 -E

​ Perl正则 -P

3 认识:正则元字符

元字符基本扩展功能说明
^匹配:行首 ^s 以s开头
$匹配:行尾 s$ 以s结尾
.匹配:任意单个字符
?匹配:<前一项>出现<0次或1次>
+匹配:<前一项>出现<1次或多次>
*匹配:<前一项>出现<0次或多次> .* 代表0个或多个任意单个字符
[ ]匹配:<字符列表>中的<任意一个字符>
注意:在 [ ] 中,字符中间的 - 有着<特殊含义>
[-] 匹配:<字符范围>中的<任意一个字符>
[-] 匹配:依照<字典顺序>到达<指定字符>截止的<所有字符>
[^] 匹配:<字符集合>外的<任意一个字符>   
在 [ ] 中,开头第一个 ^ 有着<特殊含义>    在 [ ] 中,<其余字符>均为<原义字符>
( )定义:<正则分组>
|设置:逻辑或
{ }{n} 匹配:<前一项>出现<n次>,n可以设为0
{n,} 匹配:<前一项>最少出现<n次>,n可以设为0
{,m} 匹配:<前一项>最多出现<m次>
{n,m} 匹配:<前一项>最少出现<n次>,最多出现<m次>
<匹配:<单词>的<左边界字符>
>匹配:<单词>的<右边界字符>
\b匹配:<单词>的<左边界字符>或<右边界字符>
\B匹配:<单词>的<非左边界字符>或<非右边界字符>
\d匹配:单个<数字字符> ☚ 仅属于:<perl 正则元字符集>
\D匹配:单个<非数字字符> ☚ 仅属于:<perl 正则元字符集>
\w匹配:单个<单词的字符>,如:字母、数字、_下划线
\W匹配:单个<非单词的字符>
\s匹配:单个<空白字符>,包含:水平/垂直制表符
\S匹配:单个<非空白字符>
\t匹配:水平制表符 ☚ 仅属于:<perl 正则元字符集>
\v匹配:垂直制表符 ☚ 仅属于:<perl 正则元字符集>
\f匹配:换页符 ☚ 仅属于:<perl 正则元字符集>

4 认识:转义符 \

转义符:恢复<元字符>的<原义>,还原为<本义字符>。

举例说明:

^ 	## 匹配行首,比如 s^ 是匹配 s
\^  ## 表示这是一个 ^本义字符,它不再是 ^元字符,而是^本身意思$ 	## 匹配行尾,比如 s 是匹配 s
\$  ## 表示:这是一个 $本义字符,它不再是 $元字符。

15.3.3 grep命令

grep命令的功能: 三剑客 (grep sed awk ,其中 tr 称为 小sed,cut称为小awk)

注意:grep只能列出文件想要看的精准内容,不能用grep修改

逐行扫描<文本文件>或者<标准输出/标准错误输出>的<每一行>。

通过<关键字>和<正则表达式>,来筛选出<匹配的行>,然后标准输出<匹配的整行>。

注意:通过匹配行内的某些特征去匹配那一行,使用 -o 则只输出匹配的字符

语法:grep [选项] <关键字 和 正则表达式> [文件名 | -]

文件名 指定需要读取的<文件名>

表示准备接收<标准输入> 具备这功能的命令,可以接收 | 管道符

选项:

-E		## 采用:扩展正则  egrep 相当于 grep -E
-P		## 采用:Perl正则
-i		## 忽略:大小写
-o		## 仅输出:匹配的<字符串>  不写都是输出满足匹配的行
-c		## 统计:匹配的行数 ☚ 注意:不是输出的行数 有多少行输出多少几  可拿来统计个数
-v		## 输出:不匹配的<行>
-l		## 列出:包含<匹配字符串>的<文件名>
-L		## 列出:不包含<匹配字符串>的<文件名>
-r		## 执行:目录递归操作 ☚ 仅与 -l 或 -L 配合使用
-w		## 选项表示只匹配整个单词,而不是部分匹配
--exclude=FILE_PATTERN		## 排除:指定的文件名,仅可使用<通配符>
--exclude-dir=PATTERN		## 排除:指定的目录名,仅可使用<通配符>

-c 例子 更多使用 wc -l

[root@dj exports.d]# grep -c '' /tmp/passwd   有49行
49

例子:查看文件,去除注释和空行 扩展正则

egrep -v "^\s*(#|$)" /etc/passwd 	## 以 0个或多个 空格 开头,然后接 # 号  或者  直接结尾  

-w 例子

netstat -tunlp | grep -w "22"## 会匹配 22 的内容,但是 222,223 122 这种不会被匹配

15.4 各种常用辅助命令

15.4.1 sort、uniq、wc 命令

sort 命令:排序

☛ 命令选项说明

-g		## 按<一般数值>排序
-n		## 按<字符串数值>排序
-r		## 逆序排序(默认升序) -gr 就是按数字降序排序  重要
-f		## 忽略<字母大小写>
-b		## 忽略<前导的空白区域>
-k		## 按照指定字段进行排序 sort -k 2 file.txt 按第二字段进行排序
-o		## 将<排序结果>回写入<原文件>,例如:cat 1.txt

★ uniq 命令:分组汇总

有相同的行就去重。

-c  			## 去重的同时统计出现多少次
uniq -c    		## 常用于查找ip访问最多

★ wc 命令:统计

☛ 命令选项说明

不加选项,分别表示行数、单词数量和字节数  比如 wc  /etc/passwd
-l		## 统计:行数
-W		## 统计:单词数
-c		## 统计:字节数
-m		## 统计:字符数
-L		## 显示:最长行的长度

一般组合使用是 找相同 排序 去重 在排序

统计并显示次数出现前十的ip access_log 是文件名

[root@dj ~]# cat access_log | awk '{print $1}'| sort -r | uniq -c | sort -gr | head -101055 192.168.1.25509 192.168.1.14360 192.168.1.37303 192.168.1.12262 192.168.1.41254 192.168.1.21242 192.168.1.8234 192.168.1.5203 192.168.1.31195 192.168.1.30

netstat -anlp|grep 80|grep tcp|awk ‘{print $5}’|awk -F: ‘{print $1}’|sort|uniq -c|sort -nr|head -n20

三个一起使用能发挥重大作用,一个排序 一个去重 一个分组

例子:

lastb | sort -k 3 -k 1 | uniq -c | sort -k 1   ## 统计显示:<lastb命令>查看到的登录失败信息ss -tan |sort | uniq -c			## 统计显示网络连接信息
cat /etc/passwd | wc -l			## 统计显示/etc/passwd 文件的行数
cat /etc/passwd | wc -m			## 统计显示/etc/passwd 文件的字符数

统计显示:网络连接信息:没排序和 排序和排序去重的区别对比

[root@dj ~]# ss -tan
State       Recv-Q Send-Q           Local Address:Port                    Peer Address:Port              
LISTEN      0      5                192.168.122.1:53                                *:*                  
LISTEN      0      128                      	*:22                                *:*                  
LISTEN      0      128             	   	127.0.0.1:631                               *:*                  
LISTEN      0      100                  127.0.0.1:25                                *:*                  
LISTEN      0      128                    		*:111                               *:*                  
ESTAB       0      0                192.168.58.150:22                    192.168.58.1:50395             
ESTAB       0      0                192.168.58.150:22                    192.168.58.1:50298             
ESTAB       0      0                192.168.58.150:22                    192.168.58.1:50288             
ESTAB       0      0                192.168.58.100:22                    192.168.58.1:51760             
ESTAB       0      0                192.168.58.150:22                    192.168.58.1:50394             
ESTAB       0      0                192.168.58.100:22                    192.168.58.1:51684             
ESTAB       0      0                192.168.58.100:22                    192.168.58.1:51761             
LISTEN      0      128                       [::]:22                               [::]:*                  
LISTEN      0      128                     	[::1]:631                              [::]:*                 
LISTEN      0      100                      [::1]:25                               [::]:*                  
LISTEN      0      128                       [::]:111                              [::]:* [root@dj ~]# ss -tan |sort   (排序后)
ESTAB      0      0      192.168.58.100:22                 192.168.58.1:51684              
ESTAB      0      0      192.168.58.100:22                 192.168.58.1:51760              
ESTAB      0      0      192.168.58.100:22                 192.168.58.1:51761              
ESTAB      0      0      192.168.58.150:22                 192.168.58.1:50288              
ESTAB      0      0      192.168.58.150:22                 192.168.58.1:50298              
ESTAB      0      0      192.168.58.150:22                 192.168.58.1:50395              
ESTAB      0      36     192.168.58.150:22                 192.168.58.1:50394              
LISTEN     0      100      [::1]:25                    [::]:*                  
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      128          *:111                      *:*                  
LISTEN     0      128       [::]:111                   [::]:*                  
LISTEN     0      128    127.0.0.1:631                      *:*                  
LISTEN     0      128      [::1]:631                   [::]:*                  
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      128       [::]:22                    [::]:*                  
LISTEN     0      5      192.168.122.1:53                       *:*                  
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port  [root@dj ~]# ss -tan |sort | uniq -c	(去重后)1 ESTAB      0      0      192.168.58.100:22                 192.168.58.1:51684              1 ESTAB      0      0      192.168.58.100:22                 192.168.58.1:51760              1 ESTAB      0      0      192.168.58.100:22                 192.168.58.1:51761              1 ESTAB      0      0      192.168.58.150:22                 192.168.58.1:50288              1 ESTAB      0      0      192.168.58.150:22                 192.168.58.1:50298              1 ESTAB      0      0      192.168.58.150:22                 192.168.58.1:50395              1 ESTAB      0      36     192.168.58.150:22                 192.168.58.1:50394              1 LISTEN     0      100      [::1]:25                    [::]:*                  1 LISTEN     0      100    127.0.0.1:25                       *:*                  1 LISTEN     0      128          *:111                      *:*                  1 LISTEN     0      128       [::]:111                   [::]:*                  1 LISTEN     0      128    127.0.0.1:631                      *:*                  1 LISTEN     0      128      [::1]:631                   [::]:*                  1 LISTEN     0      128          *:22                       *:*                  1 LISTEN     0      128       [::]:22                    [::]:*                  1 LISTEN     0      5      192.168.122.1:53                       *:*                  1 State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              

15.4.2 cut、tr 命令

cut 命令 小awk

功能:将<每个文件>中<每一行>的<匹配部分>打印到<标准输出>。

语法:cut OPTION… [FILE]…

注意:字符是一个整体,一个字符可能占一个字节,也可能占几个字节。a是字符也是字节

汉字是字符,但一个汉字不止占一个字节

编码utf -8 的情况下,一个汉字占3个字节

常用选项:

-b, --bytes=LIST       		## 按<字节>进行选择,不承认<分隔符>
-n               			## 和 -b选项 一起使用:不分割<多字节字符>,如:汉字
-c, --characters=LIST     	## 按<字符>进行选择,不承认<分隔符>
-f, --fields=LIST       	## 按<字段>进行选择,承认<分隔符>,以<分隔符>区分<字段>## 除非指定了-s选项,否则,还会打印任何不包含<分隔符>的<行>
-s, --only-delimited    	## 不打印没有包含<分界符>的<行>
-d, --delimiter=DELIM 		## 设置单个<自定义分隔符>,需使用<单引号 或 双引号>## 默认为Tab制表符## 连续多个<分隔符>,只有第1个有效,剩余的视为<普通字符>## 只能和 -f选项 一起使用--complement               	## 补全选中的<字节、字符、字段>
--output-delimiter=TRING  	## 使用指定的字符串作为输出分界符,默认采用输入的分界符## 针对 -b,-c,-f 这三个选项,只能使用其中的一个
## <输入顺序>将作为<读取顺序>,<每行字符串>仅能输入一次
## 每一个<LIST列表>,都是一个<范围值>,可以使用<,逗号>来分隔<多个范围值>
## <范围值>的格式如下:N   				## 仅仅<第N个>的<字节、字符、域>
N-  				## 从<第N个>到<行尾>之间(包括<第N个>)的所有<字节、字符、字段>
N-M 				## 从<第N个>到<第M个>之间(包括<第M个>)的所有<字节、字符、字段>
-M  				## 从<第1个>开始到<第M个>之间(包括<第M个>)的所有<字节、字符、字段>
N1,N2,N3,N-,N-M,-M ...	## 可以使用<,逗号>来分隔<多个范围值>

如果没有<文件参数>,或者<文件>不存在时,则从<标准输入>读取

这说明:cut 命令 可以接收 | 管道符

测试:按<字节>进行选择,不承认<分隔符>

echo -e "123456789\nabcdefghi\nABCDEFGHI" > /tmp/1.txt
cut -b 5 /tmp/1.txt      ## 只输出每一行的<第5个字节>
cut -b 2-5 /tmp/1.txt    ## 输出从<第2个字节>到<第5个字节>之间的内容
cut -b 5- /tmp/1.txt     ## 输出从<第5个字节>到<行尾>之间的内容
cut -b -5 /tmp/1.txt     ## 输出从<第1个字节>到<第5个字节>之间的内容
cut -f 1 -d ":" passwd	 ## 输出每行的第一个字段

2 tr 命令 小sed

功能:从<标准输入>中替换、缩减、删除<字符>,并将<结果>写到<标准输出>。

特点:是针对<单个字符>在<SET1字符集>中进行<匹配操作>,然后通过<SET2字符集>进行<位置映射>的<替换操作>,

​ 而不是针对<整个字符串>进行<相关操作>。

语法:tr [选项]… SET1 [SET2]

常用选项:

-c, -C, --complement  	## 取代所有不属于<SET1字符集>的字符
-d, --delete      		## 删除所有属于<SET1字符集>的字符 tr -d 任何内容转为一行输出
-s, --squeeze-repeats   ## 去重把<连续重复的字符>以<单独一个字符>表示
-t, --truncate-set1     ## 参照<SET2字符集>的个数,先截短<SET1字符集>,然后执行<替换操作>
--help       		    ## 显示此帮助信息并退出
--version      	        ## 显示版本信息并退出

<SET1字符集>:指定要替换或删除的<原字符集>,一旦匹配了<原字符集>,则对<该字符>予以<相关处理>,否则,

​ 就予以保留。

​ 当执行替换操作时,必须使用参数<SET2字符集>指定替换的<目标字符集>。

​ 但执行删除操作时,不需要参数<SET2字符集>。

<SET2字符集>:指定要替换成的<目标字符集>

<SET1字符集>和<SET2字符集>都是一组<字符串>,一般都可按照字面含义理解。

基本原理图:

echo "0123456789" | tr "9876543210" "12345"  ## 输出结果:5555554321

在这里插入图片描述

如果第二行没有第一行的内容,直接不变写入,

如果第二行存在第一行的内容,相当于把第二行的内容替换成第三行的

如果有第二行但是第三行没有替换对应的,则替换成第三行最后一个字符

语法练习

echo -e "112233445566778899\nabcdefghiABC\nABCDEFGHIabc" > /tmp/1.txt
cat /tmp/1.txt | tr "a-c" "A-C"    	## 针对每一行,将<a,b,c>替换为<A,B,C>,然后执行<标准输出>
cat /tmp/1.txt | tr "0-9" "1"       ## 针对每一行,将<所有数字>替换为<1>,然后执行<标准输出>
cat /tmp/1.txt | tr "a-zA-Z" "9"  	## 针对每一行,将<所有字母>替换为<9>,然后执行<标准输出>
cat /tmp/1.txt | tr -d "a-zA-Z"   	## 针对每一行,将<所有字母>删除,然后执行<标准输出>
cat /tmp/1.txt | tr -s "0-9"        ## 针对每一行,去除所有重复的<数字>,然后执行<标准输出>
cat /tmp/1.txt | tr -t "abc" "12"  	## 针对每一行,参照<SET2字符集>的个数,先截短<SET1字符集>,然后执行<替换操作>## 即:SET1=abc,SET=12(长度为2个字符),因此,只将<a,b>替换<1,2>
cat /tmp/1.txt | tr -c -d 1-4,'\n',a-b  	## 针对<每一行>,删除不是<1,2,3,4,a,b,\n换行符>的<其他字符># 大小写转换
echo "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | tr "A-Z" "a-z"
# 加密解密
echo "123456" | tr "0123456789" "987654321"   ## 加密,加密输出结果为:876543
echo "876543" | tr "987654321" "0123456789"   ## 解密,解密输出结果为:123456

15.4.3 read 命令脚本常用

功能:接收<键盘输入>,并保存到<指定的变量>中,即作用于输入变量定义,类似于c语言的scan,c ++ 的cin

注意:read命令不能在<while read 循环语句>中使用

选项:

-i "string"   	## 缓冲区文字,用于设置<默认输入值> 
-e       		## 交互式输入,允许<backspace键>回退
-p "string"   	## 设置<提示输入的说明文字> 必有  ep 一般一起使用
-s       		## 不显示<输入内容,包括:换行符>(常用于输入密码) 可以设置密码	
-a array_name  	## 将<word单词>赋值给<array_name数组变量>,<数组下标>从 0 开始 一般和 for循环使用,和-ep 混用要注意
-r       		## 不允许<反斜杠>转义<任何字符>

例子

-i

read -i "/usr/local/nginx" -ep "请您输入 nginx 的安装目录(例如 /usr/local/nginx):" a
## 会显示  请您输入 nginx 的安装目录(例如 /usr/local/nginx):/usr/local/nginx

vim 1.sh

#!/bin/bash
read  -p "请输入您要创建的文件名:" g
touch $g#!/bin/bash
read -e "txt" -p "请输入您要创建的文件名:" g		## 输错时候,允许回退   -e 
touch $g#!/bin/bash	
read -e -i "txt" -p "请输入您要创建的文件名:" g	## 必须输入有txt的才可以  -i
touch $g## 将输入的<参会的人员名单列表>依次保存在<dj 数组变量>中  
read -e -a dj -p "请输入<参会的人员名单列表>:"
echo ${dj[*]}

read -a例子

vim 1.sh
#!/bin/bash# 提示用户输入一行以空格分隔的字符串
echo "请输入一行以空格分隔的字符串:"
read -a array# 打印数组的每个元素
echo "数组的元素如下:"
for element in "${array[*]}"
doecho "$element"
donebash 1.sh aa bb cc  会打印 aa bb cc 一行打印一个
#!/bin/bash
echo "您可以一次创建一个或多个目录,用空格隔开"
read -ep "创建的共享目录名是(如test1):" -a gx
for i in ${gx[*]}
domkdir -p /$i
done随便输入创建 1个 或 多个目录

以 , 分隔例子

#!/bin/bashdir_paths="/,/go-data,/alidata,/k8s-alidata" # 字符串IFS=',' read -r -a dir_array <<< "$dir_paths"# 遍历每个目录for dir_path in "${dir_array[@]}"; doecho $dir_path# 使用 df 命令获取磁盘使用率,并提取使用率百分比# df_used=$(df -h "$dir_path" | awk 'NR==2 {print $5}' | sed 's/%//')# echo "$dir_path: $df_used%"done

15.4.4 sleep wait 命令

1 sleep 命令 & 把要运行的内容脚本等等 甩后台运行

​ 功能:暂停一段时间,时间单位可以是 s m h d,默认是 s(秒)

举例:

sleep 10s 				## 暂停 10 秒,直接作用不了
sleep 10s &				## 在后台暂停 10 秒

2 wait 命令

功能:

wait命令 是用来阻塞<当前进程>的执行,直至<当前进程的指定子进程>或者<当前进程的所有子进程>返回状态为0,执行结束之后,方才继续执行。

使用 wait命令 可以在<bash脚本>的<多进程>执行模式下,起到一些<特殊控制>的作用。

注意:wait命令 仅仅针对<当前父进程>的<子进程>方可生效!

语法:wait [进程号 或 作业号],例如:wait 或 wait 23 或 wait %1

备注:

wait 不带<任何的进程号或作业号> ,则会阻塞<当前进程>,直至<当前进程的所有子进程>全部执行结束

wait 23 阻塞<当前进程>,直至<指定的PID子进程>执行结束

wait %1 阻塞<当前进程>,直至<指定JOB作业任务>执行结束

#!/bin/bash
echo "当前父进程PID号 = $$"
echo "当前开始时间是:$(date)"
sleep 60 &
echo "该 sleep 60 子进程PID号 = $!"
sleep 120 &
echo "该 sleep 120 子进程PID号 = $!"
wait
echo "当前结束时间是:$(date)"

15.5.5 dirname、basename

dirname 功能:输出文件路径的根路径到父路径

basename 功能:输出文件路径的文件名

dirname  1/2/3/4/5		## 输出1/2/3/4
basename 1/2/3/4/5  	## 输出5	无论有没有该文件路径都会这样输出

15.5.6 xargs 命令

功能:xargs命令 可以通过<| 管道符>接受<字符串>,并将<接收到的字符串>依据<默认空格>分割成<许多参数>,然后,将<这些参数>作为<命令行参数>传递给<后面的命令>,<后面命令>则可以使用这些<命令行参数>来执行。

作用:管道符 | 对于不支持标准输入的命令(比如 rm ),无法传输文件,使用该命令即可 例如:

echo “1.txt” | rm -rf   ## 操作失败,因为 rm 无法感知 1.txt 是文件名还是字符串
echo "1.txt" |xargs rm	## 成功删除当前目录下的1.txt文件

选项:

-d   		## 指定分隔符,不写的情况下默认是以 空格(包括制表符等等)为分隔符
echo '11@22@33' | xargs -d '@' echo		## 指定 @ 为分隔符 输出 11 22 33
-p			## 输出<即将要执行的完整命令>,询问<是否执行>  可拿来查看将要输出什么但是不输出
-n			## 设置<每次传递>的<命令行参数>的<个数>,可以<分批传输,分批执行>
echo '11@22@33@44@55' | xargs -n 2 -d '@' echo  ## 11 22 33 44 55 将分成<两次>传送,分成<三次>执行-E 			## 仅仅传递<指定命令行参数>之前的<命令行参数>
echo '11 22 33 44' | xargs -E '33' echo			## 输出 11  22 如果加 -p 就会询问## 注意:【-E选项】不能和【-d选项】一起使用,如果一起使用,则【-E选项】无效。即便【-d选项】指定的是<空格>也是如此。                

15.5 shell 算数计算

15.5.1 计算简单格式

1 整数计算 ((…)) 和 let " " $[ ]必须使用该格式才可以进行计算和相关操作

使用 + - * / % < > 需要使用该格式 ,专门使用拿来定义表达式

n=1;((n=n+1));echo $n			## 输出2
n=1;let n-n+1;echo $n			## 输出2   let是内嵌命令let "x=10, y=5, z=x+y"				
echo $z							## 输出 15((x=100/10))   let "x=100/10"   x=$[100/10] 	## 将100/10赋值给x
例子:
[root@dj tmp]# ((x=100/10))
[root@dj tmp]# echo $x
10
[root@dj tmp]# ((x=100/5))
[root@dj tmp]# echo $x
20[root@dj tmp]# let "x=100/10"
[root@dj tmp]# echo $x
10
[root@dj tmp]# let "x=100/5"
[root@dj tmp]# echo $x
20[root@dj tmp]# x=$[100/10]
[root@dj tmp]# echo $x
10
[root@dj tmp]# x=$[100/5]
[root@dj tmp]# echo $x
20

2 浮点数计算 bc 外部命令

yum -y install bc				## 外部命令要安装
echo "10/3"						## 输出 10/3
echo "10/3" | bc				## 输出 3 默认是整数
echo "scale=2;10/3" | bc		## 输出 3.33    scale 是指定 输出小数后多少位,默认是0,即输出整数
echo "scale=3;10/3" | bc		## 输出 3.333

运算速率: [let命令] 等价于 ((…)) ;大于 [bc命令]

15.5.2 算数计算表达式:操作符(重要)

有 a++ a-- ++a --a + - * / % **(幂)

! 逻辑取反

let "x=1, y=2, z=(x<y)"			## 判断语句
echo $z							## 输出 1  ,布尔类型中 1 代表正确let "x=1, y=2, z=!(x<y)"		## 判断语句
echo $z							## 输出 0  ,布尔类型中 0 代表错误

~ 按位取反

let "x=2, y=~x" 		## y=~x 就是:按位取反
echo "y=$y" 			## 计算结果:y=-3

<< 和 >> 对应的二机制数移位

let "x=1, y=x<<1" 			 ## x<<1 就是:按位左移1位   二进制 01 左移变成 10 ,变回十进制就是 2
echo "y=$y" 		    	 ## 计算结果: y=2  输出  y=2而不是输出2[root@dj tmp]# let "x=6, y=x>>1"	## 右移动  二进制 110 右移变成 11  即 3
[root@dj tmp]# echo $y
3

& 逻辑与 | 逻辑或 ^ 逻辑异或 都是二进制的对比

&  						## 双 1 为 1 ,否则为 0
|  						## 有 1 为 1 ,全 0 为 0
^  						## 相同为 0 ,不同 为 1  
let "x=(10^12)"			## 二进制分别是 1010  1100  得到 0110 即 6
echo $x    				## 输出 6

<赋值>操作符 : =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=

=     	## 例句:let "x=1+2"
*=    	## 例句:let "x*=2"  等价于 let "x=x*2"
/=    	## 例句:let "x/=2"  等价于 let "x=x/2"
%=    	## 例句:let "x%=2"  等价于 let "x=x%2"
+=    	## 例句:let "x+=2"  等价于 let "x=x+2"
-=    	## 例句:let "x-=2"  等价于 let "x=x-2"
<<=		## 例句:let "x<<=2" 等价于 let "x=x<<2"
>>=   	## 例句:let "x>>=2" 等价于 let "x=x>>2"
&=    	## 例句:let "x&=2"  等价于 let "x=x&2"
^=    	## 例句:let "x^=2"  等价于 let "x=x^2"
|=    	## 例句:let "x|=2"  等价于 let "x=x|2"

15.6 shell 条件判断

1 算数条件判断表达式

<, <=, >, >= 		  ## 小于,小于等于,大于,大于等于
==, != 				  ## 等于,不等于expr1?expr2:expr3 	  ## 根据<条件判断>结果,来予以<赋值>if expr1; then expr2 ; else expr3 ; fi
expr1 && expr2 || expr3

意思是:如果怎么样,那么就怎么样,否则就这样

((x=($a<$b)?$a*10:$b*100))		## 如果 $a小于$b,则 x=$a*10,否则 x=$b*100 ## 三目运算符
a=1
b=2
echo $x							## 测试发现 输出 10

2 字符条件判断表达式

<整数>判断(正负整数)

-eq 	## 等于  ,如:arg1 -eq arg2
-ne 	## 不等于 ,如:arg1 -ne arg2
-gt 	## 大于  ,如:arg1 -gt arg2
-ge 	## 大于等于,如:arg1 -ge arg2
-lt 	## 小于  ,如:arg1 -lt arg2
-le 	## 小于等于,如:arg1 -le arg2

<字符串>判断

-z string 			## 判断<字符串>长度是否为<0字节>?(长度为<0字节>,则为true真)
str=""; if [[ -z $str ]]; then echo "true"; else echo "false"; fi    	
## 显示结果:ture
str="123"; if [[ -z $str ]]; then echo "cxk"; else echo "ikun"; fi 		
## 显示结果:ikun注意:中括号之间一定要加空格string  		## 判断:<字符串>长度是否为<非0字节>?(长度为<非0字节>,则为true真)
-n string 		## 判断:<字符串>长度是否为<非0字节>?(长度为<非0字节>,则为true真)## 备注:-n 可以省略
str=""; if [[ -n $str ]]; then echo "true"; else echo "false"; fi   	
## 显示结果:false
str="123"; if [[ $str ]]; then echo "true"; else echo "false"; fi    	
## 显示结果:truestring1 = string2 	## 判断:是否相同?(如:str01□=□str02,str01□==□str02)## =等于符号 是为了确保与【POSIX系统】的兼容一致性(在test命令中应该用 =)
string1 == string2 	## 判断:是否相同?(如:str01□=□str02,str01□==□str02)    	
string1 != string2 	## 判断:是否不相同?(如:str01□!=□str02)
string1 < string2 	## 判断:是否在string2之前?(注意:按字典排序)(如:str01□<□str02)
string1 > string2 	## 判断:是否在string2之后?(注意:按字典排序)(如:str01□>□str02)

3 正则条件判断表达式

<正则条件表达式>:<操作符>

=~ 扩展正则表达式 ## 判断:是否匹配<扩展正则表达式>

注意:

​ 在 =~ 扩展正则表达式 中,=~ 左右两边各有一个<空格>

​ 在 =~ 扩展正则表达式 中,<扩展正则表达式>不要使用<""双引号>和<''单引号>

​ 在 =~ 扩展正则表达式 中,如果需要匹配<空格>,则需要使用<\转义符>

以下是扩展正则表达式

n="1.1.1.1"; [[ $n =~ ^([0-9]{1,3}(\.|$)){4} ]] && echo "符合" || echo "不符合"
n="1.1.1.1"; if [[ $n =~ ^([0-9]{1,3}(\.|$)){4} ]];then echo "符合"; else echo "不符合"; fi
## 显示结果:符合n="1..1.1"; [[ $n =~ ^([0-9]{1,3}(\.|$)){4} ]] && echo "符合" || echo "不符合"
n="1..1.1"; if [[ $n =~ ^([0-9]{1,3}(\.|$)){4} ]];then echo "符合"; else echo "不符合"; fi
## 显示结果:不符合   ..不符合要求n="1.1.1.1111"; [[ $n =~ ^([0-9]{1,3}(\.|$)){4} ]] && echo "符合" || echo "不符合"
n="1.1.1.1111"; if [[ $n =~ ^([0-9]{1,3}(\.|$)){4} ]];then echo "符合"; else echo "不符合"; fi
## 显示结果:不符合    1111不符合要求,范围是 0 到 999n="1.1 1.1.1"; [[ $n =~ ^([0-9]{1,3}(\.|$)){4} ]] && echo "符合" || echo "不符合"
n="1.1 1.1.1"; if [[ $n =~ ^([0-9]{1,3}(\.|$)){4} ]];then echo "符合"; else echo "不符合"; fi
## 显示结果:不符合		5个数不符合要求,最多四个str="/testdir/subdir"; if [[ $str =~ ^~?/ ]]; then echo "true"; else echo "false"; fi
## 显示结果:true				  ##这里的~表示家目录,0或1个以 ~ 开头,然后是 / ,满足条件
str="testdir/subdir"; if [[ $str =~ ^~?/ ]]; then echo "true"; else echo "false"; fi
## 显示结果:false

注意:

grep 命令 既支持<扩展正则表达式>,又支持<Perl正则表达式>!

if 命令 =~ 仅支持<扩展正则表达式>,不支持<Perl正则表达式>!

使用grep来检验:<文件路径或目录路径>的<规范性>

要求1 可以且只能:以【/斜杠】开头或结尾,所有【/斜杠】不能重复,设计<Perl正则表达式>为:^/?

要求2 两个【/斜杠】之间,不能存在:【/斜杠】、【空格】、【Tab符】、【'或"单双引号】,设计<Perl正则表达式>为:

^(/?[^/\s\"\']+)+/?$			## 第二个 ^ 是取反的不匹配  \" 表示 "

要求3 如果路径中需要包含<空格>,则必须:在<整个路径的最外层>添加【""双引号】,设计<Perl正则表达式>为:

^[\"]{1}(/?[^\"\']+)+/?[\"]{1}$

注意:下面的写法实际上输出不是以 ” ” 开头和结尾,所以不要被后面的匹配 ‘’ ‘’ 给骗了

str="/testdir/subdir"
echo $str | grep -oP "(^(/?[^/\s\"\']+)+/?$|^[\"]{1}(/?[^\"\']+)+/?[\"]{1}$)" && echo "匹配" || echo "不匹配"
## 显示结果:匹配str="  /testdir/subdir"  ## <赋值语句>会自动去掉<字符串>前后的<空格>
echo $str | grep -oP "(^(/?[^/\s\"\']+)+/?$|^[\"]{1}(/?[^\"\']+)+/?[\"]{1}$)" && echo "匹配" || echo "不匹配"
## 显示结果:匹配    是匹配前面的   而不是匹配后面的str="/test  dir/subdir"
echo $str | grep -oP "(^(/?[^/\s\"\']+)+/?$|^[\"]{1}(/?[^\"\']+)+/?[\"]{1}$)" && echo "匹配" || echo "不匹配"
## 显示结果:不匹配str="/testdir//subdir"
echo $str | grep -oP "(^(/?[^/\s\"\']+)+/?$|^[\"]{1}(/?[^\"\']+)+/?[\"]{1}$)" && echo "匹配" || echo "不匹配"
## 显示结果:不匹配str="\"/test  dir/sub dir\""
echo $str | grep -oP "(^(/?[^/\s\"\']+)+/?$|^[\"]{1}(/?[^\"\']+)+/?[\"]{1}$)" && echo "匹配" || echo "不匹配"
## 显示结果:匹配str="\"/test  dir/sub dir/\""
echo $str | grep -oP "(^(/?[^/\s\"\']+)+/?$|^[\"]{1}(/?[^\"\']+)+/?[\"]{1}$)" && echo "匹配" || echo "不匹配"
## 显示结果:匹配  这才是 匹配后面的  以双引号开头,双引号结尾

4 多个条件判断表达式

组合符

&& 逻辑与

|| 逻辑或

示例1:组合使用多个<算数条件表达式>

(((1<2)&&(2<3)))    或者     [[ ( 1 < 2 ) && ( 2 < 3 ) ]]
echo $?          ## 结果显示:<算数条件表达式>的<状态返回值>为<0>  判断上条命令运行结果,正确返回 0,错误返回1  和布尔类型相反
(((2<1)||(2<3)))
echo $?          ## 结果显示:<算数条件表达式>的<状态返回值>为<0>

示例2:组合使用多个<字符条件表达式>

[[ "a" == "a" ]] && [[ "b" == "b" ]]
echo $?          ## 结果显示:<字符条件表达式>的<状态返回值>为<0>
[[ "a" == "a" ]] || [[ "a" == "b" ]]
echo $?          ## 结果显示:<字符条件表达式>的<状态返回值>为<0>

5 test 条件判断命令(判断文件)

这里可以拿来判断文件是否存在,是什么类型的文件,是否为空文件等等

test 和 [[ ]] 用法一样

1 判断:任意类型的文件(Linux一切皆文件)

-a filename 			## 如果filename存在,不管文件是什么类型,则为true真
-e filename 			## 如果filename存在,不管文件是什么类型,则为true真
-s filename 			## 如果filename存在,并且是<非空文件(长度 > 0字节)>,不管文件是什么类型,则为true真
[[ -a /etc/passwd ]]  或者   test -a /etc/passwd
比如:
if test -e filename; thenecho "文件存在"
fi# 或者使用 [[ ]]
if [[ -e filename ]]; thenecho "文件存在"
fi

2 判断:特定类型的文件(Linux一切皆文件)

-f filename 		## 如果filename存在,且为<常规文件>,则为true真
-d filename    		## 如果filename存在,且为<目录>,则为true真
-b filename    		## 如果filename存在,且为<块文件>,则为true真
-c filename			## 如果filename存在,且为<字符设备文件>,则为true真
-S filename 		## 如果filename存在,且为<socket套接字文件>,则为true真
-p filename 		## 如果filename存在,且为<命名管道文件>,则为true真
-h filename 		## 如果filename存在,且为<符号链接文件>,则为true真
-L filename 		## 如果filename存在,且为<符号链接文件>,则为true真## 注意:<符号链接文件>不是<硬链接文件>

3 判断:文件的属性(Linux一切皆文件)

-r filename 		## 如果针对filename,<当前用户>拥有<r读权限>,则为true真
-w filename 		## 如果针对filename,<当前用户>拥有<w写权限>,则为true真
-x filename 		## 如果针对filename,<当前用户>拥有<x可执行权限>,则为true真
-u filename 		## 如果filename设置了<SUID权限位>,则为true真
-g filename 		## 如果filename设置了<SGID权限位>,则为true真
-k filename 		## 如果filename设置了<SBIT权限位>,则为true真
-O filename 		## 如果filename的<所有者>是<当前用户>,则为true真
-G filename 		## 如果filename的<所有者>是<当前用户>的<主要组>,则为true真
-N filename 		## 如果filename在<最后一次读取>之后被修改,则为True真
-t fd 				## 如果<fd文件描述符>关联着一个<终端>,则为True真例如:test -t 0      ## 文件描述符0:是关联着<终端>的<标准输入>
例如:test -t 1      ## 文件描述符1:是关联着<终端>的<标准输出>
例如:test -t 2      ## 文件描述符2:是关联着<终端>的<标准错误输出>

4 判断:文件的对比(Linux一切皆文件)

file01 -nt file02 		## 如果file01比 file02新,则为true真
file01 -ot file02 		## 如果file01比 file02旧,则为true真
file01 -ef file02 		## 如果file01和file02是同一个文件,则为true真## 主要是依据file01和file02各自的inode编号,来判断是否为<硬链接文件/软连接文件>?

5 判断:字符串 STRING 代表字符串,不是特指

-z STRING 			## 判断:<字符串>是否为<空>?   
-n STRING 			## 判断:<字符串>是否为<非空>?
STRING1 = STRING2 	## 判断:<字符串>是否为<相等>?
STRING1 != STRING2 	## 判断:<字符串>是否为<不相等>?
STRING1 < STRING2 	## 基于<字典>来判断:<字符串1>是否小于<字符串2>?
STRING1 > STRING2 	## 基于<字典>来判断:<字符串1>是否大于<字符串2>?

6 判断:数值

arg1 OP arg2		## 比较:arg1和arg2这两个<算数值>## OP是<操作符>,取值范围是:-eq等于、-ne不等于、-lt小于、-le小于等于、-gt大于、-ge大于等于。

7 判断:其他

-o OPTION 			## 如果指定的<set内建的Shell选项>已经<启用>,则为true真
例如:
[ -o history ] && echo $?  ## 判断是否启用了<set内建的Shell选项:history功能>?
-v VAR 					   ## 如果一个<VAR变量>已被<定义并赋值>,则为true真

8 逻辑操作

! EXPR ## 执行:逻辑取反

EXPR1 -a EXPR2 ## 执行:逻辑与

EXPR1 -o EXPR2 ## 执行:逻辑或

举例:

[ -d /tmp -a -d /etc ] && echo $?
[ -d /tmp -o -d /dir01 ]  && echo $?

6 [ ] 和 [[ ]] 的区别

建议用 [[ ]]

[ ] :它是一个<命令>,是<test 内嵌命令>的<简写形式>。
[[ ]] :它不是一个<命令>,只是一个中的<关键字>,[[ ]] 结构 比 [ ] 结构 更加通用。

[[ ]] 用法:里面每一个参数都要隔开,严格按照格式,不能紧贴着,包括不能贴着两边

双中括号[[]]是bash shell中的关键词,相对于[]有以下特点和优势:

[[ ]] 结构比[ ]结构更加通用。在[[]]之间所有的字符都不会发生文件名扩展或者单词分割,但是会发生参数扩展和命令替换。支持字符串的模式匹配,使用=~操作符时甚至支持shell的正则表达式。字符串比较时可以把右边的作为一个模式,而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真。[[ ]] 中匹配字符串或通配符,不需要引号。使用[[ ... ]]条件判断结构,而不是[... ],能够防止脚本中的许多逻辑错误。比如,&&||<> 操作符能够正常存在于[[ ]]条件判断结构中,但是如果出现在[ ]结构中的话,会报错。bash把双中括号中的表达式看作一个单独的元素,并返回一个退出状态码。实际上,原理就是,在双中括号中,可以使用=~进行shell的正则匹配,找出右边的字符串是否在左边的字符串中。但是需要注意的是,双中括号在bash中可以使用,在sh中会报找不到的错,这是一个大坑。

7 if 条件判断语句

注意:if 之后判断一次,不能直接用来做循环,循环得用 for 和 while

1 语法

语法1:

if 条件; then 操作; [elif 条件; then 操作;] ... [else 操作;] fi
注意: elifelse  如果不需要可以不写,看条件选一个完成,完不成都选择

语法2 :

if 条件; then操作
[elif 另外满足的条件; then操作]
... ...
[else操作]
fi

语法3 : 常用

if 条件
then操作
elif 条件
then操作
... ...
else操作
fi
注意: ifelif 都需要加 then   ,else 不需要加 then  ,开头 if  结尾   fi
2 工作原理

按序依次检查:<if COMMANDS;语句块>和<elif COMMANDS;语句块>的<状态返回值>

如果 <状态返回值> = 0 ,则执行:<then COMMANDS;语句块>

如果 <状态返回值> = 非0,则执行:<else COMMANDS;语句块>

3 例子

以下例子全用脚本来写

输入1就1,输入2就2 输入别的就空格

#!/bin/bash
read -e -p "请输入一个数字:" a
if (( $a==1 ))
thenecho "你输入的是1"
elif (( $a==2 ))
thenecho "你输入的是2"
elseecho " "
fi
注意:这里条件判断使用 [[ ]]  也可以

字符条件判断

a="ABC"
b="CBA"
if [[ $a < $b ]]
thenecho "<a变量值>的<字符串编码数>小于<b变量值>"
elif [[ $a == $b ]]
thenecho "<a变量值>的<字符串编码数>等于<b变量值>"
elseecho "<a变量值>的<字符串编码数>大于<b变量值>"
fi

字符条件判断,,输入 no 输出yes,输入yes输出no

#!/bin/bash
read -e -p "请输入: " a
if [[ $a == "yes" ]]
thenecho "no"
elif [[ $a == "no" ]]
thenecho "yes"
elseecho "你是人才吗"
fi

定义 holiday_name 字符串变量记录节日名称 (字符串判断)

如果是 情人节 应该 买玫瑰看电影

如果是 平安夜 应该 买苹果吃大餐

如果是 生日 应该 买蛋糕

其他的日子每天都是节日啊……

#!/bin/bash
read -e -p "请输入节日名称:" holiday_name
if [[ $holiday_name == "情人节" ]]
thenecho "如果是$holiday_name应该买玫瑰"
elif [[ $holiday_name == "平安夜" ]]
thenecho "如果是$holiday_name应该买苹果"
elif [[ $holiday_name == "生日" ]]
thenecho "如果是$holiday_name应该买蛋糕"
elseecho "其他的日子每天都是节日啊……"
fi

判断是否有 /etc/passwd 文件存在 (test判断)

#!/bin/bash
if [[ -a /etc/passwd ]]
thenecho "存在"
elseecho "不存在"
fi

判断数组是否含有某变量在其中 (正则表达判断)

#!/bin/bash  
a="abc"
char_list=("bcd" "abc" "xyz")
if [[ ${char_list[*]} =~ $a ]] 			## =~ 匹配正则
then										echo "$a包含在字符串序列中"
elseecho "$a不包含在字符串序列中"
fi

8 case 条件判断命令

1 功能

根据匹配的<PATTERN模式>,有选择的执行<COMMANDS命令>

case 与 if 的不同在于:case只做一次<条件判断>,if可做多次<条件判断>

2 语法
case $a in												  case $a in
判断条件1 )													判断条件1 )操作															操作
;;														  ;&
判断条件2 )													判断条件2 )操作															操作					
;;														  ;&
……														  ……
;;														  ;&
* )														  * )操作															操作
;;														  ;&
esac													  esac#注意:case里面不需要 then ;;          ## 表示一旦匹配,则不继续往下执行<其他PATTERN模式>的<指令>;&          ## 表示即便匹配,也会继续往下执行<其他PATTERN模式>的<指令>(无论是否匹配)
3 例子

使用脚本来写,和 if 用法差不多

#!/bin/bash
read -e -p "请输入你的年龄 = " age
case $age in[0-9])echo "你是儿童";;1[0-9])echo "你是青少年";;2[0-9])echo "你正值壮年";;[3-5][0-9])echo "你步入中年了";;[6-8][0-9])echo "你是个老人了";;9[0-9]|1[0-9][0-9])echo "你是个神仙了";;*)echo "你是人还是鬼?";;
esac

输入1就1,输入2就2 输入别的显示 您真是一个大聪明

#!/bin/bash
read -e -p "请输入一个数字 " a
case $a in
1 )
echo "您输入的是1"
;;
2 )
echo "您输入的是2"
;;
* )
echo "您真是一个大聪明"
;;
esac

输入 yes 就输出no

#!/bin/bash
read -e -p "请输入: " a
case $a in
"yes")echo "no"
;;
"no")echo "yes"
;;
*)echo "你是人才吗"
;;
esac

9 ; && ||

符号功能例句
; 分号在一行中,依次执行多个命令, 不管<左边命令>是否执行成功,都将执行<右边命令>echo “ x " ; e c h o " x"; echo " x";echo"PATH”
&&条件判断:<&&左边命令>的<状态返回值>为 0(满足),方可执行<&&右边的命令>echo “KaTeX parse error: Expected 'EOF', got '&' at position 4: x" &̲& echo "PATH”
||条件判断:<||左边命令>的<状态返回值>为 非0(不满足),方可执行<||右边的命令>echo “ x " ∣ ∣ e c h o " x" || echo " x"∣∣echo"PATH”

例子

[ -d /tmp ] && rm -rf /tmp/* || echo "没有这个目录"
[ -d /tmp ] || echo "没有这个目录" && rm -rf /tmp/*## 两个意思一样:有没有/tmp 目录,有的话删除 /tmp/目录下所有文件,没有的话就输出 没有这个目录  (第一个好理解)

组合条件判断

[ -d /tmp -a -d /etc ] && echo "两个目录均存在" || echo "至少其中一个目录不存在"
[ -d /tmp -o -d /dir01 ] && echo "其中一个目录一定存在" || echo "两个目录均不存在"

15.7 shell 循环控制

15.7.1 for 循环

for 里面判断条件是 一些 数学算数判断循环,如果是字符这方面,要到 if 里面去判断

注意: for 循环 里面可以嵌套 if 和 for 循环

类似于

for ……								类似于   if [[  ]]
do											then	……											……
done										fi

语法 1 : for ((EXPR1; EXPR2; EXPR3)) ; do COMMANDS ; done

#!/bin/bash
for ((n=1;n<3;n++))
dotouch $n.txt
done
创建 1.txt  2.txt

语法 2:for 变量 [in 单词序列] ; do COMMANDS; done i为变量 for i in ……

#!/bin/bash
for a in 1 2 3 4 55			或者  for a in {1..10}
doecho "$a"
done

死循环

for ((;;)); doecho "这是一个死循环,按Ctrl + c结束"
done						## 初始值,条件,增量都不写或者for ((i=1;;i++)); doecho "这是一个死循环,按 Ctrl + c 终止"
done						## 不写循环条件或者for ((i=1;i<=10;)); doecho "这是一个死循环,按 Ctrl + c 终止"
done						## 不写增量

强制 y

#!/bin/bash
for ((;;))
doif [[ $Y != "y" ]]thenread -e -p "请输入(y/n):" Yelseexitfi
done

强制玩原神

#!/bin/bash
for ((;;))
doif [[ $Y != "玩" ]]thenread -e -p "你玩原神吗? (玩/不玩)" Yelseexitfi
done## 注意:因为要使该循环无限运行,使用要使 for 运行死循环,在使用if判断退出循环

使用ping命令去测试整个 192.168.1.0 网段,将能ping通的机器ip写到/tmp/ok.txt .将不能ping通的ip写到/tmp/no.txt (本公司网段)

将 ping 成功或者错误的信息丢进黑洞 ,每一个 ping 跑3遍即可 , 不然太慢了

#!/bin/bash
for ((a=1;a<256;a++))
doif ping 192.168.1.$a -c 3 &>> /dev/nullthenecho 192.168.1.$a >> ok.txtelseecho 192.168.1.$a >> no.txtfi
done
## -c 3 表示显示3次即可,不然ping 会一直显示在屏幕上,没学进程只能开新窗口解决
## 注意:& 要紧贴 重定向符号,表示全部信息,无论正确错误的信息都丢尽黑洞 /dev/null
## ping的过程很久,因为有 255 个ip ,可以开两个新窗口 tail -f /tmp/ok.txt 和 tail -f /tmp/no.txt 实时查看

15.7.2 while 循环

满足条件就继续循环,不满足就中止循环

for能做的while也可以,for不能的while也可以

语法:

初始值定义
while 条件
do……
done						## 最好有初始值定义,不然无法进入该循环

强制 y

#!/bin/bash
Y="n"
while [[ $Y != "y" ]]
doread -e -p "请输入(y/n):" Y
done如果用 for 循环来做,如下,需要先让for进入死循环
#!/bin/bash
for ((;;))
doif [[ $Y != "y" ]]thenread -e -p "请输入(y/n):" Yelseexitfi
done

强制玩原神

#!/bin/bash
a="随便写"
while [[ $a != "玩" ]]; doread -e -p "你玩原神吗[玩/不玩]: " a
done[root@dj tmp]# bash 3.sh
你玩原神吗[玩/不玩]: 玩
[root@dj tmp]# bash 3.sh
你玩原神吗[玩/不玩]: 不玩
你玩原神吗[玩/不玩]: 不玩

死循环

while true; doecho "OK"
done

手动创建用户,并核对二次密码

y="no"
while [[ $y == "no" ]]; doread -e -p "请输入用户名=" usernameread -e -s -p "请输入密码(不少于8个字符)=" aechoread -e -s -p "请再次输入密码(不少于8个字符)=" bechoif [[ $a == $b ]]; thenecho "OK,你本次密码设置有效"echo "$username:$a" | chpasswdy="yes"elseclearecho "NO,你的两次密码不一致,请重新输入!"y="no"fi
done

创建十个用户,分别叫user1-user5,批量设置密码,pass1-pass5
如果用户已存在则输出用户已存在,如果用户不存在则创建用户设置密码

#!/bin/bash
zh=1
while (($zh <= 10))	
doif id user$zh &> /dev/null					then										echo "用户user${zh}已经存在"    elseuseradd user$zhmm=$zhecho "user$zh:pass$mm" | chpasswdecho "用户user${zh}创建成功"echo "用户密码设置成功"fi((zh++))
done
## 如果用户存在,那么 if 会执行成功,把id user$zh 命令成功的信息写进黑洞,然后运行第一个
## 如果用户不存在,那么 if 会执行失败,把报错信息写进黑洞,然后运行第二个

计算 1+2+3+……+100 = 5050

#!/bin/bash
num=0
n=1
while ((n<=100))
do((num=num+n))((n++))
doneecho $num或者#!/bin/bash
num=0
for ((n=1;n<=100;n++))
do((num=num+n))
doneecho $num

15.7.3 while read 循环语句

语法1:遍历<标准输入>的<行>循环

COMMANDS | while read VAR; do COMMANDS; done

了解即可...

语法2:遍历<文件>的<行>循环

while read VAR; do COMMANDS; done < FILE

15.7.4 until 循环语句

直到<条件满足>,就终止循环

不满足条件就一直循环,满足就中止 和 while 相反

所以当 until 和 while 条件范围相反 ,完成的内容其实是一样的

强制 y

Y="n"
until [[ $Y == "y" ]] 
doread -e -p "请输入(y/n):" Y
done

15.7.5 continue 命令

功能:中止本次循环,但是不会中止本轮全部循环,会直接跳转到下一次循环 跳过的意思

语法: continue [N] 终止<本轮循环>

● 仅在<N层嵌套循环体>语句结构中,发挥作用,可以终止<当前循环体>的<第N层上级循环体>

● <第1层>就是<循环体自身>,即:continue 1 等同于 continue

要知道 continue和break还有exit的区别

# 打印 1 到 10 之间的奇数
#!/bin/bash
for ((i=1; i<=10; i++))
doif ((i % 2 == 0))thencontinuefiecho $i
done
##如果是偶数就不满足条件,直接中断本次循环到下一次循环判定

15.7.6 break 命令

功能:中止本轮全部循环,直接退出循环 停止的意思

语法: break [N] 终止<整个循环>

● 仅在<N层嵌套循环体>语句结构中,发挥作用,可以终止<当前循环体>的<第N层上级循环体>

● <第1层>就是<循环体自身>,即:break 1 等同于 break

和exit 区别:break之后退出该循环,但是会执行循环外面的内容

exit 直接跳出脚本,当前面使用了exit ,后面的内容直接不生效

如果循环外部后面没有内容,那exit 和 break 都可以使用

# 示例:找到第一个大于 5 的数字
#!/bin/bash
numbers=(1 2 3 4 5 6 7 8 9 10)for num in ${numbers[*]}
do# 如果数字大于 5,则打印并终止循环if [[ $num > 5 ]]thenecho "找到第一个大于 5 的数字:$num"break  #exit # 如果把break 替换为 exit ,那循环外面的内容则无用,即后面那句话不会显示fi
done
echo "break就显示,exit不显示"[root@dj ~]# bash 5.sh
找到第一个大于 5 的数字:6
break就显示,exit不显示[root@dj ~]# vim 5.sh	改成exit保存后
[root@dj ~]# bash 5.sh
找到第一个大于 5 的数字:6本来要循环10次,找到大于5的直接中止循环

15.8 sed 三剑客之一

15.8.1 命令简介

重要:可修改匹配的行或者匹配的内容返回文件,玩出花来的正则表达式

sed 是一个用于 过滤并转换 文本行 的 流编辑器

sed 和 vi 的区别:

​ vi 是<交互式>的<文本编辑器>

​ sed 是<非交互式>的<文本流编辑器>,接受<input输入字符流>,然后过滤并转换,最后输出<output输出字符流> 适合脚本和dockerfile

注意:sed 和 vi 有一些命令是通用的,比如替换 s///

​ sed 是一个基于<行>字符流,执行<整行处理>的<文本处理工具>

​ awk 是一个基于<行>字符流,精细到<列处理>的<文本处理工具>

15.8.2 工作原理

重点理解:

  1. 逐行输入、逐行处理、逐行输出。(从左到右,从上到下,一般原文件不会改变)

  2. 在<模式空间>:暂存数据、处理数据、输出数据、清空数据

在这里插入图片描述

15.8.3 sed 的运用

注意:sed 里面 替换那里不能使用 \s 做制表符,可以使用 \t ,因为 s有替换的意思 \s 会转义成s 和 s一样

功能:匹配某一行,也可以进行操作修改,或者对行内的某一些内容修改

语法:

sed [options] script filename

sed [options] -f script filename

sed 选项 ’ 地址范围和子命令(正则) ’ 文件名

注意:要修改文件的话 必须 在后面接文件名, 如果是前面用| ,那么不能修改,因为只是对前面匹配到的字符串进行操作而不是原文件

选项

-r					## 使用扩展正则
-e					## 可以同时对文件执行多个 子命令  ## 如-e 'script01' -e 'script02' -e 'script03'
-n 					## 抑制 sed命令 的标准输出,因为不使用该命令对文件操作会输出文件原内容和操作的内容
-i					## 回写 标准输出到源文件,是覆盖式回写 (修改原文件)
-i.bak				## 修改原文件之前进行备份
-f   				## 后接sed脚本,指定执行的sed脚本
\< \>				## 单词边界 ,比如 \<root\> 仅匹配root## rootab wadroot 这种不匹配,重要

-e 作用

打印pass文件的第1行和第3行
sed '1p;3p' /tmp/pass  或 sed -e '1p' -e '3p' /tmp/pass 删除第一行和最后一行
sed '1d;$d' pass打印最后一行,原理:删除第一到倒数第二行即可    sed '$!d' pass
sed '!$d' pass  和  sed '1,($-1)d' pass  都不行,必须如上所示	

-n 的作用

[root@dj ~]# sed  '' /tmp/passwd
[root@dj ~]# sed -n '' /tmp/passwdsed -n '$p' /tmp/passwd 		## 输出最后一行打印出来
sed -ni '1,5p' passwd 			## 把文件内容改成原文件内容前五行
sed -r 's/^([a-zA-Z0-9]+)([^a-zA-Z0-9])([a-zA-Z0-9]+)/\3\2\1/'		## 交换第一个和第三个单词

常用的sed 子命令

i\		      	    	##  不写\也行 在指定行新建上一行写入内容
c\						## 在指定行替换写入内容,常用,用这个进行匹配并替换整行内容
a\						## 在指定行新建下一行写入内容  追加
r\					    ## 在指定行新建下一行写入指定文件的内容 插入
比如 
sed '1a 1.txt' passwd   ## 会在passwd文件第一行下面写入 "1.txt"	
sed '1r 1.txt' passwd	## 会在passwd文件第一行下面写入文件1.txt里面的内容!						## 放在命令面前取反		
d						## 删除,写在要删除的内容的后面
p						## 打印,写在要打印的内容的后面
q						## q 匹配到 第5行就退出地址范围:
1   				## 就是处理第1行
$   				## 处理最后一行
1,5					## 处理1到5行  sed '1,5p' 1.txt
2~2 				## 从第2行开始,每隔1行  注意:2~1 相当于 2,$  ,第二行到最后一行
2~5					## 从第二行开始,每隔4行
3,+5   				## 处理3-8行或者以正则表达式匹配的作为地址范围(精确处理)
1)、/正则表达式/     一般出现 /   /    不是正则表达式 就是  替换				
如:/^root/
2)、/正则表达式1/,/正则表达式2/		表示从第一次匹配到正则表达式1开始到第一次匹配到正则表达式2之间的所有行
如:/^r/,/^s/  或者   '/^root/,/^b.*n$/p'匹配那一行改为禁用模式
sed -r -i '/^[ \t]*SELINUX=/c\SELINUX=disabled' /etc/selinux/config		
注意:如果匹配到范围开头,但是范围结尾匹配不到,那会匹配开头那行到最后一行如果匹配到范围结尾,没有匹配到开头,那就匹配不到s/被替换/替换/   s|||  s###   s@@@ 替换
g 			## 全局替换 一行的全局  vi需要在前面加 % ,这个不用,因为默认是替换所有行的第一个关键字(g写在后面)
i  			## 查找时忽略大小写
n  			## 第n个被匹配到的   n表示数字
&			## 表示将要被替换关键字   
sed -r 's/l..e/&r/' 1.txt		##  &代表前面的 l..e   .代表任意单个字符
\1  \2		## 如果被替换的内容使用了(),即正则分组进行分成几组## 那么 \1 \2 分别表示第一组和第二组
注意:g 写在后面,&\1 这些写在 替换 那里,()和别的正则 或者 地址范围写在 被替换 那里y///        ## 也是替换,但是如果找不到被替换的,会把被替换的分开出来找
s/abc/ABC/	## 把abc替换成 ABC	,前提要有 abc 整体
y/abc/ABC/	## 把a替换成A ,b替换成B,c替换成C仅将 root替换ROOT,但是rootab不会匹配到替换成 ROOTab
sed 's/\<root\>/ROOT/g' pass
注意:使用 s###的时候,如果匹配内容有 / ,那么不需要使用转义字符进行转义

分别指定第一行 写入123456 查看 i c a 的区别

[root@dj ~]# sed  '' /tmp/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin       ## 原内容,后面还有很多行省略...[root@dj ~]# sed  '1i\123456' /tmp/passwd
123456
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin[root@dj ~]# sed  '1c\123456' /tmp/passwd
123456
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin[root@dj ~]# sed  '1a\123456' /tmp/passwd
root:x:0:0:root:/root:/bin/bash
123456
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin难点:
[root@dj tmp]# sed '/^root/a\hello\nworld' /tmp/passwd    ## \n 是换行符
root:x:0:0:root:/root:/bin/bash
hello
world
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin意思:匹配以 root开头    a\ (格式,下一行新建行写入)  

查看 d p 的作用,删除第1到2行 ,从第一行开始,每隔一行进行打印 里面包括简单的地址范围

[root@dj ~]# sed  '1,2d' /tmp/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin[root@dj ~]# sed  '1~2p' /tmp/passwd
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin

地址范围难点 正则表达式

匹配以root开头的所有行并打印
[root@dj tmp]# sed -r '/^root/p' /tmp/passwd  
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin匹配以root开头那一行开始  以^b为开头那一行为结尾,打印
[root@dj tmp]# sed -r '/^root/,/^b/p' /tmp/passwd  
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin匹配从r开头那一行开始,到b开头 n结尾那一行结束并打印
[root@dj tmp]# sed -r '/^root/,/^b.*n$/p' /tmp/passwd  
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin

替换内容 难点 s/// ,如果替换内容有 / ,可以改成 s###测试

cat 1.txt	## 查看原文件内容
hello like 
my love 		[root@dj tmp]# sed -r 's/l..e/&r/' 1.txt	## &代表前面的 l..e 即like和love
hello liker
my lover[root@dj tmp]# sed -r 's/l(.)(.)e/\2\1s/' 1.txt
hello kis						## 难点:ik 和 ov满足匹配,并被分为两组			
my vos							## 将匹配的内容,替换成 正则的 第二组 + 第一组 加 r## i 是第一组 k是第2组 ,第二行的o是第一组,v是第二组删除每一行倒数第二个字符
[root@dj tmp]# sed -r 's#(.)(.)$#\2#' /tmp/passwd  
root:x:0:0:root:/root:/bin/bah
bin:x:1:1:bin:/bin:/sbin/nologn
daemon:x:2:2:daemon:/sbin:/sbin/nologn## 匹配倒数最后两个字符分两组,然后替换成第2组,第1组(倒数第二个字符)就没了将passwd文件的每一行 第二个字符和倒数第二个字符替换
sed -r -e 's/^(.)(.)(.*)(.)(.)$/\1\4\3\2\5/' passwd  ## 无-e也行,只有一个子命令注意:在这里如果要\1\2之间有空格,可使用空格键和 \t,\s不行,会和s一样在 \1 \2 之间输出s,因为s有替换的功能,\s相当于转义成s

15.9 awk 命令

15.9.1 awk 命令简介

1、awk的特点和作用
格式化文本,对文本进行比较复杂的格式处理(此格式化是变成有格式,而不是删除内容的格式化)

2、awk的程序结构(也就是语法结构)
语法:awk 选项 ‘BEGIN{命令} pattern{命令}END{命令}’ 文件名

AWK 是一种解释执行的编程语言,它非常的强大,被设计用来专门处理文本数据。

AWK 的名称是由它们设计者的名字缩写而来 —— Afred Aho, Peter Weinberger 与 Brian Kernighan。

GNU/Linux 发布的 AWK 版本通常被称之为 GNU AWK,由自由软件基金( Free Software Foundation, FSF)负责开发维护的

目前总共有如下几种不同的 AWK 版本。

​ AWK 这个版本是 AWK 最原初的版本,它由 AT&T 实验室开发。

​ NAWK NAWK(New AWK)是 AWK 的改进增强版本。

​ GAWK GAWK 即 GNU AWK,所有的 GNU/Linux 发行版都包括 GAWK,且 GAWK 完全兼容 AWK 与 NAWK。

CentOS 7 中,<awk命令>就是<gawk命令>的<符号链接文件>

★ AWK 可以做非常多的工作,下面只是其中的一小部分:

文本处理

生成格式化的文本报告

进行算术运算

字符串操作

以及其它更多

15.9.2 awk 工作原理

★ AWK 执行的流程非常简单:读(Read)、执行(Execute)与重复(Repeat)

★ AWK 分为三个功能代码块:

(1) BEGIN {…;…;…}开始块 ☚ 可选

(2) pattern {…;…;…}主体块 ☚ 必选 重要

(3) END {…;…;…} 结束块 ☚ 可选

15.9.3 awk 基本运用

1 原理

1. 了解:BEGIN {…;…;…} 开始块

注意:<BEGIN开始块>是可选的,你的程序可以没有<BEGIN开始块>。

开始块的语法: BEGIN {awk-commands}

同一行多个<awk子命令>用<;分号>隔开

功能:顾名思义,<开始块>就是在<程序启动>时的<执行代码>,并且它在<整个过程>中只<执行一次>。

一般情况下,我们在<开始块>中<初始化>一些<变量>,BEGIN 是 AWK 的关键字,因此它必须是大写的。

2 了解 pattern {…;…;…} 主体块 (重要)

主体块是必有的

主体部分的语法: pattern {awk-commands}

同一行多个<awk子命令>用<;分号>隔开

在<主体块>没有<关键字>的存在,pattern 表示<模> ,但是pattern不是关键字。(使用awk不用写,默认’ ’ 里面就是主体块)

功能:对于<每一个输入的行>都会执行一次<主体部分的命令>,但是,我们可以将其限定在指定的<pattern模>中。

<pattern模>可以是:

/正则表达式/		awk '/pattern/ {print $0}' file   ## 打印 主体块的全部内容
关系表达式		awk 'END{print $0}' file 		  ## 打印最后一行

多个 pattern模 的运用:

pattern1&&pattern2          ## 表示逻辑与(AND),既要匹配pattern1,也要匹配pattern2
pattern1||pattern2          ## 表示逻辑或(OR),或者匹配pattern1,或者匹配pattern2
pattern1?pattern2:pattern3	## 表示如果pattern1匹配为ture,则执行 pattern2匹配,否则,则执行 pattern3匹配
(pattern)                   ## 表示限定pattern的范围
!pattern                    ## 表示不匹配pattern
pattern1,pattern2           ## 表示从匹配pattern1的行开始,到匹配pattern2的行结束##  通过<正则表达式>来表示<一个字段的结尾>,可以使用<$符号>,因为awk将<每一个字段>当做<一个个的小行>。

例子

cat >1.txt <<EOF
tcp  1.2.3.4
tcp6 1.2.3.4
EOF匹配第一个字段以tcp字符串结尾的行
[root@dj tmp]# cat 1.txt | awk '$1~/tcp$/{print}'	
tcp  1.2.3.4## ~/tcp$/ 表示正则匹配以 tcp 结尾的字符串匹配第一字段()以6结尾的行
[root@dj tmp]# cat 1.txt | awk '$1~/6$/{print}'			
tcp6 1.2.3.4## ~ 表示后面正则匹配的对象是匹配前面的内容,而不是全文匹配,这里是对第一字段进行匹配

3. 了解:END {…;…;…} 结束块

注意:<END结束块>是可选的,你的程序可以没有<END结束块>。

结束块的语法: END {awk-commands} ☚ 同一行多个<awk子命令>用<;分号>隔开

顾名思义,<结束块>是在程序结束时执行的代码,并且它在整个过程中只执行一次。

END 也是 AWK 的关键字,它也必须大写。

2 awk 简单语法和例子
用法选项介绍

可以精准定义 某一行某一列的某一内容。

语法 awk ’ {print } ’ 1.txt

注意: { } 外面左边匹配行,NR匹配行号 或者 ~匹配正则,定义行 。{ }里面匹配列(字段)

语法:

awk 选项 ‘’ 文件名 注意:’ '里面写操作命令或者内置变量,比如 ‘NR==1 {print $1}’
命令 | awk 选项 ‘’
命令 | xargs awk 选项 ‘’

选项:

-F 		## 指定分割符,不写默认以空白为分隔符    常用
例如 
-F ":" ,以:为分隔符    
-F ":|/" ,以:或者 / 作为分隔符-v		## 自定义变量

内置变量:

NR 		## 指定行 默认全部行  ## 如  NR==2 文件第二行, NR > 2 第三行到结尾  {print NR}输出行号
\t  	## 是TAB键
\n  	## 是换行符
$1  	## 是内置变量,$0表示一整行,$1表示每行的第1列,$2表示每行的第2列
$0这个变量包含执行过程中当前行的文本内容NF 		 表示每一行的字段数,默认分隔符是空白  {print NF} 打印每行的字段数
$NF		 当前行最后一个字段的值		{print $NF}	打印每一行最后一个字段
$(NF-1)  当前行倒数第二个字段的值FIELDWIDTHS		## 定义每个数据字段宽度 
FS				## 输入列字段分隔符(默认是空格)
RS				## 输入行分隔符(默认是换行符\n)
OFS				## 输出列字段分隔符(默认值是空格),可替换分隔符输出,不能修改源文件
ORS				## 输出行分隔符(默认值是换行符\n)

例子: 不常用

OFS:  也是修改分隔符,不过在BEGIN 里面使用 例如 把 :作为分隔符,并改成 @ 显示出来
[root@dj tmp]# awk -F ":" 'BEGIN{OFS="@"} {print $1,$2,$4}' passwd
root@x@0
bin@x@1
daemon@x@2if (条件表达式){动作}else if(条件表达式){动作}else{动作}
awk -F ":" '{if($3==0){print $0" 这是root用户"}else if(($3>0)&&($3<1000)){print $0" 这是系统用户"}else{print $0" 这是普通用户"}}' /etc/passwd案例:
awk '{max=$1;for(i=2;i<=4;i++){if($i>max){max=$i}}print max}' /tmp/number
awk  -F : 'NR==1{for(i=1;i<=NF;i++){print $i}}' /etc/passwd
1 主体用法案例

下面语法都是只使用了 主体 而没有使用 BEGIN 和 END

例子 自定义变量

用户自定义变量
定义变量和引用变量,均不需要加$符号(推荐使用小写)
例子:
[root@exercise1 opt]# awk 'BEGIN{var="1"}{print var}' a2.txt
1
1   
使用选项定义变量
[root@exercise1 opt]# awk -v var=1 '{print var}' a2.txt
1
1
## 有多少行就输出多少个1,在主体里面有多少行 就打印多少次打印passwd行数
awk '{n++;}END{print n}' /etc/passwd
或者
awk -v n=0 '{n++}END{print n}' passwd

例子 NR

打印passwd文件的奇数行和偶数行  还有行号显示
awk 'NR%2==0{print NR,$0}' /tmp/passwd		## 偶数行 括号里面的 NR 表示打印行号
awk 'NR%2!=0{print NR,$0}' /tmp/passwd		## 注意:$0 是打印全部内容把第一行的字段换行显示出来
awk  -F : 'NR==1{for(i=1;i<=NF;i++){print $i}}' /etc/passwd

例子 -F 和 print 还有 printf

df -h | awk '{print $5}' 				## 输出每一行的第五字段的内容
awk -F ":" '{print "随便输出"}' passwd	 ## 输出每一行,写着 随便输出 ,有多少行就输出多少个 随便输出
awk -F ":/" '{print $1}' passwd			## 输出以 :/ 作为分隔符的第一个字段
awk -F ":|/" '{print $1}' passw			## 输出以 : 或者 / 为分隔符 的第一字段## 注意:如果两个分隔符连在一起,那么会假设两分隔符之间的内容是空白,会输出空白行指定 df -h 的第6行 第5字段(列),默认是空格为分隔符
[root@dj tmp]# df -h | awk 'NR==6{print "你的根分区用了"$5}' 
你的根分区用了0%			大于第2行的每一行的第5字段打印出来
df -h | awk 'NR>2{print "你的根分区用了"$5}'  自定义第1字段占15字符左对齐,第7字段左对齐
awk -F ":" '{printf "%15-s %-s\n",$1,$5}' passwd    ## 类似于c语言输出, %s 表示 十进制整数 %-s 表示左对齐  %8-s 表示左对齐,且占8个字符位置

查工资

[root@dj tmp]# cat 1.txt 
工资条
姓名     工号   银行卡号   基本工资       绩效      五险一金   
小明     121   45678789   3500        5000      -1000      
小小明    123   45678987   2500        3000      -800      
[root@dj tmp]# awk 'NR>2{print $1"的工资是"$4+$5+$6}' 1.txt
小明的工资是7500
小小明的工资是4700
2 BEGIN+主体+END 结合使用案例
## 创建一个名为 test.txt 的文件(其中包括序列号、学生名字、课程名称与所得分数)。
cat > /tmp/test.txt <<EOF
1)    Amit     Physics    80
2)    Rahul    Maths      90
3)    Shyam    Biology    87
4)    Kedar    English    85
5)    Hari     History    89
EOF[root@dj ~]# awk '($4>=80)&&($4<86){print}' /tmp/test.txt 
1)    Amit     Physics    80
4)    Kedar    English    85## 输出<文件中的内容>,同时输出<表头信息>即输出  序号  姓名  课程  分数
[root@dj ~]# awk 'BEGIN {printf "序号\t姓名\t课程\t分数\n"} {print $1"\t"$2"\t"$3"\t"$4} END {print "###打印结束###"}' /tmp/test.txt
序号	姓名	课程	分数
1)	Amit	Physics	80
2)	Rahul	Maths	90
3)	Shyam	Biology	87
4)	Kedar	English	85
5)	Hari	History	89
###打印结束###示例6:##pattern1?pattern2:pattern3(条件表达式)
条件?满足就做这个:不满足就做这个
## 显示Amit同学成绩大于等于80成绩单,同时,也显示成绩大于等于90的其他同学的成绩单
awk '/Amit/?($4>=80):($4>=90){print}' /tmp/test.txt示例7:!pattern(逻辑非)
## 显示非English课程的成绩单
awk '!/English/{print}' ~/test.txt示例8:pattern1,pattern2(地址对)
## 显示成绩单中从Amit同学到Shyam同学的行
awk '/Amit/,/Shyam/{print}' /tmp/test.txt## 创建一个名为 students.txt 的文件(其中包括学生名字、性别、籍贯、年龄)。
cat >/tmp/students.txt <<EOF
张三 男 湖北 12岁
李丽 女 湖北 12岁
李丽 女 山西 12岁
李丽 女 北京 12岁
王五 男 湖北 12岁
EOF
[root@dj ~]# awk 'BEGIN{print "姓名\t性别\t地址\t年龄"} {print $1"\t"$2"\t"$3"\t"$4} END{print "##原神玩家列表##"}' /tmp/students.txt 
姓名	性别	地址	年龄
张三	男	湖北	12岁
李丽	女	湖北	12岁
李丽	女	山西	12岁
李丽	女	北京	12岁
王五	男	湖北	12##原神玩家列表##

判断问题

判断uid小于等于10 输出 aaa 大于10 输出 bbb (多个方法)
awk -F: '{$3<10?USER="aaa":USER="bbb";print $1,USER}' /etc/passwd判断uid小于等于10 输出 user 大于10 输出 pass
awk -F: '{if($3<10){print "user=>"$1}else{print "pass=>"$1}}' /etc/passwd判断 普通用户系统用户root用户
awk -F ":" '{if($3==0){print $0" 这是root用户"}else if(($3>0)&&($3<1000)){print $0" 这是系统用户"}else{print $0" 这是普通用户"}}' /etc/passwd
3 awk 的变量使用
[root@server144 ~]# echo $name						## 这里的 $ 是通配符
root
[root@server144 ~]# cat /etc/passwd|awk -F ':' '/^$name/{print $1}'			
## 开了正则 $ 是正则
[root@server144 ~]# cat /etc/passwd|awk -F ':' '/^\$name/{print $1}'		
## 转义了  是$ ,无任何意义
[root@server144 ~]# cat /etc/passwd|awk -F ':' '/^"$name"/{print $1}'		
## 无法输出	  和 第一条一样
[root@server144 ~]# cat /etc/passwd|awk -F ':' '/^'$name'/{print $1}'		
## 成功 
root

15.10 函数的使用

函数的声明

语法1:

​ function 变量名 {

​ 命令

​ }

语法2:

​ 变量名(){

​ 命令

​ }

#!/bin/bash
sbsb(){echo "eb"}echo "6666666"
sbsb
声明函数 sbsb  并使用

函数的传参

例子

 #!/bin/bash
a(){
echo "$1 $2 $3 "
}
a 123 $2 789
[root@dj tmp]# bash 7.sh abc 456 awfa
123 456 789 
对于脚本来说 $1 是固定123  $3 是固定 789   ,而$2 没有规定,在运行脚本后面的 $2的位置 是456

http://www.ppmy.cn/news/1556164.html

相关文章

【教程】如何在服务器的终端中创建定时任务?

以CentOS 7系统的服务器为列&#xff0c;我们可以使用cron服务来实现定时任务配置。具体操作如下&#xff1a; 编辑cron任务配置文件 使用以下命令编辑当前用户的cron任务配置文件&#xff1a; crontab -e执行该命令后&#xff0c;会打开一个文本编辑器&#xff0c;用于编辑…

app的测试范围以及web和app的测试区别

目录 图1.App的测试范围1.1功能测试1.2专项测试1.3性能测试 2.Web和App的测试区别2.1相同点2.2不同点 &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01; ⭐️ 收藏&#xff0c;你的青睐是我努力的方向&#xff01; ✏️ 评论&#xff0c;你的意见是我进步的…

神经网络基础-初识神经网络

人工神经网络&#xff08; Artificial Neural Network&#xff0c; 简写为ANN&#xff09;也简称为神经网络&#xff08;NN&#xff09;&#xff0c;是一种模仿生物神经网络结构和功能的计算模型。人脑可以看做是一个生物神经网络&#xff0c;由众多的神经元连接而成。各个神经…

immaculate C# DragDrop 注册失败 解决 C#窗口程序如何看控制台打印的日志

C# DragDrop 注册失败 System.InvalidOperationExceptionHResult0x80131509MessageDragDrop 注册失败。SourceSystem.Windows.FormsStackTrace:在 System.Windows.Forms.Control.SetAcceptDrops(Boolean accept)在 System.Windows.Forms.Control.OnHandleCreated(EventArgs e)…

网络隧道与代理

文章目录 网络隧道网络代理参考 网络隧道 使用隧道的原因是在不兼容的网络上传输数据&#xff0c;或在不安全网络上提供一个安全路径。网络隧道的一个典型特征就是封装报文和对报文加密。如下是两个典型的案例&#xff1a;IPv4到IPv6的迁移、VPN。 图3.1 IPv4到IPv6的迁移 图…

异步线程使用

创建线程的几种方法&#xff1a; 执行完整个main方法&#xff0c;在后台慢慢打印。 1.2.3方式都不能获得控制资源。 4、 线程池方法&#xff0c;每个异步任务&#xff0c;提交给线程池让他自己去执行就行。 1、降低资源的消耗 通过重复利用已经创建好的线程降低线程的创建和…

【python面向对象】304-面向对象三大特性-3.多态

三种特性&#xff1a; 封装性&#xff1a; 私有属性和私有方法把属性和方法隐藏起来&#xff0c;属性没有特殊要求都用私有属性意义&#xff1a; 以面向对象的编程思想进行项目开发封装数据属性&#xff1a;明确的区分内外&#xff0c;控制外部对隐藏的属性的操作行为(保护数据…

泷羽Sec学习笔记-zmap搭建炮台

zmap搭建炮台 zmap扫描环境&#xff1a;kali-linux 先更新软件库 sudo apt update 下载zmap sudo apt install zmap 开始扫描(需要root权限) sudo zmap -p 80 -o raw_ips.txt 代码解析&#xff1a; sudo&#xff1a;以超级用户&#xff08;管理员&#xff09;权限运行…