特殊状态变量
$?:上一次命令执行状态返回值,0正确,非0失败
$$:当前shell脚本的进程号
$!:获取上一次后台进程的id
$_:取得之前执行的命令最后一个参数
特殊状态变量查找方式: man bash
搜索special parameters
1)$? :脚本执行返回值
脚本控制返回值的用法,这个脚本执行完毕了,会返回一个数值,称呼之为返回值,学习shell函数编程以后才能彻底理解,如下只是演示:
[root@hadoop102 ~]# vim jiujiu.sh
[root@hadoop102 ~]# cat jiujiu.sh
#!/bin/bash
# $#:取参数总个数 -ne:不等于 2:满足的条件 &&:与(并且)的意思 []:里面是一个条件判断
[ $# -ne 2 ] && {
echo "必须是两个参数" #如果不是2个参数则执行本行代码输出
exit 110 #终止程序运行。且返回110状态码,提供给当前shell函数的$?变量,若是在函数里,可以用return 110用法
}
echo "输入参数正确"
[root@hadoop102 ~]# bash jiujiu.sh 15 20 35
必须是两个参数
[root@hadoop102 ~]# echo $?
110
[root@hadoop102 ~]# bash jiujiu.sh 15 20
输入参数正确
[root@hadoop102 ~]# echo $?
0
2)$!:获取上一次后台执行的程序PID
[root@hadoop102 ~]# nohup ping baiduo.com & 1> /dev/null # 让后台运行ping百度,且日志写入/dev/null
[1] 3182
[root@hadoop102 ~]# nohup: ignoring input and appending output to ‘nohup.out’
[root@hadoop102 ~]# ps -ef | grep 3182
root 3182 2586 0 19:18 pts/0 00:00:00 ping baiduo.com
root 3184 2586 0 19:19 pts/0 00:00:00 grep --color=auto 3182
[root@hadoop102 ~]# echo $!
3182
3)$$ :获取当前脚本的PID
[root@hadoop102 ~]# vim jiujiu.sh
[root@hadoop102 ~]# cat jiujiu.sh
#!/bin/bash
# $#:取参数总个数 -ne:不等于 2:满足的条件 &&:与(并且)的意思 []:里面是一个条件判断
[ $# -ne 2 ] && {
echo "必须是两个参数" #如果不是2个参数则执行本行代码输出
exit 110 #终止程序运行。且返回110状态码,提供给当前shell函数的$?变量,若是在函数里,可以用return 110用法
}
echo "输入参数正确"
echo "当前的脚本id是: $$"
[root@hadoop102 ~]# bash jiujiu.sh 12 25
输入参数正确
当前的脚本id是: 3188
注:脚本PID每次执行都会发生改变
4)$_ :获取上次命令的最后一个参数
[root@hadoop102 ~]# bash jiujiu.sh 12 25
输入参数正确
当前的脚本id是: 3200
[root@hadoop102 ~]# echo $_
25
shell子串
bash一些基础的内置命令
echo
eval
exec
export
read
shift
1)echo命令
输出,打印
选项:
-n:不换行输出
-e:解析字符串中的特殊符号
\n :换行
\r :回车
\t :制表符 表示四个空格
\b :退格
#不换行输出打印
[root@hadoop102 ~]# echo 你好
你好
[root@hadoop102 ~]# echo 你好;echo 你真胖 (不加-n,则当成两个命令换行输出)
你好
你真胖
[root@hadoop102 ~]# echo -n 你好;echo 你真胖 (加-n表示不换行输出)
你好你真胖
[root@hadoop102 ~]# echo -n 你好;echo -n 你真胖 (前后命令都加-n)
你好你真胖[root@hadoop102 ~]# (因为都加了-n,所以命令提示符不换行而跟在输出结果后面
#识别字符串特殊符号
[root@hadoop102 ~]# echo '今晚月色\n真美' (认识别不了特殊符号,当普通字符串输出)
今晚月色\n真美
[root@hadoop102 ~]# echo -e '今晚月色\n真美' (加上-e,解析字符串特殊符号)
今晚月色
真美
# 打印 printf
#printf自动识别特殊符号,因为没加-n所有没换行输出
[root@hadoop102 ~]# printf '你好\t我是\t吴彦祖'
你好 我是 吴彦祖[root@hadoop102 ~]# ^C
[root@hadoop102 ~]# printf '你好\t我是\t吴彦祖\n'
你好 我是 吴彦祖
[root@hadoop102 ~]#
2)eval
执行多个命令
# 先执行ls在执行cd命令
[root@hadoop102 ~]# eval ls;cd jiujiu
anaconda-ks.cfg dir1 fine9 jiujiu jiujiu.sh like.sh nohup.out
[root@hadoop102 jiujiu]#
3)exec
不创建子进程,执行后续命令,且执行完毕后,自动exit
# 先切入普通用户执行命令,执行完毕后exit退出
[root@hadoop102 ~]# su jiujiu
[jiujiu@hadoop102 root]$ exec date
Tue Mar 28 20:21:13 CST 2023
[root@hadoop102 ~]#
shell子串的花式用法
shell
1.基础语法
${变量} : 返回变量值
[root@hadoop102 ~]# name='jiujiu'
[root@hadoop102 ~]# echo ${name}
jiujiu
${#变量} :返回变量长度,字符长度
[root@hadoop102 ~]# name='jiujiu'
[root@hadoop102 ~]# echo ${#name}
6
${变量:start} :返回变量start数值之后的字符
每个变量值都是有索引和序号的,
start:表示数字
name=jiujiu
0 1 2 3 4 5
echo ${name:5} # 表示取变量name值的从索引5开始到后面,包括索引5所在的字符
[root@hadoop102 ~]# name='jiujiu'
[root@hadoop102 ~]# echo ${name:5}
iu
${变量:start:length} 提取start之后的length限制的字符
length :表示限制数字
name=jiujiu
0 1 2 3 4 5
echo ${name:4} # 表示取变量name值的从索引4开始到后面,包括索引4所在的字符
[root@hadoop102 ~]# name='jiujiu'
[root@hadoop102 ~]# echo ${name:2:4} 设置起点以及元素长度
iuj
${变量#world} 从变量开头删除最短匹配的word子串
# 井号后面跟需删出的字符,从开头开始删除
[root@hadoop102 ~]# name2="abcABCabcABC"
[root@hadoop102 ~]# echo ${name2#a*c}
ABCabcABC
${变量##world} 从变量开头,删除最长匹配的word
[root@hadoop102 ~]# name2="abcABCabcABC"
[root@hadoop102 ~]# echo ${name2##a*c}
ABC
${变量%world} 从变量结尾删除最短的word
# %号后面跟需删出的字符,只不过是从后面开始删除
[root@hadoop102 ~]# name='hello,world'
[root@hadoop102 ~]# echo ${name%w}
hello,
${变量%%world} 从变量结尾开始删除最长匹配的word
${变量} = "Hello world! Goodbye world!"
则通过${变量%%world}操作,将从变量结尾开始删除最长匹配的"world",得到的结果为:"Hello world! Goodbye "
${变量/pattern/string} 用string代替第一个匹配的pattern
[root@hadoop102 ~]# name='jiujiu'
[root@hadoop102 ~]# echo ${name/jiu/999}
999jiu
${变量//pattern/string} 用string代替所以的pattern
[root@hadoop102 ~]# name='jiujiu'
[root@hadoop102 ~]# echo ${name//jiu/999}
999999
字串的实际案列:
shell变量截取字符串通常有两种方式:
从指定位置开始截取和指定字符(子字符串)开始截取。
从指定位置开始截取
这种方式需要两个参数,除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串
既然需要指定起始位置,那么就涉及到计数方向的问题,到底是从字符串左边开始计算,还是从字符串右边开始计算,
答案是:shell同时支持两种计数方式
1)从字符串左边开始计算
# ${string:start:length}
其中:
string:是要截取的字符串
start:是起始位置(从左边开始,从0开始计数)
length:是要截取的长度(省略的话表示直到字符串的末尾)
计算变量长度的各种方法
多种统计方法
name='hello,world'
#方法一 最快的统计方式
[root@hadoop102 ~]# name='jiujiu'
[root@hadoop102 ~]# echo ${#name}
6
#方法二
[root@hadoop102 ~]# name='jiujiu'
[root@hadoop102 ~]# echo $name | wc -L
6
#方法三
利用数值计算expr(shell中用于数值计算的)
[root@hadoop102 ~]# name='hello,world'
[root@hadoop102 ~]# expr length ${name}
11
#方法四
awk统计长度,length函数
[root@hadoop102 ~]# echo ${name} | awk '{print length($0)}'
11
统计速度比较:
time命令,统计命令时长
for循环的shell编程
# for 语法
# for number in {1..100}
for [变量] in [循环的序列,比如1~100]
do
echo number #你要做什么事
done
写在一行的写法:[root@hadoop102 ~]# for i in {1..10};do echo $i;done
1
2
3
4
5
6
7
8
9
10
#方法一 ${#变量}
拓展:seq生成序列,默认以空格为分隔符,-s指定分隔符
[root@hadoop102 ~]# time for i in {1..10000};do jj=`seq -s ":" 100`;echo ${#jj} &>/dev/null;done
real 0m30.010s #实际运行时间
user 0m17.750s # 用户态执行的时间
sys 0m11.981s #内核态执行的时间
#方法二 wc -L
[root@hadoop102 ~]# time for i in {1..10000};do jj=`seq -s ":" 100`;echo ${jj} | wc -L &>/dev/null;done
real 1m11.701s
user 0m39.893s
sys 0m31.073s
# 方法三 expr命令的length函数
[root@hadoop102 ~]# time for i in {1..10000};do jj=`seq -s ":" 100`;expr length ’${jj}‘ &>/dev/null;done
real 1m1.522s
user 0m37.090s
sys 0m23.817s
# 方法四 awk加工处理
[root@hadoop102 ~]# time for i in {1..10000};do jj=`seq -s ":" 100`;echo '${jj}' | awk '{print length($0)}' &>/dev/null;done
real 1m19.003s
user 0m42.968s
sys 0m35.314s
#总结
shell编程,尽量使用linux内置命令,内置的操作和内置的函数,效率最高,尽可能的减少管道符的操作
字符串的截取
删除匹配到的字符串
[root@hadoop102 ~]# name="I am jiujiu"
[root@hadoop102 ~]# echo ${name:2:3}
am
[root@hadoop102 ~]# echo ${name:2:5}
am ji
# # ##
[root@hadoop102 ~]# name2="abcABCabcABC"
[root@hadoop102 ~]# echo ${name2#a*c}
ABCabcABC
[root@hadoop102 ~]# echo ${name2##a*c}
ABC
# % %%
[root@hadoop102 ~]# name2="abcABC123ABCabc"
[root@hadoop102 ~]# echo ${name2%a*c}
abcABC123ABC
[root@hadoop102 ~]# echo ${name2%%a*c}
#因为是从a开始匹配到c结尾,所以整行都给截取了输出为空
替换字符串
# / /
[root@hadoop102 ~]# jj="hello,girl,you Beautiful"
[root@hadoop102 ~]# echo ${jj/girl/boy}
hello,boy,you Beautiful
#全局替换
[root@hadoop102 ~]# jj="hello,girl,you Beautiful"
[root@hadoop102 ~]# echo ${jj//o/O}
hellO,girl,yOu Beautiful
[root@hadoop102 ~]# jj="hello,girl,you Beautiful;hello,boy,you Beautiful"
[root@hadoop102 ~]# echo ${jj//hello/HELLO}
HELLO,girl,you Beautiful;HELLO,boy,you Beautiful
删除文件名案例
# 测试准备
[root@hadoop102 sub_str]# ll
total 0
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_1_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_1_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_2_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_2_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_3_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_3_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_4_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_4_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_5_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_5_like.npg
#题:去掉所有文件的like字符信息
解答:
# 思路一:单个文件去掉这个字符
[root@hadoop102 sub_str]# mv jiujiu_1_like.jpg jiujiu_1.jpg
[root@hadoop102 sub_str]# ll
total 0
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_1.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_1_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_2_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_2_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_3_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_3_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_4_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_4_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_5_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_5_like.npg
# 思路二:利用变量的字串功能,去掉后缀
[root@hadoop102 sub_str]# f=jiujiu_1_like.jpg
[root@hadoop102 sub_str]# echo ${f/_like/}
jiujiu_1.jpg
# 思路三:利用反引号的功能
[root@hadoop102 sub_str]# f=jiujiu_1_like.npg
[root@hadoop102 sub_str]# mv $f `echo ${f//_like/}`
[root@hadoop102 sub_str]# ll
total 0
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_1.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_1.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_2_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_2_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_3_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_3_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_4_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_4_like.npg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_5_like.jpg
-rw-r--r--. 1 root root 0 Mar 30 20:06 jiujiu_5_like.npg
# 思路四:批量文件名替换 只修改所有的jpg文件
[root@hadoop102 sub_str]# ls *like*.jpg
jiujiu_2_like.jpg jiujiu_3_like.jpg jiujiu_4_like.jpg jiujiu_5_like.jpg
[root@hadoop102 sub_str]# for jj in `ls *like*.jpg`;do echo $jj;done
jiujiu_2_like.jpg
jiujiu_3_like.jpg
jiujiu_4_like.jpg
jiujiu_5_like.jpg
[root@hadoop102 sub_str]# for jj in `ls *like*.jpg`;do mv $jj `echo ${jj/_like/}`;done
[root@hadoop102 sub_str]# ls *.jpg
jiujiu_1.jpg jiujiu_2.jpg jiujiu_3.jpg jiujiu_4.jpg jiujiu_5.jpg
特殊shell扩展变量
变量的处理
这四个扩展变量,都属于对变量的值进行判断丶处理
: -
如果parameter变量值为空,返回word字符串
${parameter:-word}
: =
如果para变量值为空,则word替代变量值,且返回其值
${parameter:=word}
: ?
如果para变量值为空,word当作s't'derr输出,否则输出变量值用于设置变量为空导致错误时,返回的错误信息
${parameter:?word}
: +
如果para为空变量为空,什么都不做,否则word返回
${parameter:+word}
实际案例
: -
作用:判断如果变量值为空,就返回后面的字符信息,可以通过result变量去接收
# 变量值为空的情况下
[root@hadoop102 ~]# echo $jiujiu # 此变量为空
[root@hadoop102 ~]# result=${jiujiu:-hello} #定义变量名,
[root@hadoop102 ~]# echo $jiujiu #变量还是为空
[root@hadoop102 ~]# echo $result #echo result输出变量值
hello
# 变量值不为空的情况
[root@hadoop102 ~]# jiujiu=125
[root@hadoop102 ~]# result2=${jiujiu:-hello}
[root@hadoop102 ~]# echo $jiujiu
125
[root@hadoop102 ~]# echo $result2 #在变量值不为空的情况下,则:-赋值失效
125
:=
作用:如果变量值为空,后面的值,赋值给接收者,以及变量本身
# 变量值为空的前提下
[root@hadoop102 ~]# echo $jiujiu $result
[root@hadoop102 ~]# result=${jiujiu:=hello}
[root@hadoop102 ~]# echo $jiujiu;echo $result
hello
hello
# 变量值不为空的前提下
[root@hadoop102 ~]# echo $jiujiu
hello
[root@hadoop102 ~]# result2=${jiujiu:=daxigaun}
[root@hadoop102 ~]# echo $jiujiu;echo $result2
hello
hello # 变量值有值的情况下,:=赋值失效 result2取jiujiu的变量值
:?
作用:当变量值为空时,主动输出一个错误信息
# 变量值为空的前提下
[root@hadoop102 ~]# echo ${new_jiujiu}
[root@hadoop102 ~]# echo ${new_jiujiu:?该变量值为空}
-bash: new_jiujiu: 改变量值为空
[root@hadoop102 ~]# new_jiujiu=hello
[root@hadoop102 ~]# echo ${new_jiujiu:?改变量值为空}
hello
:+
作用:当变量为空,什么都不做,否则字符返回个接收者
# 变量值为空的前提下
[root@hadoop102 ~]# echo $jiujiu
[root@hadoop102 ~]# echo ${jiujiu:+hei}
# 变量值不为空的前提下
[root@hadoop102 ~]# jiujiu=hello
[root@hadoop102 ~]# echo ${jiujiu:+hei}
hei
[root@hadoop102 ~]# like=${jiujiu:+nihao}
[root@hadoop102 ~]# echo $like
nihao
实践案例
数据备份,删除过期数据的脚本
find xagrs :搜索,且删除
# 删除7天以上的过期文件
find 需要搜索的目录 -name 你要搜索的文件名字 -type 文件类型 -mtime +7 | xargs
rm -f
[root@hadoop102 ~]# vim del_data.sh
[root@hadoop102 ~]# cat del_data.sh
#!/bin/bash
#shell语法是否有bug
#希望删除某个数据文件夹的备份文件
#定义变量值:dir_path="/data/mysql_back_data/"
#如果有bug歧义,就会在当前目录,搜索,删除
#find ${dir_path] -name '*.tar.gz' -type f -mtime +7 | rm -rf
#变量名的扩展
find ${dir_path:=/data/mysql_back_data/} -name '*.tar.gz' -type f -mtimr +7 | rm -rf
父子shell
1丶source和点,执行脚本,只在当前的shell环境中执行生效
2丶指定bash sh解释器运行脚本,是开启subshell,开启子shell运行脚本命令
3丶./script,都会指定shebang,通过解释器运行,也是开启subshell运行命令
父shell概念
通过命令pstree看到此状态就表示在父shell
[root@hadoop102 ~]# pstree
systemd─┬─NetworkManager───2*[{NetworkManager}]
├─VGAuthService
├─agetty
├─auditd───{auditd}
├─crond
├─dbus-daemon───{dbus-daemon}
├─lvmetad
├─master─┬─pickup
│ └─qmgr
├─polkitd───6*[{polkitd}]
├─rsyslogd───2*[{rsyslogd}]
├─sshd───sshd───bash───pstree # shell环境所在
├─systemd-journal
├─systemd-logind
├─systemd-udevd
├─tuned───4*[{tuned}]
└─vmtoolsd───2*[{vmtoolsd}]
# ps进程管理命令查看
-f:显示UID,PID,PPID
-e:列出所有进程的信息,如同-A选项
#通过一条命令,检查父子shell的关系
ps -ef --forest
子shell概念
#多个子shell概念
[root@hadoop101 ~]#bash
[root@hadoop101 ~]#sh
sh-4.2# bash
[root@hadoop101 ~]#ps -ef --forest
root 1268 1018 0 11:11 ? 00:00:00 \_ sshd: root@pts/0
root 1272 1268 0 11:11 pts/0 00:00:00 \_ -bash
root 1309 1272 0 11:32 pts/0 00:00:00 \_ bash
root 1322 1309 0 11:35 pts/0 00:00:00 \_ bash
root 1333 1322 0 11:35 pts/0 00:00:00 \_ sh
root 1334 1333 0 11:35 pts/0 00:00:00 \_ bash
root 1346 1334 0 11:36 pts/0 00:00:00 \_ ps -ef --forest
root 1019 1 0 11:11 ? 00:00:00 /usr/bin/python2 -Es /usr/sbin/tuned -l -P
#只要输入exit就能退出子shell环境了
创建进程列表(创建子shell)
#需要执行一系列的命令,该如何去操作呢?
#是这样:“ls;pwd;cd /tmp;”嘛?
NO,NO,NO
#所谓进程列表,列表列表,就是指被包括起来的数据
shell的进程列表理念,需要用()小括号,如下执行方式,就称之为进程列表
[root@hadoop101 ~]#(pwd;(ls;(echo $BASH_SUBSHELL))) //开启小括号,就是开启子shell运行命令
#作用:检测是否在子shell环境当中
linux有个默认有关shell的变量
$BATH_SUBSHELL # 该变量值的特点,如果是0就是在当前shell环境中执行的;如果非0,就是开辟子shell环境去运行
## 检测是否开启了子shell运行命令
[root@hadoop101 ~]#pwd;ls;cd /opt/;ls;ll;echo $BASH_SUBSHELL
/root
git
total 0
drwxr-xr-x. 3 root root 23 Mar 31 16:26 git
0
#明确开启子shell运行的命令进程列表,并开启子shell运行
[root@hadoop101 ~]#(pwd;ls;cd /opt/;ls;ll;echo $BASH_SUBSHELL) //一个括号表示开启一个子shell去运行
/root
git
total 0
drwxr-xr-x. 3 root root 23 Mar 31 16:26 git
1 # 1表示子shell个数
子shell的意义:
子shell嵌套运行
刚才是一个小括号,开启一个子shell运行命令,还可以嵌套多个
[root@hadoop101 ~]#(pwd;(ls;cd /opt/;(ls;ll;(echo $BASH_SUBSHELL))))
/root
git
total 0
drwxr-xr-x. 3 root root 23 Mar 31 16:26 git
4
#利用括号,开启子shell得到理念,以及检查,在shell脚本开发中,经常会用子shell进行多进程的处理,提高程序并发执行效率
内置命令/外置命令
什么是内置命令?什么外置命令呢
内置命令:在系统启动是就加载入内存,常驻内存,执行效率更高,但是占用资源 (比如cd)
外置命令:用户需要从硬盘中读取程序文件,再读入内存加载 (
外置命令
外置命令,也称之为自己单独下载的文件系统命令,处于bash丶shell之外的程序
#外部命令一般储存在:
/bin
/usr/bin
/sbin
/usr/sbin
#可以通过linux中的命令type去判断命令是否属于内置还是外置
[root@hadoop101 ~]#type cd
cd is a shell builtin
外置的命令特点:
一定会开启子进程进程
#bash在执行外部命令时,一定会开启一个子进程去执行命令
内置命令
特点:内置命令不会产生子进程
内置命令和shell是为一体的,是shell的一部分,不需要单独去读取某个文件,系统启动后就执行在内存中了
#查看linux系统的内置shell命令
compgen -b
shell脚本开发
内容回顾
1丶shell执行多行命令
[root@hadoop101 ~]#pwd;ls;cd /opt/;ls;ll;echo $BASH_SUBSHELL //用分号把命令隔开
#我们可以把复杂的命令执行过程,通过逻辑代码,组成一个脚本文件,再去执行该文件就好了
2丶shebang 解释器
#!/bin/bash
#!/bin/perl
#!/bin/python
写什么样的脚本就指定什么shebang
3丶执行脚本的方式
bash
sh
./ //需赋予脚本权限
source
.
4丶echo命令,在linux下的格式化打印
#执行脚本如下
[root@hadoop101 ~]#cat echo.sh
#!/bin/bash
echo "The time and date are: "
date
echo "Let's see who's logged into the system: "
who
[root@hadoop101 ~]# ./echo.sh
-bash: ./echo.sh: Permission denied
[root@hadoop101 ~]#chmod +x echo.sh
[root@hadoop101 ~]# ./echo.sh
The time and date are:
Sat Apr 1 16:11:51 CST 2023
Let's see who's logged into the system:
root pts/0 2023-04-01 12:32 (192.168.16.1)
#echo 和转义符的概念
所谓转义符就是还原符号原本的意思,不会被阅读为特殊含义
" "
''
#双引号的结果
[root@hadoop101 ~]#echo "这个手机的价格是: $998"
这个手机的价格是: 98 //不加转义符就默认识别为$9 98
[root@hadoop101 ~]#echo "这个手机的价格是: \$998"
这个手机的价格是: $998
#单引号的结果
[root@hadoop101 ~]#echo '这个手机的价格是: $998'
这个手机的价格是: $998 //原文输出
区别:单引号不做任何语法的翻译,双引号支持语法翻译
5丶变量在脚本中的作用
变量被引用的时候,会赋予其值,脚本中的变量,在shell中执行完毕后就会消失,根据执行的方式决定是否消失
[root@hadoop101 ~]#vim jiu.sh
[root@hadoop101 ~]#cat jiu.sh
#!/bin/bash
name="jiujiu"
days=10
echo "$name学shell脚本$days天"
[root@hadoop101 ~]#bash jiu.sh
jiujiu学shell脚本10天
[root@hadoop101 ~]#echo $name
[root@hadoop101 ~]#echo $days
[root@hadoop101 ~]#source jiu.sh
jiujiu学shell脚本10天
[root@hadoop101 ~]#echo $name
jiujiu
[root@hadoop101 ~]#echo $days
10
#注:当你用不同的方式。执行脚本,产生后的后果也不一样
source和. 是在当前的shell环境中加载变量执行脚本
bash和sh 去执行脚本的时候,是开启子shell运行的,变量也是在子shell环境中加载,子shell退出后,变量也就消失了
记住,对变量名的获取,一定要加上美元符号$,否则就错误
6丶Linux、shell变量的替换引用
shell一大特性,就是可以从命令的执行结果中,再提取结果,因此特别适合编写脚本
$()
``
#拓展:下列符号代表意思
${}:取出变量值
[root@hadoop101 ~]#name=jiujiu
[root@hadoop101 ~]#echo ${name}
jiujiu
$():在括号中执行命令,且拿到命令的执行结果
[root@hadoop101 ~]#$(date)
-bash: Sat: command not found
[root@hadoop101 ~]#echo "当前时间是: $(date)"
当前时间是: Sat Apr 1 16:55:32 CST 2023
``:在括号中执行命令,且拿到命令的执行结果
[root@hadoop101 ~]#`whoami`
-bash: root: command not found
[root@hadoop101 ~]#echo "当前登录用户: `whoami`"
当前登录用户: root
():开启子shell执行命令结果
[root@hadoop101 ~]#(pwd;ls;echo $BASH_SUBSHELL)
/root
jiu.sh
1
$vars:取出变量值
[root@hadoop101 ~]#name=jiujiu
[root@hadoop101 ~]#echo ${name}
jiujiu
数值计算
运算符号,linux的用于计算的命令
常见的运算符号
算术运算符 | 意义(*表示常用) |
---|---|
+丶- | 加法(或正号)丶减法(或负号)* |
*丶/丶% | 乘法丶除法丶取余(取模)* |
** | 幂运算* |
++丶-- | 增加及减少,可前置也可放在变量结尾* |
!丶&&丶|| | 逻辑非(取反)丶逻辑与(and)丶逻辑或(or)* |
<丶<=丶>丶>= | 比较符号(小于,小于等于,大于,大于等于) |
==丶!=丶= | 比较符号(相等,不相等,对于字符串“=”也可以表示相当于) |
<<丶>> | 向左移位,向右移位 |
~丶|丶&丶^ | 按位取反,按位异位,按位与,按位或 |
=丶+=丶-=丶*=丶/=丶%= | 赋值运算符,例如a+=1相当于a=a+1,a-=1相当于a=a-1* |
常见的运算命令
运算操作符与运算命令 | 意义 |
---|---|
(()) | 用于整数运算的常用运算符,效率很高 |
let | 用于整数运算。类似于“(())” |
expr | 可用于整数运算,但还有很多其他的功能 |
bc | Linux下的下一个计算器程序(适合整数及小数运算) |
$[] | 用于整数运算 |
awk | awk即可以用于整数运算,也可以用于小数运算 |
declare | 定义变量值和属性吗,-i参数可以用于定义整形变量,做运算 |
用于数值计算的命令
shell的一些命令,只支持整数的运算,小数的计算需要如bc这样的命令才能支持
双小括号(())
运算操作符和运算命令 | 意义 |
---|---|
((i=i+1)) | 此种书写方法为运算后赋值法,即将i+1的运算结果赋值给变量i。注意,不能用“echo ((i=i+1))“的形式输出表达式的值,但可以用echo $((i=i+1))输出其值 |
i=$((i+1)) | 可以在(())前加$符,表示将表达式运算后赋值给i |
$((8>7&&5==5)) | 可以进行比较操作,还可以加入逻辑与和逻辑或,用于条件判断 |
echo $((2+1)) | 需要直接输出运算表达式的运算结果时,可以在(())前加上$符 |
#有关逻辑运算, 真,假的区别 真为1 假为0
[root@jiujiu ~]# echo $((10>8)) //10大于8是正确的
1
[root@jiujiu ~]# echo $((10>15)) //10大于15是错误的
0
# 逻辑与的用法:&& //符号两边结果都为真,结果才能为真,输出值才能为1,否则都是0
[root@jiujiu ~]# echo $((10>15&&4<5))
0
[root@jiujiu ~]# echo $((10<15&&4<5))
1
[root@jiujiu ~]# echo $((20>15&&4<5))
1
# 实践双小括括号用法
加减乘除
[root@jiujiu ~]# #加减乘除
[root@jiujiu ~]# echo $((6+4))
10
[root@jiujiu ~]# echo $((6-4))
2
[root@jiujiu ~]# echo $((1-4))
-3
[root@jiujiu ~]# echo $((6*4))
24
[root@jiujiu ~]# echo $((8/4))
2
[root@jiujiu ~]# echo $((7/4)) //除法取商
1
[root@jiujiu ~]# echo $((10/4))
2
#幂运算
[root@jiujiu ~]# echo $((5**2)) //5的二次方
25
[root@jiujiu ~]# echo $((5**3)) //5的三次方
125
#取余数/取模
[root@jiujiu ~]# echo $((5%3)) //5除3得商1余2
2
[root@jiujiu ~]# echo $((6%3)) //6除3得商2余0
0
[root@jiujiu ~]# echo $((10%3)) //10除3得商3余1
1
#结合变量计算
[root@jiujiu ~]# num=1 //赋值为1
[root@jiujiu ~]# ((num*5)) //做计算乘5
[root@jiujiu ~]# echo $num //直接echo $输出值不变
1
[root@jiujiu ~]# ((num=num*5)) //定义num=num*5
[root@jiujiu ~]#echo $((num=num*5)) // 输出结果为5
5
[root@jiujiu ~]# echo $((num=num*5)) //每重新输出一次,值都会递增
25
[root@jiujiu ~]# echo $((num=num*5))
125
#错误语法
[root@jiujiu ~]# echo ((num=num*5)) //变量前需加上$符才行
-bash: syntax error near unexpected token `('
#复杂数学运算
[root@jiujiu ~]# ((a=5+4**2-8%5)) //a=5+16-3
[root@jiujiu ~]# echo $a
18
[root@jiujiu ~]# b=((5+4**2-8%5)) //报错是因为要取双括号里的值前面得加上$
-bash: syntax error near unexpected token `('
[root@jiujiu ~]# b=$((5+4**2-8%5))
[root@jiujiu ~]# echo $b
18
特殊符号运算
符号 | 意义 |
---|---|
++ | 加一 |
-- | 减一 |
注意事项:
# ++ 有个坑记得避免
++a 先计算+1,在赋值给a
a++ 先对变量a操作,在进行+1
#例:++a a++
[root@localhost ~]# a=5
[root@localhost ~]# echo $((++a)) //先进行+1也就是5+1 ,再赋值给a 所以输出等于6
6
[root@localhost ~]# echo $((a++)) //因为上面重新赋值的原因,所有这里a值还是输出为6
6
[root@localhost ~]# echo $a //这里为什么输出结果为7呢?因为a++是先对变量a操作,在进行+1。所有这也就是上面输出还是为6的原因,对a变量操作过后再进行+1,这时我们在输出a值结果就为7
7
脚本开发,复杂的对用户输入判断的脚本开发
注:该脚本开发,利用了很多后期学习内容,涉及shell函数丶if逻辑判断
例:脚本开发
- 1.想好脚本的功能丶作用丶以及需求
- 转换为shell代码
#开发一个,接受用户输入的数字,且对运算符号判断,最终得出结果的一个计算脚本
#1.接受用户输入
#2.对用户输入的是不是数字进行判断
#对输入的运算符号进行判断
#最终进行结果计算,输出打印
#!/bin/bash
#脚本开发
#函数的作用,就是把你写的功能代码,进行打包,封装成一个函数名,绕后调用该函数名,函数就会执行
#函数中的代码就会执行
#涵数的名字
#这个函数作用是,告诉用户,你到底应该输入什么的一个简单 提示函数
print_usage(){
printf "Please enter on integer\n"
#给脚本的执行结果,赋予一个状态码,退出码
exit 1
}
#接受用户输入的命令-p参数后面写,给用户考到的提示信息
#read -p "提示信息" 接受用户输入的变量
read -p "Please input your number: " firstnum
#进行对用户输入判断if 语句 注意语法格式!
#限制用户必须输入纯数字
#if语法必须写一个中括号,而且中括号里面前后必须空格
#-n 是if的语句,对字符串判断,如果字符串为空,条件就不成立。如果字符串不为空,条件成立
#sed的作用是把变量字符串进行替换,把所有的数字替换为空,那么就剩下其他非数字的内容了
if [ -n "`echo $firstnum|sed 's/[0-9]//g'`" ]
then
print_usage
fi
#上诉代码的意思是如果字符串不为空就去执行函数名。如果为空,则跳过该段代码
#上面的代码都是对用户输入进行判断 //希望用户输入3+5
#此时,对运运算符进行输入
read -p "Please input your operator:" operator
#对运算符进行判断
#限制在+ - * /四个运算符号
if [ "${operator}" != "+" ] && [ "${operator}" != "-" ] && [ "${operator}" != "*" ] && [ "${operator}" != "/" ]
then
echo "只允许输入+|-|*|/"
exit 2
fi
#对第二个变量进行处理
read -p "Pleasr input second number:" secondnum
if [ -n "`echo $secondnum|sed 's/[0-9]//g'`" ]
then
print_usage
fi
#最后进行数值计算,利用双小括号计算
echo "${firstnum}${operator}${secondnum}结果是:$((${firstnum}${operator}${secondnum}))"
简单的运算玩法:
#!/bin/bash
echo $(($1))
[root@localhost ~]# cat jisuan.sh
#!/bin/bash
echo $(($1))
[root@localhost ~]# sh jisuan.sh 6*8
48
[root@localhost ~]# sh jisuan.sh 6 * 8 //shell是以空格来区分命令的
6
[root@localhost ~]# sh jisuan.sh "6 * 8" //可以用双引号引起来使用
48
let命令计算
简单计算:
#let命令的执行,效果等同于双小括号 但是,双小括号效率更高
[root@localhost ~]# num=5
[root@localhost ~]# num=num+5 //此种方式不会重新赋值于num
[root@localhost ~]# echo $num
num+5
[root@localhost ~]# num=$num+5
[root@localhost ~]# echo $num
num+5+5
[root@localhost ~]# unset num
[root@localhost ~]# num=5
[root@localhost ~]# let num=num+5 //使用let计算
[root@localhost ~]# echo $num
10
[root@localhost ~]# num2=5
[root@localhost ~]# echo $((num2=num2+10))
15
实践:复杂的let脚本开发(开发,检测nginx服务是否运行的脚本)
#!/bin/bash
CheckUrl(){
timeout=5
#相当于一个计算器
fails=0
success=0
#循环的检测,循环执行一个命令
while true
do
wget -- timeout=$ {timeout} --tries=1 http:/ /pythonav.cn/ -q -o /dev/null ##此行填写nginx网站地址
if [ $? -ne 0 ]
then
let fails=fails+1 #失败次数加一
else
let success=1
fi
#判断当成功次数大于1的时候,就可以得出该网站是正确访问
if [ ${success} -ge 1 ]
then
echo "该网站正确运行"
#返回一个状态码
exit 0
fi
#当错误次数的大于2的时候报警
if [ ${fails} -ge 2 ];then
echo "网站有问题"
exit 2
fi
done
}
CheckUrl #函数写完一定要写变量
expr
简单的计算执行命令
实践
expr必须是传入参数的形式
语法: expr 参数 运算符号 参数
expr 5 + 5
#加减乘除
[root@localhost ~]# expr 5 * 5 //expr并不是很好用,因为它是基于空格传入参数,但是在sehll里一些元字符都是有特殊含义的,expr识别不了,只能加上转义字符\
expr: syntax error
[root@localhost ~]# expr 5 \* 5
25
#逻辑判断 0为错误 1为正确
[root@localhost ~]# expr 5 \> 6
0
[root@localhost ~]# expr 9 \> 6
1
#求长度
length内置函数
[root@localhost ~]# expr length shajdfhahrf
11
[root@localhost ~]# expr length 123456789
9
expr模式匹配
expr命令也支持模式匹配功能
expr的两个特殊符号
: 冒号,计算字符串的字符数量,
.* 任意的字符重复0此或者多次
[root@localhost ~]# expr 123456789 ":" ".*"
9
#最后的字符可以自定义
[root@localhost ~]# expr 11111er21154564 ":" ".*"
15
[root@localhost ~]# expr 11111er21154564 ":" ".*e"
6
[root@localhost ~]# expr 11111er21154f5564 ":" ".*f"
13
[root@localhost ~]# expr jiujiu.png ":" ".*\.jpg"
0
[root@localhost ~]# expr jiujiu.jpg ":" ".*\.jpg"
10
[root@localhost ~]# expr jiujiu.jpog ":" ".*\.jpg"
0
[root@localhost ~]# expr jiujiu.jpgggggggggggggg ":" ".*\.jpg" //之后匹配到jpg结尾为止
10
expr命令判断文件名后缀是否合法
需求:执行脚本,传入一个文件名,判断是否为.jpg图片
思路:
1.先明白expr命令的匹配模式功能,字符数匹配上了才能统计长度,匹配不上就返回0,在shell中0为假,非0就为中
对真假条件判断的不同条件执行
if
else语句
--------------------------------------------------------------------------
#!/bin/bash
if expr "$1" ":" ".*\.jpg" &> /dev/null
then
echo "这的确是以.jpg结尾的"
else
echo "这不是以.jpg结尾的"
fi
#找出长度小于3的单词 -lt=小于 -le=小于等于
#!/bin/bash
for i in hello I am is Mr wang
do
if [ `expr length $i` -lt 3 ]
then
echo $i
fi
done
bc
bc命令是当作计算器来使用的,而且是命令行的计算器
#交互式的操作,支持小数点的运算
[root@xbs ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
5+5
10
5/5
1
5-5
0
5*5
25
1.1*5
5.5
#bc命令结合管道符来进行计算数学
[root@xbs ~]# echo '4+4'
4+4
[root@xbs ~]# echo '4+4'| bc
8
[root@xbs ~]# echo '4*4'
4*4
[root@xbs ~]# echo '4*4'| bc
16
#bc案例:
计算1-1000的总和
#脚本编写思路:如何生成1+2+3+4+....+999+1000
可以利用echo{}输出序数
利用tr替换数值中间的空格为“+”号
#利用bc计算
[root@xbs ~]# num=`echo {1..100} | tr ' ' '+'` | bc
5050
[root@xbs ~]# seq -s "+" 100 // -s 指定分隔符
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@xbs ~]# seq -s "+" 100 | bc
5050
#利用双括号来计算
[root@xbs ~]# echo $((`seq -s "+" 100`))
5050
#利用expr命令计算,稍许复杂,expr命令是接受多个参数来计算的
[root@xbs ~]# seq -s "+" 100 | xargs expr //因为expr计算,数值要与运算符号要有空格
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@xbs ~]# seq -s " + " 100 | xargs expr
5050
#awk计算数值
[root@xbs ~]# echo '5.5 4.5' | awk '{print $1+$2}'
10
[root@xbs ~]# echo '5.5 4.5' | awk '{print $1*$2}'
24.75
[root@xbs ~]# echo '5.5 4.5' | awk '{print $1+2*$2}' //遵循数学计算规则,先乘后除
14.5
#中括号计算数值
语法:$[表达式]
[root@xbs ~]# i=5
[root@xbs ~]# num=$[i+5]
[root@xbs ~]# echo $num
10
[root@xbs ~]# num=$[i+10]
[root@xbs ~]# echo $num
15
读取用户输入
read
shell变量除了直接赋值,或者脚本传参,还有就是read命令读取,read也是内置命令
选项:
-p 设置提示信息
-t 等待用户输入超时,timeout
read -p "请输入: " vars
shell条件测试
得出真,假的概念
在shell中具有条件测试的语法
test命令
[ ] 中括号
条件测试语法 | 说明 |
---|---|
语法1:test <测试表达式> | 这是利用test命令进行条件测试判断式。test命令和“<测试表达式>”之间至少有一个空格 |
语法2:[ < 测试表达式> ] | 这是通过[](中括号)进行条件判断表达式的方法,和test命令的用法相同,[]的边界和内容之间至少有一个空格 |
语法3:[[ < 测试表达式> ]] | 这是通过[[]] (双中括号)进行条件测试表达式的方法,是比test和[] 更新的语法格式,[[]]的边界和内容之间至少有一个空格 |
语法4:(( < 测试表达式> )) | 这是通过(())(双小括号)进行条件测试表达式的语法,一般用于if语句里,(())(双小括号)两端不需要空格 |
test条件测试
test命令评估一个表达式,它的结果是真,还是假。如果条件为真,那么命令执行状态码结果就为0,否则就不是为0,通过$?取值
#test命令参数
-e 判断文件是否存在(普通文件丶目录 存在就为真,否则为假)
#简单演示
[root@xbs2 ~]# ll
total 8
-rw-r--r-- 1 root root 97 Apr 23 21:50 99.sh
-rw-r--r-- 1 root root 190 Apr 23 20:24 ip.sh
[root@xbs2 ~]# test -e 99.sh
[root@xbs2 ~]# echo $?
0
[root@xbs2 ~]# test -e 99
[root@xbs2 ~]# echo $?
1
#test参数
语法
#按文件类型:
关于某个文件名的r类型a侦测(存在与否),如test -e filename
-e该文件名8是否存在? (常用)
-f该文件名a是否为文件(file)? (常用)
-d该文件名J是否为目录(directory)? (常用)
-b该「文件名J是否为一个block device 装置?
-C该「文件名J是否为一个character device 装置?
-S该”文件名8是否为一个Socket 文件?
-p该文件名1是否为一个FIFO (pipe) 文件?
-L该「文件名J是否为一个连结档?
#按文件权限:
2.关于文件的权限侦测,如test -r filename
-r侦测该文件名是否具有r可读a的属性?
-W侦测该文件名是否具有r可写a的属性?
-x侦测该文件名是否具有r可执行a的属性?
-u侦测该文件名是否具有PSUIDa 的属性?
-g侦测该文件名是否具有SGIDg 的属性?
-k 侦测该文件名是否具有FSticky bita 的属性?
-s侦测该文件名是否为非空白文件a ?
#两个文件之间的比较:
-nt (newer than)判断 file1 是否比file2 新
-ot (older than)判断file1 是否比file2 旧
-ef判断file2与file2是否为同一文件,可用在判断hard link 的判定上。 主要意义在判定,
两个文件是否均指向同一个inode 哩!
4.关于两个整数之间的判定,例如test n1 -eq n2
#针对变量数值的大小比较判断
-eq两数值相等( equal )
-ne两数值不等(not equal)
-gt n1大于n2 ( greater than )
-lt n1小于n2 (less than)
-ge n1大于等于n2 ( greater than or equal )
le n1小于等于n2 (less than or equal)
#判定字符串的数据
test -Z :判断字符串是否为空,空为真,反之为假
test -n :与-z相反,字符串为空时假,不为空时为真
注:
-n亦可省略
test str1 . str2 判定str1 是否等于str2,
著相等,则回传true
test str1 != str2判定str1 是否不等于str2,若相等,则回传false
#多重条件判定,例如: test -r filename -a -x filename
-a (and)两状况同时成立!例如test -r file -a -x file, 则file 同时具有r与x权限时,才回传true.
-a (or)两状况任何一个成立!例如test -r file -0 -x file, 则file 具有r或x权限时,就可回传true.
! 反相状态,如test ! -X file,当file不具有x时,回传true
test条件实践
-e:判断文件是否为真,存在就为真,否则就为假
shell对于真假判断的逻辑,提供了两个符号`"&&":与运算,并且的,并且的两端是递进的关系 "||":或运算
A条件 && B条件 :当A条件成立,并且执行B条件
A条件 || B条件 :当A条件不成立的时候,才会执行B条件
#例:
语法:test 测试参数 文件名/目录名 对结果进行判断执行的逻辑动作
[root@xbs ~]# ll
total 4
-rw-r--r-- 1 root root 190 Apr 25 14:19 ip.sh
[root@xbs ~]# test -e ip.sh && echo 这个文件存在
这个文件存在
[root@xbs ~]# test -e xxx.sh && echo "这个文件存在"
[root@xbs ~]# echo $?
1 #//因为没有xxx,sh这个文件,所以没有输出,状态值也非0
[
[root@xbs ~]# test -e "jiujiu" && echo "这个文件已存在,无法创建" || touch jiujiu //与和或的运用
这个文件已存在,无法创建
[root@xbs ~]# ll
total 4
-rw-r--r-- 1 root root 190 Apr 25 14:19 ip.sh
-rw-r--r-- 1 root root 0 Apr 27 14:18 jiujiu
[root@xbs ~]# test -e "like" && echo "这个文件已存在,无法创建" || touch hello
[root@xbs ~]# ll
total 4
-rw-r--r-- 1 root root 0 Apr 27 14:21 hello
-rw-r--r-- 1 root root 190 Apr 25 14:19 ip.sh
-rw-r--r-- 1 root root 0 Apr 27 14:18 jiujiu