15.1 Shell 变量
-
了解:Shell的功能
-
了解:Shell的种类
-
了解:Shell的调用
-
了解:Shell变量的概念
-
了解:Shell变量的定义
-
了解:Shell数组变量
-
了解:Shell内置变量
-
了解:双引号 和 单引号
-
了解: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 的调用
bash
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变量
bash
var01="123" ## 直接赋值
var02="${var01}456" ## 引用<其他变量值>来赋值
var03="`date` ## 引用<命令屏显输出>来赋值
echo "$var01" ## 显示<变量值> 123
echo "$var02" ## 显示<变量值> 123456
echo "$var03" ## 显示<变量值> 显示时间,注意显示的是定义变量的时间,而不是echo 这里的时间
15.1.5 Shell 数组变量
Shell 数组变量:就是一组同姓不同名的<变量集合>,彼此用<索引下标>加以区分。
Shell 数组变量:分为两类
第一类:普通数组,即:数字下标,有序数组 (最常用)
第二类:关联数组,即:命名下标,无序数组 (不常用)
定义:Shell数组变量(普通数组)
bash
a=() ## 只定义不赋值
a=("a" "b" "c") ## 赋值需要空格隔开,下标从0开始
a=(
a
b
c
) ## 同上
a[0]="a"
a[1]="b"
a[5]="c" ## 赋值单个数组
引用:Shell数组变量(普通数组),里面的都是数组而不是单个变量
bash
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
bash
echo ${#a[*]} ## 在判断某不确定命令有多少数组时,去抓取做循环很好
${a[@]} 使用例子,数组一个个打印出来
bash
#!/bin/bash
Service="1,2,3,4,5"
# 使用逗号分割字符串为数组 赋值给service
IFS=',' read -r -a service <<< "$Service"
# 将数组每个值赋值给 test
for test in "${service[@]}"; do
echo "$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内置变量:
bash
$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进程号>
例子:
bash
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部署状态
bash
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; then
echo "-------------部署未完成,超时打印日志-------------"
# 获取部署的所有 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}; do
echo "Logs for pod ${pod}:"
kubectl logs ${pod} -n go
done
exit 1
else
echo "部署成功"
fi
15.1.9 双引号 和 单引号
<""双引号> 是:<部分引用>,可以识别<特殊符号>
<''单引号> 是:<完全引用>,不能识别<特殊符号>
举例:
bash
终端显示如下
[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不支持 所以一般使用 反引号 ``
bash
a="ls -l"
echo $a 会输出 ls -l
a=`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"
环境变量定义方法:
bash
export a="123" ## 定义变量a为Shell 环境变量 一般使用这个
declare -x b="456" ## 定义变量b为Shell 环境变量
例子证明
bash
a=123 ## 定义本地变量
vim test.sh ## 写一个脚本证明
## 脚本相当于一个子进程,因为脚本也可以使用另外一个远程连接来写
#!/bin/bash
echo $a 在脚本里面运行
bash test.sh
## 发现并无 123 输出
## 说明本地变量不能给子进程使用,只能给创建a的进程使用
bash
export b=123 ## 定义环境变量
vim test.sh ## 写一个脚本证明
## 脚本相当于一个子进程,因为脚本也可以使用另外一个远程连接来写
#!/bin/bash
echo $b 在脚本里面运行
bash test.sh 发现有123显示在终端上,说明环境变量可在脚本里面也可运行
## 注意:因为前面用 a 设置本地变量,所以设置环境变量不能使用 a ,否则会出错,因为环境变量虽然会覆盖本地变量的a,但是其实在配置文件里面还会存在本地变量a为123
## 这时就会出现 在脚本里面运行的时候,会运行本地变量的 a 而不是环境变量的 a 从而导致一样没有123显示,这样就无法判断
Linux 系统中设置环境变量例子 .env文件
bash
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] [参数 ...]
选项参数:
bash
-n ## 不追加换行符 ☚ 默认情况下,两条命令结合起来会追加换行符
-e ## 启用:反斜杠转义
echo 支持的常用<反斜杠字符>如下:
bash
\n ## 换行
\t ## 横向制表符
\r ## 用于表示回车符 一般用于循环覆盖前面的内容
比如 echo -e "\r" 表示输出一个回车符
回车符会将光标移动到当前行的开头位置,使得后续输出会覆盖当前行的内容。这在一些需要实现文本动态更新或进度条效果的场景中特别有用。
比如 倒数计时
bash
[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选项:
bash
[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 选项
bash
[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 不起作用
bash
echo 1.txt | rm -rf ## 这样不行,因为echo只是输出给 rm 接收,它不一定会接收
## 不行就添加 xargs
倒计时覆盖
bash
#!/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 表示)
(& 表示全部,无论正确还是错误信息)
bash
ls 1>> 1.txt ## 把ls 展现的正确输出结果 追加到 1.txt
sksoefsl 2>> 1.txt ## 把展示出来的错误信息 追加到 1.txt
ls &>> 1.txt ## 无论是正确还是错误的,该命令的输出全部追加到 1.txt
对于不需要的信息,我们可以写入 黑洞 ,不占内存,黑洞路径是 /dev/null ,比如
bash
yum -y install tree &> /dev/null
echo $? ## 查看上条命令成功与否,0为成功,1为失败
省略不写相当于是标准输出1
常用于丢弃输出,然后自定义显示
命令 > /dev/null 2>&1
bash
ls -waadaw > /dev/null 2>&1 ## 标准输出1重定向到/dev/null
## 标准错误输出2重定向到标准输出1
或
ls -waadaw &> /dev/null ## 将标准输出和标准错误都重定向到/dev/null
15.2.3 >、>> 标准错误输出重定向
重定向:改变<命令>的<标准输出/标准错误输出>的<输出方向>,不输出到<屏幕>,而是输出到<文件>。
★ > 重定向符:覆盖式重定向
★ >> 重定向符:追加式重定向
<标准输出>重定向,主要用于保存<命令执行成功的结果信息>,便于后期引用。
bash
echo "123" > 1.txt ## 覆盖式重定向
echo "456" >> 1.txt ## 追加式重定向
<标准错误输出>重定向,主要用于保存<命令执行失败的错误信息>,便于查阅分析
bash
yum instll wget -y 2> 1.txt ## 覆盖式重定向
yum updata -y 2>> 1.txt ## 追加式重定向
<标准输出/标准错误输出>重定向,静默执行命令 不输出内容
bash
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 <、<< 标准重定向输出
★ < 输入重定向:从<指定文件>中,读取数据,输入到<指定文件>中
★ << 输入重定向:从<标准输入>中,读取数据,输入到<指定文件>或<指定命令>中
< 输入重定向例子:
覆盖式输入
bash
echo "aaa" > 1.txt
cat > 3.txt < 1.txt ## 将1.txt里面的内容写进3.txt并覆盖
追加式输入
bash
cat >> 3.txt < 1.txt
<< 输入重定向例子
bash
cat <<
EOF
This is a test.
Hello, world!
EOF
## 会直接在终端输出
This is a test.
Hello, world!
将111222追加到 3.txt
bash
cat << EOF >> 3.txt
111222
EOF
15.2.5 | 管道符
管道符:像流水线一样,将一个<命令>的<标准输出>,作为另一个<命令>的<标准输入>。
bash
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
原理:
bash
#!/bin/bash
random_number=$RANDOM
echo "随机数:$random_number"
##随机数 生成 0 到 32767 的某一个数
例子1:随机生成 0 到 2的随机数
bash
#!/bin/bash
echo $[RANDOM%3]
相当于对 3 取余,只有 0 1 2 产生
##运行脚本 生成 0 1 2 三个随机数,可拿来做数组下标,做一个随机生成名字喊人回答问题的代码
随机字符 /dev/urandom 生成随机字符
bash
[root@dj tmp]# cat /dev/urandom |tr -dc [:alnum:] |head -c 8
nRSCSps2
[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
```bash
#!/bin/bash
echo "我们一起来玩石头剪刀布吧"
xt=1
while (($xt<3))
do
xt=$[RANDOM%3]
read -e -p "请输入您的选择(0:石头 1:剪刀 2:布):" a
if [[ $a == 0 ]]
then
echo "您出的是石头"
if (($xt==1))
then
echo "我出的是剪刀,恭喜宁赢了,欢迎下次再来pk"
break
elif (($xt==0))
then
echo "不好意思我出的也是石头,平局喔,请继续"
else
echo "不好意思我出的是布,您输了,请继续"
fi
elif [[ $a == 1 ]]
then
echo "您出的是剪刀"
if (($xt==2))
then
echo "我出的是布,恭喜宁赢了,欢迎下次再来pk"
break
elif (($xt==1))
then
echo "不好意思我出的也是剪刀,平局喔,请继续"
else
echo "不好意思我出的是石头,您输了,请继续"
fi
elif [[ $a == 2 ]]
then
echo "您出的是布"
if (($xt==0))
then
echo "我出的是石头,恭喜宁赢了,欢迎下次再来pk"
break
elif (($xt==2))
then
echo "不好意思我出的也是布,平局喔,请继续"
else
echo "不好意思我出的是石头,您输了,请继续"
fi
else
echo "输入错误,请重新输入"
fi
done
##注意:输入那里的 if 判断必须使用 [[ ]] ,如果是 (( )),如果输入大于3的数字会正常,但是输入字母会默认成石头
```
例子3 随机点名
```bash
#!/bin/bash
for i in {2..1}
do
echo -n 还有$i秒,幸运儿究竟是谁 !!
echo -ne "\r\r"
sleep 1
done
echo " "
clear
name=(`cat /root/name.txt`)
name_list=${#name[*]} ## 数组有多少元素赋值给它,假如有5个元素则为5
name_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的随机数
```bash
#!/bin/bash
target=$[$RANDOM%1000+1]
while true
do
read -p "请输入一个数字(1到1000): " n
# 检查输入是否为整数
if ! [[ $n =~ ^[0-9]+$ ]]; then
echo "请输入有效的数字!"
continue
fi
# 判断猜测结果
if [ $guess -eq $n ]; then
echo "恭喜你,猜对了!"
break
elif [ $guess -lt $n ]; then
echo "猜的数字太小了,请继续猜!"
else
echo "猜的数字太大了,请继续猜!"
fi
done
```
### 15.3 通配符、正则、grep
#### 15.3.1 通配符
通配符:是Shell自身的一种特殊语句,实现\<路径名展开 pathname expansion\>展开功能。
用于:模糊搜索\<文件\>。
```bash
* ## 表示0或多个的<任意字符> (相当于正则里面 .*)
? ## 表示单个<任意字符> (相当于正则 .)
[ ] ## 表示匹配<字符列表或范围>中的<单个字符>(和正则里面一样)
[xyz] ## 设置可选的<字符列表>
[x-y] ## 设置可选的<字符范围>
[^xyz] ## 表示不选择x、y、z;
^ ## 表示不匹配,^ 必须是第一个字符
```
举例:
```bash
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)\>。
注意:\<正则表达式\>和\
文件名 指定需要读取的<文件名>
表示准备接收<标准输入> 具备这功能的命令,可以接收 | 管道符
选项:建议无脑开启扩展正则
bash
-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
bash
[root@dj exports.d]# grep -c '' /tmp/passwd 有49行
49
例子:查看文件,去除注释和空行 扩展正则
bash
egrep -v "^\s*(#|$)" /etc/passwd
## 以 0个或多个 空格 开头,然后接 # 号 或者 直接结尾
-w 例子
bash
netstat -tunlp | grep -w "22"
## 会匹配 22 的内容,但是 222,223 122 这种不会被匹配
-o 例子 过滤数字和字母
bash
egrep -o '[0-9]+'
egrep -o '[a-zA-Z]+'
15.4 各种常用辅助命令
15.4.1 sort、uniq、wc 命令
sort 命令:排序
☛ 命令选项说明
bash
-g ## 按<一般数值>排序
-n ## 按<字符串数值>排序
-r ## 逆序排序(默认升序) -gr 就是按数字降序排序 重要
-f ## 忽略<字母大小写>
-b ## 忽略<前导的空白区域>
-k ## 按照指定字段进行排序 sort -k 2 file.txt 按第二字段进行排序
-o ## 将<排序结果>回写入<原文件>,例如:cat 1.txt
★ uniq 命令:分组汇总
有相同的行就去重。
bash
-c ## 去重的同时统计出现多少次
uniq -c ## 常用于查找ip访问最多
★ wc 命令:统计
☛ 命令选项说明
bash
不加选项,分别表示行数、单词数量和字节数 比如 wc /etc/passwd
-l ## 统计:行数
-W ## 统计:单词数
-c ## 统计:字节数
-m ## 统计:字符数
-L ## 显示:最长行的长度
一般组合使用是 找相同 排序 去重 在排序
统计并显示次数出现前十的ip access_log 是文件名
bash
[root@dj ~]# cat access_log | awk '{print $1}'| sort -r | uniq -c | sort -gr | head -10
1055 192.168.1.25
509 192.168.1.14
360 192.168.1.37
303 192.168.1.12
262 192.168.1.41
254 192.168.1.21
242 192.168.1.8
234 192.168.1.5
203 192.168.1.31
195 192.168.1.30
netstat -anlp|grep 80|grep tcp|awk '{print 5}'\|awk -F: '{print 1}'|sort|uniq -c|sort -nr|head -n20
三个一起使用能发挥重大作用,一个排序 一个去重 一个分组
例子:
bash
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 文件的字符数
统计显示:网络连接信息:没排序和 排序和排序去重的区别对比
bash
[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个字节
常用选项:
bash
-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 命令 可以接收 | 管道符
测试:按<字节>进行选择,不承认<分隔符>
bash
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]
常用选项:
bash
-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字符集>都是一组<字符串>,一般都可按照字面含义理解。
基本原理图:
bash
echo "0123456789" | tr "9876543210" "12345" ## 输出结果:5555554321

如果第二行没有第一行的内容,直接不变写入,
如果第二行存在第一行的内容,相当于把第二行的内容替换成第三行的
如果有第二行但是第三行没有替换对应的,则替换成第三行最后一个字符
语法练习
bash
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 循环语句>中使用
选项:
bash
-i "string" ## 缓冲区文字,用于设置<默认输入值>
-e ## 交互式输入,允许<backspace键>回退
-p "string" ## 设置<提示输入的说明文字> 必有 ep 一般一起使用
-s ## 不显示<输入内容,包括:换行符>(常用于输入密码) 可以设置密码
-a array_name ## 将<word单词>赋值给<array_name数组变量>,<数组下标>从 0 开始 一般和 for循环使用,和-ep 混用要注意
-r ## 不允许<反斜杠>转义<任何字符>
例子
-i
bash
read -i "/usr/local/nginx" -ep "请您输入 nginx 的安装目录(例如 /usr/local/nginx):" a
## 会显示 请您输入 nginx 的安装目录(例如 /usr/local/nginx):/usr/local/nginx
vim 1.sh
bash
#!/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例子
bash
vim 1.sh
#!/bin/bash
# 提示用户输入一行以空格分隔的字符串
echo "请输入一行以空格分隔的字符串:"
read -a array
# 打印数组的每个元素
echo "数组的元素如下:"
for element in "${array[*]}"
do
echo "$element"
done
bash 1.sh aa bb cc 会打印 aa bb cc 一行打印一个
bash
#!/bin/bash
echo "您可以一次创建一个或多个目录,用空格隔开"
read -ep "创建的共享目录名是(如test1):" -a gx
for i in ${gx[*]}
do
mkdir -p /$i
done
随便输入创建 1个 或 多个目录
以 , 分隔例子
bash
#!/bin/bash
dir_paths="/,/go-data,/alidata,/k8s-alidata" # 字符串
IFS=',' read -r -a dir_array <<< "$dir_paths"
# 遍历每个目录
for dir_path in "${dir_array[@]}"; do
echo $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(秒)
举例:
bash
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作业任务>执行结束
bash
#!/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 功能:输出文件路径的文件名
bash
dirname 1/2/3/4/5 ## 输出1/2/3/4
basename 1/2/3/4/5 ## 输出5 无论有没有该文件路径都会这样输出
15.5.6 xargs 命令
功能:xargs命令 可以通过<| 管道符>接受<字符串>,并将<接收到的字符串>依据<默认空格>分割成<许多参数>,然后,将<这些参数>作为<命令行参数>传递给<后面的命令>,<后面命令>则可以使用这些<命令行参数>来执行。
作用:管道符 | 对于不支持标准输入的命令(比如 rm ),无法传输文件,使用该命令即可 例如:
bash
echo "1.txt" | rm -rf ## 操作失败,因为 rm 无法感知 1.txt 是文件名还是字符串
echo "1.txt" |xargs rm ## 成功删除当前目录下的1.txt文件
选项:
bash
-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 " " $[ ]必须使用该格式才可以进行计算和相关操作
使用 + - * / % < > 需要使用该格式 ,专门使用拿来定义表达式
bash
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 外部命令
bash
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 + - * / % **(幂)
! 逻辑取反
bash
let "x=1, y=2, z=(x<y)" ## 判断语句
echo $z ## 输出 1 ,布尔类型中 1 代表正确
let "x=1, y=2, z=!(x<y)" ## 判断语句
echo $z ## 输出 0 ,布尔类型中 0 代表错误
~ 按位取反
bash
let "x=2, y=~x" ## y=~x 就是:按位取反
echo "y=$y" ## 计算结果:y=-3
<< 和 >> 对应的二机制数移位
bash
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
& 逻辑与 | 逻辑或 ^ 逻辑异或 都是二进制的对比
bash
& ## 双 1 为 1 ,否则为 0
| ## 有 1 为 1 ,全 0 为 0
^ ## 相同为 0 ,不同 为 1
let "x=(10^12)" ## 二进制分别是 1010 1100 得到 0110 即 6
echo $x ## 输出 6
<赋值>操作符 : =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=
bash
= ## 例句: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 算数条件判断表达式
bash
<, <=, >, >= ## 小于,小于等于,大于,大于等于
==, != ## 等于,不等于
expr1?expr2:expr3 ## 根据<条件判断>结果,来予以<赋值>
if expr1; then expr2 ; else expr3 ; fi
expr1 && expr2 || expr3
意思是:如果怎么样,那么就怎么样,否则就这样
bash
((x=($a<$b)?$a*10:$b*100)) ## 如果 $a小于$b,则 x=$a*10,否则 x=$b*100
## 三目运算符
a=1
b=2
echo $x ## 测试发现 输出 10
2 字符条件判断表达式
<整数>判断(正负整数)
bash
-eq ## 等于 ,如:arg1 -eq arg2
-ne ## 不等于 ,如:arg1 -ne arg2
-gt ## 大于 ,如:arg1 -gt arg2
-ge ## 大于等于,如:arg1 -ge arg2
-lt ## 小于 ,如:arg1 -lt arg2
-le ## 小于等于,如:arg1 -le arg2
<字符串>判断
bash
-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
## 显示结果:true
string1 = 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 正则条件判断表达式
<正则条件表达式>:<操作符>
=~ 扩展正则表达式 ## 判断:是否匹配<扩展正则表达式>
注意:
在 =~ 扩展正则表达式 中,=~ 左右两边各有一个<空格>
在 =~ 扩展正则表达式 中,<扩展正则表达式>不要使用<""双引号>和<''单引号>
在 =~ 扩展正则表达式 中,如果需要匹配<空格>,则需要使用<\转义符>
以下是扩展正则表达式
bash
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 到 999
n="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正则表达式>为:
bash
^(/?[^/\s\"\']+)+/?$ ## 第二个 ^ 是取反的不匹配 \" 表示 "
要求3 如果路径中需要包含<空格>,则必须:在<整个路径的最外层>添加【""双引号】,设计<Perl正则表达式>为:
bash
^[\"]{1}(/?[^\"\']+)+/?[\"]{1}$
注意:下面的写法实际上输出不是以 " " 开头和结尾,所以不要被后面的匹配 '' '' 给骗了
bash
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:组合使用多个<算数条件表达式>
bash
(((1<2)&&(2<3))) 或者 [[ ( 1 < 2 ) && ( 2 < 3 ) ]]
echo $? ## 结果显示:<算数条件表达式>的<状态返回值>为<0> 判断上条命令运行结果,正确返回 0,错误返回1 和布尔类型相反
(((2<1)||(2<3)))
echo $? ## 结果显示:<算数条件表达式>的<状态返回值>为<0>
示例2:组合使用多个<字符条件表达式>
bash
[[ "a" == "a" ]] && [[ "b" == "b" ]]
echo $? ## 结果显示:<字符条件表达式>的<状态返回值>为<0>
[[ "a" == "a" ]] || [[ "a" == "b" ]]
echo $? ## 结果显示:<字符条件表达式>的<状态返回值>为<0>
5 test 条件判断命令(判断文件)
这里可以拿来判断文件是否存在,是什么类型的文件,是否为空文件等等
test 和 [[ ]] 用法一样
1 判断:任意类型的文件(Linux一切皆文件)
bash
-a filename ## 如果filename存在,不管文件是什么类型,则为true真
-e filename ## 如果filename存在,不管文件是什么类型,则为true真
-s filename ## 如果filename存在,并且是<非空文件(长度 > 0字节)>,不管文件是什么类型,则为true真
[[ -a /etc/passwd ]] 或者 test -a /etc/passwd
比如:
if test -e filename; then
echo "文件存在"
fi
# 或者使用 [[ ]]
if [[ -e filename ]]; then
echo "文件存在"
fi
2 判断:特定类型的文件(Linux一切皆文件)
bash
-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一切皆文件)
bash
-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一切皆文件)
bash
file01 -nt file02 ## 如果file01比 file02新,则为true真
file01 -ot file02 ## 如果file01比 file02旧,则为true真
file01 -ef file02 ## 如果file01和file02是同一个文件,则为true真
## 主要是依据file01和file02各自的inode编号,来判断是否为<硬链接文件/软连接文件>?
5 判断:字符串 STRING 代表字符串,不是特指
bash
-z STRING ## 判断:<字符串>是否为<空>?
-n STRING ## 判断:<字符串>是否为<非空>?
STRING1 = STRING2 ## 判断:<字符串>是否为<相等>?
STRING1 != STRING2 ## 判断:<字符串>是否为<不相等>?
STRING1 < STRING2 ## 基于<字典>来判断:<字符串1>是否小于<字符串2>?
STRING1 > STRING2 ## 基于<字典>来判断:<字符串1>是否大于<字符串2>?
6 判断:数值
bash
arg1 OP arg2 ## 比较:arg1和arg2这两个<算数值>
## OP是<操作符>,取值范围是:-eq等于、-ne不等于、-lt小于、-le小于等于、-gt大于、-ge大于等于。
7 判断:其他
bash
-o OPTION ## 如果指定的<set内建的Shell选项>已经<启用>,则为true真
例如:
[ -o history ] && echo $? ## 判断是否启用了<set内建的Shell选项:history功能>?
-v VAR ## 如果一个<VAR变量>已被<定义并赋值>,则为true真
8 逻辑操作
! EXPR ## 执行:逻辑取反
EXPR1 -a EXPR2 ## 执行:逻辑与
EXPR1 -o EXPR2 ## 执行:逻辑或
举例:
bash
[ -d /tmp -a -d /etc ] && echo $?
[ -d /tmp -o -d /dir01 ] && echo $?
6 [ ] 和 [[ ]] 的区别
建议用 [[ ]]
\] :它是一个\<命令\>,是\