shell编程
大于:-gt(greater than) 小于:-lt(less than) 等于:-eq(equal) 不等于:-ne(not equal) 大于等于:-ge(greater than or equal) 小于等于:-le(less than and equal)
用到 && 要用双中括号
判断是不是链接
[ -L /etc/rc.local ];echo $?
[root@wzb shell]# ll /etc/rc.local
lrwxrwxrwx. 1 root root 13 3月 20 16:02 /etc/rc.local -> rc.d/rc.local
判断是不是设备文件
[root@wzb shell]# [ -b /dev/sda ];echo $?
0
判断是不是字符设备
[ -c ]
变量关系
命令执行成功则输出 0
失败则输出 1-255
[root@wzb shell]# les
bash: les: 未找到命令...
\[root@wzb shell]# echo $?
127
[root@wzb shell]# ls
ping1.sh ping.sh
[root@wzb shell]# echo $?
0
若ping通则输出up,否则输出down
&&
||
[root@wzb shell]# cat ping1.sh
ping -c1 www.baidu.com && echo "its up" || echo "down"
[root@wzb shell]# ./ping1.sh
PING www.a.shifen.com (112.80.248.75) 56(84) bytes of data.
64 bytes from 112.80.248.75 (112.80.248.75): icmp_seq=1 ttl=128 time=16.5 ms
--- www.a.shifen.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 16.598/16.598/16.598/0.000 ms
its up
&>/dev/null
将结果输出到文件
[root@wzb shell]# cat ping.sh
ping -c1 www.baidu.com &>/dev/null && echo "its up" || echo "down"
[root@wzb shell]# ./ping.sh
its up
单引号不能识别特殊语法
双引号能识别特殊语法
[root@wzb shell]# age1='$age'
[root@wzb shell]# echo $age1
$age
[root@wzb shell]# age1="$age"
[root@wzb shell]# echo $age1
14
开启子shell
每次调用bash/sh
解释器执行脚本,都会开启一个子shell,因此不保留当前的shell变量,
当前的变量是name=“萝卜”
[root@wzb shell]# cat t1.sh
name="萝卜"
[root@wzb shell]# name="西瓜"
[root@wzb shell]# echo $name
西瓜
[root@wzb shell]# bash t1.sh
[root@wzb shell]# echo $name
西瓜
不开启子shell
[root@wzb shell]# source t1.sh
[root@wzb shell]# echo $name
萝卜
pas
创建进程列表(创建子shell)
进程列表,案例
加上小括号就是开启子shell运行
[root@wzb ~]# (cd ~;pwd;ls;cd /tmp/;pwd;ls)
检测是否在子shell中
BASH_SUBSHELL
该变量的值特点,如果是0,就是在当前shell环境中执行的,否则就是开辟子shell去运行的
[root@wzb tmp]# cd ~;pwd;ls;cd /tmp/;pwd;ls;echo ${BASH_SUBSHELL}
结果输出0
那么就是在当前shell中执行的
[root@wzb tmp]# (cd ~;pwd;ls;cd /tmp/;pwd;ls;echo $BASH_SUBSHELL)
结果为1
那么就开启了子shell
子shell嵌套运行
表明在2层子shell
[root@wzb tmp]# (pwd;(echo $BASH_SUBSHELL))
/tmp
2
3层子shell
[root@wzb tmp]# (pwd;(echo $BASH_SUBSHELL;(echo $BASH_SUBSHELL)))
/tmp
2
3
环境变量设置
用户个人配置文件:/.bash_profile、/.bashrc 远程登录用户特有文件
在 ~/.bash_profile 添加 export name="吴泽斌"
但是echo $name
输出是空的
exit
su - wzb
再次 echo $name
发现可以输出
但是退出之后输入su wzb
似乎是不行的
所以用户登录之后会加载 ~/.bash_profile
但是回到root
用户就无法输出
在/etc/profile
添加export name="大萝卜"
退出登陆su - root
即可输出对应的值
登陆 wzb
用户,将 在~/.bash_profile
添加 的export name="吴泽斌"
注释
重新登陆wzb和root
在wzb用户下
[wzb@wzb ~]$ echo $name
大萝卜
输出了全局变量
在当前shell定义变量ip1
export ip1
或
export ip1=1.1.1.1
引用其他脚本的变量
需要被引用变量的脚本
[root@wzb shell]# cat var.sh
#!/bin/bash
ip1=114.114.114.114
name=wzb666
引用变量
[root@wzb shell]# cat use_var.sh
#!/bin/bash
. var.sh
echo $ip1
echo $name
可以将变量转为环境变量,影响到全局
检查系统环境变量的命令
set,输出所有变量,包括全局变量、局部变量
env,只显示全局变量
declare,输出所有的变量,如同set
export,显示和设置环境变量值,找出环境变量
特殊变量
参数传递
位置参数的获取
$0 获取文件名
1 获取第一个参数,大于 9 则写 1 获取第一个参数,大于9则写 1获取第一个参数,大于9则写{10}
$# 获取执行的shell脚本后面的参数总个数
$* 加引号,获取shell脚本所有参数为单个字符串
加引号,将参数看作一份数据
$@ 加引号,接收所有参数为独立的字符串
[root@wzb shell]# cat t3.sh
#! /bin/bash
echo 'result:' $0 $1 $2
echo '======='
echo '$#'
echo 'result:' $#
echo '======='
echo '特殊变量 $@'
echo 'result:' $@
echo '======='
echo '$*'
echo 'result:' "$*"
pass
[root@wzb shell]# ./t3.sh q w e
result: ./t3.sh q w
=======
$#
result: 3
=======
特殊变量 $@
result: q w e
=======
$*
result: q w e
∗ 和 *和 ∗和@
pass
[root@wzb shell]# cat difference.sh
#! /bin/bash
echo "print each param from \$*"
for var in "$*"
do
echo "$var"
done
echo "each from \$@"
for var in "$@"
do
echo "$var"
done
结果
怎么传入数据的?
[root@wzb shell]# ./difference.sh 1 2 3 4 5
print each param from $*
1 2 3 4 5
each from $@
1
2
3
4
5
特殊状态变量
$? 上一次命令执行状态返回值,0正确,非0失败
$$ 当前shell脚本的进程号
$! 上一次后台进程的PID
$_ 上一个命令传入的最后一个参数
$?
[root@wzb shell]# cat t4.sh
#! /bin/bash
[ $# -ne 2 ] && {
echo "2 args"
exit 199
}
echo ok
结果
[root@wzb shell]# ./t4.sh 1 2 3
2 args
[root@wzb shell]# echo $?
199
$!
获取上一次在后台执行的程序的PID
nohup 后台执行
& 1> 将结果输出重定向
[root@wzb shell]# nohup ping baidu.com & 1> ./tong
[1] 12211
[root@wzb shell]# nohup: 忽略输入并把输出追加到"nohup.out"
pass
[root@wzb shell]# ps -ef | grep ping
wzb 5943 5540 0 14:49 ? 00:00:01 /usr/libexec/gsd-housekeeping
root 12211 11423 1 21:38 pts/0 00:00:00 ping baidu.com
[root@wzb shell]# echo $!
12211
$$
pass
$_
pass
简单内置shell命令
echo
eval
execexport
read
shift
echo
-n 不换行输出
-e 解析字符串中的特殊符号
\n 换行
\r 回车
\t 制表符 四个空格
\b 退格
不换行打印
[root@wzb shell]# echo sb
sb
[root@wzb shell]# echo sb;echo nt
sb
nt
[root@wzb shell]# echo -n sb;echo nt
sbnt
-e 案例
[root@wzb shell]# echo "你是个\n傻逼"
你是个\n傻逼
[root@wzb shell]# echo -e "你是个\n傻逼"
你是个
傻逼
printf可以自动识别
[root@wzb shell]# printf "你是个\n傻逼\n"
你是个
傻逼
eval 执行多个命令
[root@wzb shell]# cd /home/
[root@wzb home]# eval ls ;cd /home/wzb/my_prac/
centos7-test dockerfile docker-test-volume itheima test tom wzb wzb123 wzb.txt
exec
不创建子进程,执行后续命令,执行完毕后自动exit
pass
shell子串(变量内容的修改)
输出变量长度
[root@wzb ~]# name="超级大萝卜"
[root@wzb ~]# echo $name
超级大萝卜
[root@wzb ~]# echo ${#name}
5
截取(索引和切片)
[root@wzb shell]# echo ${info}
123456789
从索引为2的开始
[root@wzb shell]# echo ${info:2}
3456789
设置起点和长度
[root@wzb shell]# echo ${info:5:3}
678
wc -L
找出最长的一行,统计长度
expr
[root@wzb ~]# echo $name1
qweqweqwe
[root@wzb ~]# expr length $name1
9
awk 计算长度
[root@wzb ~]# echo $name1
qweqweqwe
[root@wzb ~]# echo $name1 | awk '{print length($0)}'
9
seq声称序列
-s 指定分隔符
[root@wzb ~]# seq 5
1
2
3
4
5
[root@wzb ~]# seq -s ":" 5
1:2:3:4:5
time
[root@wzb ~]# time for n in {1..10000};do char=`seq -s "qqq" 100`;echo ${#char} &> /home/wzb/my_prac/tong;done
real 0m12.376s #实际运行的时间
user 0m2.981s #用户态执行的时间
sys 0m9.173s #内核态执行的时间
变量内容的修改
${变量#word} 从变量开头删除最短匹配的word子串
不会真正修改变量
q1=abcABCABCabc
将开头匹配到的abc删除再输出
[root@wzb ~]# echo ${q1#a*c}
ABCABCabc
${变量##word} 从变量开头删除最长匹配的word子串
输出为空
[root@wzb ~]# echo ${q1##a*c}
${变量%word} 从变量结尾删除最短的word
[root@wzb ~]# echo ${q1%a*c}
abcABCABC
${变量%%word} 从变量结尾开始删除最长匹配的word
[root@wzb ~]# echo ${q1%%a*c}
字符串的替换
代替第一个匹配项
[root@wzb ~]# str1="hello,man"
[root@wzb ~]# echo ${str1/man/boy}
hello,boy
代替所有匹配项
[root@wzb ~]# echo ${str2}
hello,man,man,123
[root@wzb ~]# echo ${str2//man/boy}
hello,boy,boy,123
变量的赋值
echo ${变量-内容}
var1没有被定义过
若变量没有被定义过,那么-后面的内容就会赋给变量
就算值是空的,也不会覆盖
[root@wzb shell]# echo $var1
[root@wzb shell]# echo ${var1-aa}
aa
[root@wzb shell]# echo ${var2}
[root@wzb shell]# var3=
[root@wzb shell]# echo ${var3-vv}
echo ${变量:-内容}
变量没有被赋值或变量的值为空,就可以赋予变量新的值
特殊shell扩展变量
:- 为空就赋值
若name的值为空
那么将123 赋给result
[root@wzb 2022-5-4]# echo $name
[root@wzb 2022-5-4]# result=${name:-123}
[root@wzb 2022-5-4]# echo $name
[root@wzb 2022-5-4]# echo $result
123
:=
name和result都为空
则将apple都赋给它们
若 name 有值 那么 效果等同于 result=${name}
[root@wzb 2022-5-4]# result=${name:=apple}
[root@wzb 2022-5-4]# echo $name;echo $result
apple
apple
:? 检验是否为空
可以检验变量是否为空
为空时,主动抛出的错误信息
[root@wzb 2022-5-4]# echo ${name:?}
-bash: name: 参数为空或未设置
[root@wzb 2022-5-4]# echo $name
[root@wzb 2022-5-4]# name=wzb
[root@wzb 2022-5-4]# echo ${name:?}
wzb
:+ 有值就赋值
若为空则什么都不做,否则将值赋给接收者
有值则重新赋值
[root@wzb 2022-5-4]# echo ${name:+haha}
[root@wzb 2022-5-4]# name=123
[root@wzb 2022-5-4]# echo ${name:+haha}
haha
[root@wzb 2022-5-4]# echo $name
123
实际应用
删除过期数据的脚本
删除七天以上的过期数据
不懂
find 需要搜索的目录 -name 文件名 -type 文件类型 -mtime +7 | xargs rm -f
pass
find ${dir_path} -name '*.tar.gz' -type -f -mtime +7 | xargs rm -f
若 "dir_path"没有被定义过,那么该脚本就会在当前目录搜索、删除
改进
若 dir_path 的只是空的 那么/data/mysql_back_data/
就会赋给dir_path
find ${dir_path:-/data/mysql_back_data/} -name '*.tar.gz' -type -f -mtime +7 | xargs rm -f
shell数值计算
双小括号,逻辑运算
只能对整数
[root@wzb ~]# echo $((1>3))
0
[root@wzb ~]# echo $((5>3))
1
[root@wzb ~]# echo $((1>3))
0
[root@wzb ~]# echo $((5>3))
1
[root@wzb ~]# echo $((5>3||1>1))
1
a++,++a
a++:先计算+1,再打印
++a:先对变量a操作,也就是先打印a,在进行+1
[root@wzb ~]# a=5
[root@wzb ~]# echo $((++a))
6
[root@wzb ~]# echo $a
6
[root@wzb ~]# a=5
[root@wzb ~]# echo $((a++))
5
[root@wzb ~]# echo $a
6
输入数字和运算符
-n参数是if的语句,对字符串判断,如果字符串为空,条件就不成立,如果字符串不为空,条件成立
比如当用户输入字母加数字 123abc
那么sed 将数字替换为空
条件不成立 执行printf
exit 1 退出脚本
待解决
[root@wzb shell]# cat jisuan.sh
#!/bin/bash
read -p 'please input a num:' first_num
#echo $first_num
if [ -n "`echo ${first_num} | sed 's/[0-9]//g'`" ];#注释
then
echo "输入一个数字"
exit 1
fi
read -p '输入一个运算符:' fuhao
if [ ${fuhao} != '+' ] && [ ${fuhao} != '-' ] && [ ${fuhao} != '*' ] && [ ${fuhao} != '/' ];
then
echo "符号错了"
exit 1
fi
read -p 'please input second num:' second_num
if [ -n "`echo ${second_num} | sed 's/[0-9]//g'`" ];
then
echo "数字错了"
exit 1
fi
#echo "`awk '{print $first_num $fuhao $second_num}'`"
#exit 0
echo $first_num $fuhao $second_num | awk '{print $1 $2 $3}'
echo "结果是: $((${first_num}${fuhao}${second_num}))"
let命令计算
用的比较多
[root@wzb my_prac]# num=4
[root@wzb my_prac]# echo $((num=num+4))
8
[root@wzb my_prac]# let num=num+1
[root@wzb my_prac]# echo $num
9
expr
pass
[root@wzb shell]# expr 5 + 3
8
[root@wzb shell]# expr 5 * 3
expr: 语法错误
[root@wzb shell]# expr 5 \* 3
15
求长度
[root@wzb shell]# expr length 123wzb
6
逻辑判断
[root@wzb shell]# expr 5 - 5 = 0
1
[root@wzb shell]# expr 5 - 2 = 0
0
expr模式匹配
求长度
[root@wzb shell]# expr 123wzb ":" ".*"
6
[root@wzb shell]# expr 123wzb ":" ".*w"
4
判断文件名后缀是否合法
[root@wzb shell]# cat t6.sh
#!/bin/bash
read -p "输入文件名:" file_name
expr $file_name ":" ".*\.jpg" &> /dev/null
if [ $? != 0 ];then
echo "not jpg"
else
echo "it is jpg"
fi
apss
找出长度不大于5的单词
[root@wzb shell]# cat length_word.sh
#!/bin/bash
for str1 in aaa bbbb cccc dddddd eeeeeeeeeeee
do
#echo $str1
#expr length $str1
if [ `expr length $str1` -le 5 ];then
echo $str1
fi
done
bc命令
支持小数的运算,除法不支持
[root@wzb ~]# echo "4.5 * 4.4" | bc
19.8
[root@wzb ~]# echo "4.5 / 4.4" | bc
1
pass
[root@wzb shell]# echo {1..10} | sed "s/[[:space:]]/"+"/g"
1+2+3+4+5+6+7+8+9+10
pas
[root@wzb shell]# seq -s "+" 10
1+2+3+4+5+6+7+8+9+10
pas
[root@wzb shell]# echo {1..10} | tr " " "+"
1+2+3+4+5+6+7+8+9+10
结果计算
[root@wzb shell]# echo {1..100} | sed "s/ /+/g"
1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100
[root@wzb shell]# echo {1..100} | sed "s/ /+/g" | bc
5050
双小括号计算
[root@wzb shell]# echo $((`seq -s "+" 10`))
55
expr
[root@wzb shell]# seq -s " + " 100 | xargs expr
5050
awk计算
[root@wzb shell]# echo "3.1 3.5" | awk '{print $1 + $2}'
6.6
小数除法
[root@wzb shell]# echo "1.3 3.3" | awk '{print $1 / $2}'
0.393939
中括号计算
[root@wzb shell]# echo $num
5
[root@wzb shell]# echo $[num + 1]
6
Shell条件测试
test条件测试
-e
-e 检测文件是否存在
条件为真 输出 0
[root@wzb shell]# test -e t1.sh
[root@wzb shell]# echo $?
0
判断文件是否创建
#!/bin/bash
test -e "t21.sh" && echo "该文件或目录已存在" || mkdir t21.sh
-f
-f 判断文件是否是普通文件类型
[root@wzb shell]# test -f 5 && echo yes || echo no
yes
[root@wzb shell]# mkdir 123
[root@wzb shell]# test -f 123 && echo yes || echo no
no
-d
-d 判断是否为目录
pas
中括号条件测试
[root@wzb shell]# [ -f "$file_name" ] && echo yes || echo no
yes
pas
[root@wzb shell]# su - wzb
上一次登录:三 5月 4 09:43:06 CST 2022:0 上
[wzb@wzb ~]$ touch mod
[wzb@wzb ~]$ [ -r "mod" ] && echo ok || echo no
ok
[wzb@wzb ~]$ chmod 000 mod
[wzb@wzb ~]$ [ -r "mod" ] && echo ok || echo no
no
[wzb@wzb ~]$ echo 123 mod
123 mod
[wzb@wzb ~]$ echo 123>mod
-bash: mod: 权限不够
求变量值是否相等
变量要用双引号
[wzb@wzb my_prac]$ name=wzb
[wzb@wzb my_prac]$ [ $name == wzb ] && echo yes || echo no
yes
在中括号中,使用数学比较符号需要使用转义符
[root@wzb shell]# [ 1<2 ] && echo yes || echo no
no
[root@wzb shell]# [ 1\<2 ] && echo yes || echo no
yes
pas
[root@wzb shell]# [ $n1 -gt $n2 ] && echo yes || echo no
no
双中括号
支持正则表达式,不需要转义符
逻辑运算符
pass
-a 和 -o的使用
[root@wzb shell]# echo $file_name $file_name1 $file_name1
/home/wzb.txt /home/wzb1.txt /home/wzb1.txt
[root@wzb shell]# [ -f $file_name -a -f $file_name1 ] && echo yes || echo no
yes
[root@wzb shell]# [ -f $file_name -a -f $file_name2 ] && echo yes || echo no
no
[root@wzb shell]# [ -f $file_name -o -f $file_name2 ] && echo yes || echo no
yes
逻辑运算脚本
pas
[root@wzb shell]# cat test_of_or_and.sh
#!/bin/bash
read -p "input a num:" num1
[ "$num1" -eq "1" ] && {
echo $num1
exit 0
}
[ "$num1" -eq "2" ] && {
echo $num1
exit 0
}
[ "$num1" -ne "1" -a "$num1" -ne "1" ] && {
echo "输入1或2"
exit 1
}
安装lamp/lnmp 脚本模版
[root@wzb shell]# cat lamp_or_lnmp.sh
#!/bin/bash
path=/home/wzb/practice/shell #路径
cat <<END
1.lamp
2.lnmp
3.exit
please in a num you want to do
END
#检验是否输入的是数字
read -p "input a num:" num
[ -n "`echo "$num" | sed 's/[0-9]//g'`" ] && {
echo 'error num'
exit 1
}
#input 1
[ "$num" -eq "1" ] && {
echo "..."
#判断是否有权限执行
[ -x "$path/lamp.sh" ] || {
echo "没有权限"
exit 1
}
$path/lamp.sh
exit $?
}
[ "$num" -eq "2" ] && {
echo "..."
[ -x "$path/lnmp.sh" ] || {
echo "没有权限"
exit 1
}
$path/lnmp.sh
exit $?
}
#3 退出
[ "$num" -eq "3" ] && {
exit 0
}
#限制用户必须输入的是1,2,3
[[ ! "$num" =~ [1-3] ]] && {
echo "你要输入1,2,3"
}
创建目录或文件
在333下创建aaa和bbb,创建444
[root@wzb tom]# mkdir -pv /home/tom/{333/{aaa,bbb},444}
mkdir: 已创建目录 "/home/tom/333"
mkdir: 已创建目录 "/home/tom/333/aaa"
mkdir: 已创建目录 "/home/tom/333/bbb"
mkdir: 已创建目录 "/home/tom/444"
[root@wzb tom]# tree
.
├── 333
│ ├── aaa
│ └── bbb
└── 444
4 directories, 0 files
一 一对应的创建
[root@wzb tom]# touch {a..c}{1..3}
[root@wzb tom]# ls
a1 a2 a3 b1 b2 b3 c1 c2 c3
echo -e 支持正则
if语句
显示内存情况
free -m
[root@wzb shell]# free -m
total used free shared buff/cache available
Mem: 1953 870 468 11 613 887
Swap: 2047 0 2047
当前可用内存
[root@wzb shell]# free -m | awk 'NR==2 {print $NF}'
887
检测可用内存
[root@wzb my_prac]# cat t9.sh
#!/bin/bash
FreeMem=`free -m | awk 'NR==2 {print $NF}'`
echo $FreeMem
mess="可用为:$FreeMem"
if [ "$FreeMem" -lt "2199" ]
then
echo $mess
echo "内存不足"
fi
比较数值
[root@wzb my_prac]# cat t10.sh
#!/bin/bash
read -p "输入a和b的值" a b
if [ $a -lt $b ];then
echo 'a < b'
elif [ $1 -eq $2 ];then
echo 'a = b'
elif [ $1 -gt $2 ];then
echo 'a > b'
fi
检测nginx是否运行
[root@wzb shell]# cat nginx_test.sh
#!/bin/bash
Check(){
timeout=5
fails=0
success=0
while true
do
wget --timeout=${timeout} --tries=1 192.168.92.5 -q -o /dev/null
if [ $? -ne 0 ];then
let fails+=1
else
let success+=1
fi
if [ ${success} -ge 1 ];then
echo "success"
exit 0
fi
if [ ${fails} -ge 2 ];then
echo "no"
exit 2
fi
done
}
Check
for语句
删除文件名
创建文件
touch pic_{1..5}_finished.jpg
touch pic_{1..5}_finished.png
去掉 “finished”
#!/bin/bash
for file_name in `ls pic*`
do
#echo ${file_name//_finished/}
mv ${file_name} ${file_name//_finished/}
done
${变量//你要删除的内容}
[root@wzb ~]# echo $url
www.baidu.com.qqq
[root@wzb ~]# echo ${url//www.baidu}
.com.qqq
数组
普通数组
[root@wzb shell]# books=(linux shell awk ansible docker)
[root@wzb shell]# echo ${books[3]}
ansible
设定索引值
[root@wzb shell]# books=(linux shell awk ansible [20]=docker)
[root@wzb shell]# echo ${books[20]}
docker
关联数组
declare -a info 申明是普通数组
[root@wzb shell]# declare -A info #申明这个数组是关联数组
[root@wzb shell]# info=([name]=tianyun [sex]=male [age]=37 [height]=170 [skill]=cloud)
[root@wzb shell]# echo ${info[name]}
tianyun
数组的赋值
统计数字元素的个数
[root@wzb shell]# echo ${#info[@]}
5
获取数组索引
[root@wzb shell]# echo ${!info[@]}
name height age skill sex
索引
[root@wzb shell]# echo ${info[@]:1} #从索引为1开始获取
tianyun 170 37 cloud male
[root@wzb shell]# echo ${info[@]:1:2} #从索引为1开始获取,获取两个
tianyun 170