一: shell编程介绍
1、一个shell脚本运行必须要拥有r和x权限; chamod u+x 1.txt
2、运行一个shell脚本的3个步骤:
(1)启动bash解释器
(2)bash把文件内容从硬盘读入内存
(3)bash把读入到内存的内容进行语法解释,控制操作系统执行shell代码
写脚本时候,应该规避好一些交互式的命令
3、运行脚本的有2中方法:
(1)交互式环境:每敲一个命令执行一次,但是不能永久保存
(2)文件内:一次全部执行,但是会永久保存
4、在当前界面运行脚本,不开启新进程
. 1.txt 在当前进程 或者 source 1.txt
bash 1.txt 开启了一个新进程
./ 1.txt 开启了新的一个进程
二:变量
1、变量的定义
变量需要先定义,后引用
per=36 #定义变量的时候,=两边不能有空格,否则报错 @@@
echo $per
echo ${per}G # 引用变量最好用这样的格式 @@@
2、变量名的使用规则
用字母,数字,下划线组成;不可用中文,以及一些既有的命令
3、变量值的类型
int整型 name=10
fault浮点型 name=3.14
str字符串 name="hello world"
4、特殊的符号 @@@@
"":双引号
'':单引号的硬引用; 引号内的符号均无特殊意义
``:取里面命令的结果
$():取里面命令的结果,区别于上条的是,本条可以进行嵌套:
$(命令 $(命令))
:右斜杠
例子:
a=10
echo "$a" #结果是10
echo '$a' #结果是$10
echo "$a" #结果是$a #取消特殊字符的意义
echo $(+date %Y-%m-%y).txt
5、变量的作用域(生效范围)
(1)全局变量:定义的变量只在该进程有效
例子:
per=36
vim 1.txt (echo "$per")
echo "$per" #有结果
bash 1.txt #没有结果
source 1.txt #有结果
export per ; bash 1.txt #有结果 (继承了per的结果给子进程,父进程看不到) @@@@
export的是遗传,子子孙孙进程都可以看待;但是子进程开启的变量,父进程不可以引用;
(2)变量先从局部变量找,没有了再从父辈遗传的找,再从环境变量里找
(3)添加环境变量 #登录时候就自动启动
* 对/root 临时添加环境变量 PATH=$PATH:/root
* 对/root 永久添加环境变量,并给到所有的子进程
vim /etc/profile
PATH=$PATH:/root
export PATH
env 查看所有的环境变量
set 查看所有的变量,临时的也可以看到
(4)删除变量
unset per
三:元字符(一些特殊符号)
元字符是被shell解释器解释,而正则表达式是被命令解释
1、{}、[]和!的应用:
touch {1..5}.txt
touch {a..e}.txt
ls [0-9].txt
ls [!0-9].txt
2、变量间的运算
(1)整型和浮点型的运算
***expr 变量间不做什么的情况下只是整型int的运算
$a + $b 特别注意中间的空格 @@@@@
c=`expr $a + $b`
expr 支持 + - / % 除的话取整数,%为取余 @@@@@
乘法是:expr 5 * 3 @@@@@
***bc
echo " 3.14 < 3.16 " | bc
(2)let x++ 等于在原来变量上加1不支持多加 没有此变量时候默认就为0
a=10; echo `expr $a + 1 `
(3)&
1个&放在命令的结尾表示后台运行
2个&&放在2个命令之间,表示并且的意思;只有左边的命令成功的情况下才可以运行右边的命令
&> 无论结果是否正确都重定向
(4)|| 或者的意思
wssfsf || ls 左边命令错误时,右边命令才运行; 也就是要不左边成立,要不右边成立
ls || pwd 此时只运行ls命令
(5);
不管命令是否正确都会运行
asfg ; pwd
(6)()
*取命令的结果:echo $(expr $a + $b)
*嵌套取命令结果,或者进行运算:$(命令 $(命令))
> >= < <=
==(等于)
!=(不等于)
&&(and)
||(or)
例子:
x=100
(( $x == 100 )) ; echo $? #结果是1
(( $x != 100 || 100 >= 10 )) ; echo $? #结果是0
(7)[] 类似于test命令
*test
test $x -eq 5 ; echo $?
*[]
*进行整型运算:echo $[10 + 2]
*进程过滤时候:ps -aux | grep [p]ing 去掉这个命令执行时候的进程
*判断字符值是否相同;例子如下
name="egon" ; [ $name = "egon" ]
echo $? #结果为0是真,结果为1是假的
**小脚本(用户登录程序:要求只有用户名和密码对输出ok,其他ng):
[root@local-test var]# cat 1.txt
#!/bin/bash
read -p "yourname:" name
read -p "passwd:" passwd
[ $name = "zzyy" ] && [ $passwd = 123 ] && echo ok || echo NG
*判断数字值是否相同;例子如下 @@@@@
*判断数字值相等:[ 10 -eq 10 ] && echo $? 结果是0
*判断数字值大于:[ 10 -gt 8 ] && echo $? 结果是0
*判断数字值大于等于:[ 10 -ge 12] ; echo $? 结果是1
*判断数字值小于:[ 10 -lt 8 ] ; echo $? 结果是0
*判断数字值小于等于:[ 10 -le 12] ; echo $? 结果是1
*判断数字值不等于:[ 10 -ne 12] ; echo $? 结果是0
*判断是否有一般文件:[ -f /root/2.txt ] ; echo $? 判断是否有这个普通文件2.txt
*判断是否有这个文件:[ -e /root/2.txt ] ; echo #? 判断是否有这个文件2.txt
*
(8)? 指定一个字符
ls ?.txt ; ls ??txt
查找所有a到Z的文件 ls /etc/[a-Z].txt
(9) : 结果永远为真
: ; echo $? #结果是0
总结:
; 不管左右的命令是否成功都运行,错误就报错
&& 只有左边的成功时,右边的才会运行
|| 或者左边的运行。或者右边的运行
3、如何进行小数的运算
(1)yum install bc -y
(2)echo $(echo "scale=2; 3/10" | bc | cut -d '.' -f2 )% @@@@@
结果:30%
scale=2取小数点两位,后执行命令3/10,后bc做小数,后以.分割取出来点后的2位数字
echo " 3.14 < 3.16 " | bc
4、awk和bc的小例子
a=free | awk 'NR==2{print $NF}' #拿出实际使用的内存 awk必须是单引号
b=free | awk 'NR==2{print $2}' #总的内存
echo $(echo "scale=2; $a / $b " | bc | cut -d '.' -f2)% #计算出%比
echo `echo "scale=2; $a / $b " |bc | cut -d "." -f2 `%
写脚本的时候注意事项:
对数据的增改删的时候需要
(1)set -o errexit 脚本有错误的时候不运行退出
[root@local-test ~]# cat 1.txt
#!/bin/bash
set -o errexit
echo 111
echo 222
echo 333
afsdfgdg
echo 444
[root@local-test ~]# bash 1.txt
111
222
333
1.txt: line 7: afsdfgdg: command not found
(2)set -o nounset 只要变量不存在就退出 echo $yyy yyy之前不被定义
(3)set -o pippefail addsv| echo 123
egon--元字符总结
1、`` 与$():取命令的结果
[root@localhost ~]# echo `pwd`
/root
[root@localhost ~]# echo $(pwd)
/root
不一样的地方在于$()可以嵌套,而``不能嵌套
[root@localhost ~]# echo $(ls $(pwd))
# 练习
[root@aliyun test]# touch $(date +%F)_bak.tar.gz
[root@aliyun test]# ls
2020-08-24_bak.tar.gz
2、~家目录
3、.与..
4、!调用历史命令、取反
# 1、调用历史命令
[root@egon ~]# !1066
[root@egon ~]# !ls # 匹配最近的一次历史命令
# 2、取反1:对命令的结果取反
[root@egon ~]# ! pwd
/root
[root@egon ~]# echo $?
1
# 3、取反2:效果与^雷同
[root@localhost ~]# touch /test/{1.txt,2.txt,a.txt,aaa_bbb.txt}
[root@localhost ~]# find /test ! -name 1.txt
/test
/test/2.txt
/test/a.txt
/test/aaa_bbb.txt
[root@localhost ~]# ls /test/[!0-9].txt # .txt前只有一个字符,但是非数字
/test/a.txt
[root@localhost ~]# ls /test/[^0-9].txt # .txt前只有一个字符,但是非数字
/test/a.txt
[root@aliyun test]# ls -a /etc/skel/.[!.]*
5、@无特殊意义
6、#注释
7、$取变量值
(1)[root@localhost ~]# x=1
[root@localhost ~]# echo $x
(2)%、-、+运算符,注意%可以与jobs配合“kill %工作号”杀后台进程。-减号还有区间及cd -回到上一级的意思
# 数学运算
# 1、bc是比较常用的linux计算工具了,而且支持浮点运算:
[root@localhost ~]# res=`echo 1+1 | bc`
[root@localhost ~]# res=`echo 1.2+1.3|bc`
[root@localhost ~]# res=`echo "scale=2;5.0/3.0"|bc`
[root@localhost ~]# echo $res
2 取余
[root@localhost ~]# res=`echo 10 % 3 | bc`
[root@localhost ~]# echo $res
[root@localhost ~]# res=`echo 5.0+3.0|bc`
[root@localhost ~]# echo $res
3、expr不支持浮点数计算。而且要注意数字与运算符中的空格
[root@localhost ~]# res=`expr 5 / 3` # 不支持浮点计算
[root@localhost ~]# echo $res
[root@localhost ~]# res=`expr 1+1` # 注意:要有空格
[root@localhost ~]# echo $res
[root@localhost ~]# res=`expr 1 + 1`
[root@localhost ~]# echo $res
[root@localhost ~]# res=`expr 5 * 3` # 在expr中*号要转义才能用,否则报语法错
4、$(()) 同expr,不支持浮点数运算
[root@localhost ~]# echo $((1+1))
[root@localhost ~]# echo $((1.0+2.0))
-bash: 1.0+2.0: 语法错误: 无效的算术运算符 (错误符号是 ".0+2.0")
5、$[]同expr以及$(()),不支持浮点运算
[root@localhost ~]# x=1
[root@localhost ~]# echo $[$x+1]
6、let 不支持浮点数运算,而且不支持直接输出,只能赋值
[root@localhost ~]# let res=1+1
[root@localhost ~]# echo $res
2
[root@localhost ~]#
[root@localhost ~]# let res=50/5
[root@localhost ~]# echo $res
10
[root@localhost ~]# let c=1.3*3
-bash: let: c=1.3*3: 语法错误: 无效的算术运算符 (错误符号是 ".3*3"
[root@aliyun test]# x=1
[root@aliyun test]# let x+=10
[root@aliyun test]# echo $x
11
8、^同!一样
9、&后台运行
[root@localhost home]# echo "hello";sleep 3;echo "world" &
&& 两个命令之间,前面那个运行成功后面才运行
10、*任意多个字符
[root@localhost ~]# touch 1.txt 2.txt aa.txt aaa.txt
[root@localhost ~]# rm -rf *.txt
[root@localhost ~]# touch 1.txt 2.txt aa.txt aaa.txt a1c.txt
[root@localhost ~]# ls *.txt
1.txt 2.txt a1c.txt aaa.txt aa.txt
11、()在子shell中执行
[root@localhost ~]# (x=1)
[root@localhost ~]# echo $x
应用
[root@localhost ~]# (umask 066;touch a.txt) # umask的设置只在子shell中有效
[root@localhost ~]# ll a.txt
-rw-------. 1 root root 0 8月 13 15:22 a.txt
[root@localhost ~]# touch b.txt
[root@localhost ~]# ll b.txt
-rw-r--r--. 1 root root 0 8月 13 15:23 b.txt
10、_下划线:无特殊意义,可以用于名字的声明
[root@localhost ~]# tar -czvf `date +%F_%H:%M:%S`_bak.tar.gz /etc/
11、=赋值,==判断相等性
[root@localhost ~]# [ 1 == 1 ] # 条件1 == 1的左右两边必须有空格
[root@localhost ~]# echo $? # 判断上一条命令的结果是否为真,0=》true
0
12、|管道:把一个进程的处理结果传递给另外一个进程
[root@localhost ~]# ps aux | grep python
|管道命令的作用,是将左侧命令的标准输出转换为标准输入,提供给右侧命令作为参数。但是,大多数命令都不接受标准输入作为参数,只能直接在命令行输入参数,这导致无法用管道命令传递参数。比如echo命令就不接受管道传参。
$ echo "hello world" | echo
xargs命令的作用,是将标准输入转为命令行参数,例如
$ echo "hello world" | xargs echo
hello world
[root@localhost ~]# find /home/ -type d -name "test*" |xargs ls
1.txt 2.txt 3.txt
[root@localhost ~]# ls /home/test
1.txt 2.txt 3.txt
13、转义特殊字符
[root@localhost ~]# mkdir a b.txt # 虽然可以,但不推荐
[root@localhost ~]# ll
总用量 0
drwxr-xr-x. 2 root root 6 8月 13 15:35 a b.txt
[root@localhost ~]# echo $RMB # 默认会当成变量
[root@localhost ~]# echo '$RMB' # 取消特殊意义
$RMB
[root@localhost ~]# echo $RMB # 取消特殊意义
$RMB
14、[]条件测试,后续会详细介绍
[root@localhost ~]# name="egon"
[root@localhost ~]# [ $name == "egon" ];echo $?
0
[root@localhost ~]# name="adf"
[root@localhost ~]# [ $name == "egon" ];echo $?
1
[root@localhost ~]# [ -d /test ];echo $?
0
[root@localhost ~]#
15、引号
'' 强引用(在单引号中都视为普通字符)
" " 弱引用 (在双引号中保留变量)
[root@localhost ~]# x=111
[root@localhost ~]# echo "$x"
111
[root@localhost ~]# echo '$x'
$x
[roo
16、;与&&与||连接多条命令
[root@localhost home]# gagaga;ls # 不论前一条命令运行成功与否,都会执行后续命令
bash: gagaga: 未找到命令...
egon
[root@localhost home]# gagaga && ls # 只有前一条命令执行成功,才会执行后续命令
bash: gagaga: 未找到命令...
[root@localhost home]# ls /test || mkdir /test # 前一条命令执行不成功才会执行后续命令
0.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt
17、:空命令,真值
[root@egon ~]# :
[root@egon ~]# echo $?
0
18、/路径分隔符
19、{}循环列表
[root@localhost home]# touch /test/{0..9}.txt
[root@localhost home]# ls /test/
0.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt
[root@localhost ~]# touch {1..3}{a..d}.txt
[root@localhost ~]# ls
1a.txt 1b.txt 1c.txt 1d.txt 2a.txt 2b.txt 2c.txt 2d.txt 3a.txt 3b.txt 3c.txt 3d.txt
[root@egon ~]# x=100
[root@egon ~]# echo ${x}% # 控制变量名的范围
100%
[root@egon ~]#
[root@egon ~]# echo $xrmb
[root@egon ~]# echo ${x}rmb
100rmb
20、重定向
> >> 输出重定向
< << 输入重定向
> 覆盖 >> 追加
[root@localhost home]# cat >> a.txt << EOF
> 111
> 222
> 333
> EOF
0标准输入、1标准正确输出、2标准错误输出,&标准正确和错误输出
[root@localhost home]# pwd 1>a.txt
[root@localhost home]# cat a.txt
/home
[root@localhost home]# gagag 2>a.txt
[root@localhost home]# cat a.txt
bash: gagag: 未找到命令...
[root@localhost home]# gagaga &>/dev/null
< << 输入重定向
[root@localhost ~]# mysql -uroot -p123 < bbs.sql
[root@localhost home]# grep root < /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost home]# dd if=/dev/zero of=/a.txt bs=1M count=10
记录了10+0 的读入
记录了10+0 的写出
10485760字节(10 MB)已复制,0.024387 秒,430 MB/秒
[root@localhost home]# dd </dev/zero >/b.txt bs=1M count=10
记录了10+0 的读入
记录了10+0 的写出
10485760字节(10 MB)已复制,0.0202365 秒,518 MB/秒
[root@localhost home]# ll /a.txt
-rw-r--r--. 1 root root 10485760 8月 13 16:02 /a.txt
[root@localhost home]# ll /b.txt
-rw-r--r--. 1 root root 10485760 8月 13 16:03 /b.txt
21、?任意一个字符
[root@localhost ~]# ls ??.txt
aa.txt
[root@localhost ~]# ls a?c.txt
a1c.txt
[root@localhost ~]# rm -rf *.txt
22、范围中的任意一个字符 [12] [ac] [a-z] [0-9]
[root@localhost ~]# touch a1c a2c axc aXc axd
[root@localhost ~]# ls a?c
a1c a2c axc aXc
[root@localhost ~]# ls a[1x]c
a1c axc
[root@localhost ~]# ls a[a-z]c
axc aXc
[root@localhost ~]# ls a[A-Z]c # 不区分大小写
axc aXc
[root@localhost ~]# ls a[x]c
axc
[root@localhost ~]# ls a[X]c
aXc
[root@localhost ~]# ls a[0-9]c
a1c a2c
[root@localhost ~]# ls /dev/sd[a-z]*
/dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb1
三:流程控制之if判断
if的语法:
什么是if判断,为什么要用?
根据条件的真假来决定去做什么事情,要用是因为要让计算机代替人去做判断
1. 语法
***完整语法
if 条件 ; then # 如果条件是命令,一般把它丢到空里/dev/null
代码1 #如果条件1成立下面的elif不允许
代码2
elif 条件 ; then #如果条件1不成立,本条件运行
代码1
代码2
elif 条件 ; then
代码1
代码2
else
代码1
代码2
fi #if的反过来
***单分支语法
if 条件 ; then
代码1
代码2
fi
***单分支语法
if 条件 ; then
代码1
代码2
else
代码1
代码2
fi
判断成绩的小例子:
#!/bin/bash
read -p "your score:" score
if [ $score -gt 90 ] ;then
echo "excellent"
elif [ $score -ge 60 ] ; then
echo "good"
else
echo "bad"
fi
四:循环
while循环
* true和:表示为真;开始和结束以do和done为准;结束循环可以是自然结束和break
命令格式:
while 条件
do
命令1
命令2
done
(1)脚本中的运行
登录的小例子:
#!/bin/bash
while :
do
read -p "yourname:" name
read -p "passwd:" passwd
if [ $name = "zz" ] && [ $passwd = 123 ] ; then
echo "login ok"
break
else
echo "fluse"
fi
done
打印10以内的数字:
#!/bin/bash
count=0 #中间不能有空格
while [ $count -le 10 ]
do
echo $count
(( count ++ ))
done
(2)在命令行中写入一行:
while 条件 ; do 命令1 ;命令2 ; done 实时显示一些状态
while true ; do ifconfig; sleep 0.5;clear; done clear就是清屏
for 循环应用
for循环在固定循环次数的情况下优势强
#!/bin/bash
for i in `seq 0 3`
do
if [ $i -gt 2 ]; then
break
fi
echo $i
备注:seq 0 5 和{0..5} 是一个效果;都是0 1 2 3 4 5
ping一个段内的所有网址,看是否ping通
#!/bin/bash
for i in {2..100}
do
(ping -c1 192.168.15.$i &>/dev/null
if [ $? -eq 0 ]; then
echo "192.168.15.$i up"
else
echo "192.168.15.$i down"
fi) &
done
备注:在命令后加上&,是使其在后台运行,能很大程度的提升运行速度
批量修改文件名
for name in ls *.txt
;do mv $name ${name%.txt}.log;done
rename log txt *