shell 脚本(一)

目录

1 shell 脚本语言的基本用法

1 shell 脚本的用途

2 shell 脚本基本结构

3 shell脚本创建过程

4 shell 脚本注释规范

5 第一个 shell 脚本

6 shell 脚本调试

2变量

1 变量

2 变量类型

3 编程语言分类

4 Shell中变量命名法则

5 变量定义和引用

6 环境变量

7 只读变量

8 位置变量

9 退出状态码变量

10 展开命令行

11 脚本安全和 set

3格式化输出 printf

4算术运算

5逻辑运算

5.1 与或非

 5.2 短路运算

6条件测试命令

6.1变量测试

6.2 数值测试

6.3 字符串测试

6.4 文件测试

6.5关于 () 和 {}

6.6组合测试条件

6.7使用read命令来接受输入

7.1按生效范围划分两类

7.2shell登录两种方式分类

7.3按功能划分分类

7.4 编辑配置文件生效

7.5 Bash 退出任务

8流程控制

8.1 条件判断分绍

8. 2 选择执行 if 语句

8.3 条件判断 case 语句

9循环

9.1 循环执行介绍

9.2 循环 for 

 9.3循环 while

9.4 while 特殊用法 while read

9.5循环 until

9.6 循环控制语句 continue

9.7 循环控制语句 break

9.8 循环控制 shift 命令

9.9 循环与菜单 select


1 shell 脚本语言的基本用法

1 shell 脚本的用途

将简单的命令组合完成复杂的工作,自动化执行命令,提高工作效率
减少手工命令的输入,一定程度上避免人为错误
将软件或应用的安装及配置实现标准化
用于实现日常性的,重复性的,非交互式的运维工作,如:文件打包压缩备份,监控系统运行状态并实现
告警等

2 shell 脚本基本结构

shell脚本编程:是基于过程式、解释执行的语言
编程语言的基本结构:
各种系统命令的组合
数据存储:变量、数组
表达式:a + b
控制语句:if
shell脚本:包含一些命令或声明,并符合一定格式的文本文件
格式要求:首行shebang机制

#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
#!/usr/bin/ruby
#!/usr/bin/lua

3 shell脚本创建过程

第一步:使用文本编辑器来创建文本文件
第一行必须包括shell声明序列:#!

#!/bin/bash

添加注释,注释以#开头
第二步:加执行权限
给予执行权限,在命令行上指定脚本的绝对或相对路径
第三步:运行脚本
直接运行解释器,将脚本作为解释器程序的参数运行

4 shell 脚本注释规范

1、第一行一般为调用使用的语言
2、程序名,避免更改文件名为无法找到正确的文件
3、版本号
4、更改后的时间
5、作者相关信息
6、该程序的作用,及注意事项
7、最后是各版本的更新简要说明

5 第一个 shell 脚本

[root@centos8 ~]#vim /data/hello.sh
#!/bin/bash

#经典写法
echo "hello, world"

#流行写法
echo 'Hello, world!'

#执行方法1
[root@centos8 ~]#bash /data/hello.sh

#执行方法2
[root@centos8 ~]#cat /data/hello.sh | bash

#执行方法3
[root@centos8 ~]#bash < /data/hello.sh

#执行方法4
[root@centos8 ~]#chmod +x /data/hello.sh

#绝对路径
[root@centos8 ~]#/data/hello.sh

#相法路径
[root@centos8 ~]#cd /data/
[root@centos8 ~]#./hello.sh

#执行方法5,本方法可以实现执行远程主机的shell脚本
[root@centos8 ~]#yum -y install httpd
[root@centos8 ~]#systemctl start httpd
[root@centos8 ~]#mv /data/hello.sh /var/www/html/

[root@centos8 ~]#curl http://10.0.0.8/hello.sh
#经典写法
echo "hello, world"
#流行写法
echo 'Hello, world!'

[root@centos8 ~]#curl http://10.0.0.8/hello.sh |bash
% Total  % Received % Xferd Average Speed  Time  Time   Time Current
                Dload Upload  Total  Spent  Left Speed
100  388  100  388   0   0  378k    0 --:--:-- --:--:-- --:--:-- 378k
hello, world
Hello, world!
[root@centos8 ~]#curl -s http://10.0.0.8/hello.sh |bash
hello, world
Hello, world!
[root@centos8 ~]#curl http://10.0.0.8/hello.sh 2>/dev/null |bash
hello, world
Hello, world!




#例:在远程主机运行本地shell脚本
[root@centos8 ~]#hostname -I
10.0.0.8
[root@centos8 ~]#cat test.sh
#!/bin/bash
hostname -I
[root@centos8 ~]#ssh 10.0.0.18 /bin/bash < test.sh
root@10.0.0.18's password:
10.0.0.18

例:备份脚本

#!/bin/bash
echo -e "\033[1;32mStarting backup...\033[0m"
sleep 2
cp -av /etc/ /data/etc`date +%F`/
echo -e "\033[1;32mBackup is finished\033[0m"

6 shell 脚本调试

#只检测脚本中的语法错误,但无法检查出命令错误,但不真正执行脚本
bash -n /path/to/some_script

#调试并执行
bash -x /path/to/some_script

#例:
[root@centos8 scripts]#cat -A test.sh
#!/bin/bash$
echo line1$
hostnam$
cat > test.txt <<EOF $
aaa$
bbb$
ccc$
EOF $
$
echo line22$
[root@centos8 scripts]#bash -n test.sh
f1.sh: line 20: warning: here-document at line 14 delimited by end-of-file
(wanted `EOF')

总结:脚本错误常见的有三种
语法错误,会导致后续的命令不继续执行,可以用bash -n 检查错误,提示的出错行数不一定是准
确的
命令错误,默认后续的命令还会继续执行,用bash -n 无法检查出来 ,可以使用 bash -x 进行观察
逻辑错误:只能使用 bash -x 进行观察

2变量

1 变量

变量表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据

2 变量类型

变量类型:
        内置变量,如:PS1,PATH,UID,HOSTNAME,$$,BASHPID,PPID,$?,HISTSIZE
        用户自定义变量
不同的变量存放的数据不同,决定了以下
        数据存储方式
        参与的运算
        表示的数据范围
变量数据类型:
        字符
        数值:整型、浮点型,bash 不支持浮点数

3 编程语言分类

*静态和动态语言

静态编译语言:使用变量前,先声明变量类型,之后类型不能改变,在编译时检查,如:java,c
动态编译语言:不用事先声明,可随时改变类型,如:bash,Python

*强类型和弱类型语言

强类型语言:不同类型数据操作,必须经过强制转换才同一类型才能运算,如java , c# ,
python

弱类型语言:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会
自动进行隐式类型转换;变量无须事先定义可直接调用
如:bash ,php,javascript

4 Shell中变量命名法则

1 命名要求

区分大小写
不能使程序中的保留字和内置变量:如:if, for
只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线 “ - ”,和主机名相反

2 命名习惯

见名知义,用英文单词命名,并体现出实际作用,不要用简写,如:ATM
变量名大写
局部变量小写
函数名小写
大驼峰StudentFirstName,由多个单词组成,且每个单词的首字母是大写,其它小写
小驼峰studentFirstName ,由多个单词组成,第一个单词的首字母小写,后续每个单词的首字母是
大写,其它小写
下划线: student_name

5 变量定义和引用

变量的生效范围等标准划分变量类型
        普通变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效
        环境变量:生效范围为当前shell进程及其子进程
        本地变量:生效范围为当前shell进程中某代码片断,通常指函数

#变量赋值:
name='value'

#value 可以是以下多种形式
直接字串:name='root'
变量引用:name="$USER"
命令引用:name=`COMMAND` 或者 name=$(COMMAND)

#注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量会随着脚
本结束,也会自动删除


#变量引用:
$name
${name} 


#弱引用和强引用
"$name" 弱引用,其中的变量引用会被替换为变量值
'$name' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串


#例:变量的各种赋值方式和引用
[root@centos8 ~]#TITLE='cto'
[root@centos8 ~]#echo $TITLE
cto
[root@centos8 ~]#echo I am $TITLE
I am cto
[root@centos8 ~]#echo "I am $TITLE"
I am cto
[root@centos8 ~]#echo 'I am $TITLE'
I am $TITLE
[root@centos8 ~]#NAME=$USER
[root@centos8 ~]#echo $NAME
root
[root@centos8 ~]#USER=`whoami`
[root@centos8 ~]#echo $USER
root
[root@centos8 ~]#FILE=`ls /run`
[root@centos8 ~]#echo $FILE
agetty.reload atd.pid auditd.pid autofs.fifo-misc autofs.fifo-net console
cron.reboot cryptsetup dbus faillock fsck initctl initramfs lock log mount
NetworkManager plymouth rsyslogd.pid screen sepermit setrans sshd.pid sssd.pid
sudo systemd tmpfiles.d tuned udev user utmp vmware
[root@centos8 ~]#FILE=/etc/*
[root@centos8 ~]#echo $FILE
/etc/adjtime /etc/aliases /etc/alternatives /etc/anacrontab /etc/at.deny
/etc/audit /etc/authselect /etc/autofs.conf /etc/autofs_ldap_auth.conf
[root@centos8 ~]#seq 10
1
2
3
4
5
6
7
8
9
10
[root@centos8 ~]#NUM=`seq 10`
[root@centos8 ~]#echo $NUM
1 2 3 4 5 6 7 8 9 10
[root@centos8 ~]#echo "$NUM"
1
2
3
4
5
6
7
8
9
10
[root@centos8 ~]#NAMES="wen
> zhang
> zhao
> li"
[root@centos8 ~]#echo $NAMES
wen zhang zhao li
[root@centos8 ~]#echo "$NAMES"
wen
zhang
zhao
li


#例:变量引用
[root@centos8 data]#NAME=xin
[root@centos8 data]#AGE=20
[root@centos8 data]#echo $NAME
xinxin
[root@centos8 data]#echo $AGE
20
[root@centos8 data]#echo $NAME $AGE
xinxin 20
[root@centos8 data]#echo $NAME$AGE
xinxin 20
[root@centos8 data]#echo $NAME_$AGE
20
[root@centos8 data]#echo ${NAME}_$AGE
xinxin_20


#例:变量的间接赋值和引用
[root@centos8 ~]#TITLE=cto
[root@centos8 ~]#NAME=wen
[root@centos8 ~]#TITLE=$NAME
[root@centos8 ~]#echo $NAME
wen
[root@centos8 ~]#echo $TITLE
wen
[root@centos8 ~]#NAME=xinxin
[root@centos8 ~]#echo $NAME
xinxin
[root@centos8 ~]#echo $TITLE
wen


#例:变量追加值
[root@centos8 ~]#TITLE=CTO
[root@centos8 ~]#TITLE+=:xinxin
[root@centos8 ~]#echo $TITLE
CTO:xinxin

#例:利用变量实现动态命令
[root@centos8 ~]#CMD=hostname
[root@centos8 ~]#$CMD
centos8.xinxin.com


#显示已定义的所有变量:
set


#删除变量
#例:
[root@centos8 ~]#NAME=xin
[root@centos8 ~]#TITLE=ceo
[root@centos8 ~]#echo $NAME $TITLE
xin ceo
[root@centos8 ~]#unset NAME TITLE
[root@centos8 ~]#echo $NAME $TITLE


#例:显示系统信息
[root@centos8 scripts]#cat systeminfo.sh
#!/bin/bash
RED="\E[1;31m"
GREEN="echo -e \E[1;32m"
END="\E[0m"
. /etc/os-release
$GREEN----------------------Host systeminfo--------------------$END
echo -e  "HOSTNAME:   $RED`hostname`$END"
#echo -e "IPADDR:    $RED` ifconfig eth0|grep -Eo '([0-9]{1,3}\.){3}[0-9]
{1,3}' |head -n1`$END"
echo -e  "IPADDR:    $RED` hostname -I`$END"
echo -e  "OSVERSION:   $RED$PRETTY_NAME$END"
echo -e  "KERNEL:    $RED`uname -r`$END"
echo -e  "CPU:     $RED`lscpu|grep '^Model name'|tr -s ' '|cut -d : -
f2`$END"
echo -e  "MEMORY:    $RED`free -h|grep Mem|tr -s ' ' : |cut -d : -f2`$END"
echo -e  "DISK:     $RED`lsblk |grep '^sd' |tr -s ' ' |cut -d " " -f4`$END"
$GREEN---------------------------------------------------------$END


[root@centos8 scripts]#cat system_info.sh
#!/bin/bash
RED="\E[1;31m"
GREEN="echo -e \E[1;32m"
END="\E[0m"
$GREEN----------------------Host systeminfo--------------------$END
echo -e  "HOSTNAME:   $RED`hostname`$END"
echo -e  "IPADDR:    $RED` ifconfig eth0|grep -Eo '([0-9]{1,3}\.){3}[0-9]
{1,3}' |head -n1`$END"
echo -e  "OSVERSION:   $RED`cat /etc/redhat-release`$END"
echo -e  "KERNEL:    $RED`uname -r`$END"
echo -e  "CPU:     $RED`lscpu|grep 'Model name'|tr -s ' '|cut -d : -f2`$END"
echo -e  "MEMORY:    $RED`free -h|grep Mem|tr -s ' ' : |cut -d : -f2`$END"
echo -e  "DISK:     $RED`lsblk |grep '^sd' |tr -s ' ' |cut -d " " -f4`$END"
$GREEN---------------------------------------------------------$END


#例:
[root@centos8 script]#cat backup.sh
#!/bin/bash
#Author: wen
#Date: 2020-08-05
#Description: test
#FILE: backup.sh
#VERSION: 1.0
COLOR='echo -e \E[1;35m'
END='\E[0m'
BACKUP=/backup
SRC=/etc
DATE=`date +%F`
${COLOR}Starting backup...$END
sleep 2
cp -av $SRC  ${BACKUP}${SRC}_$DATE
${COLOR}Backup is finished$END

6 环境变量

可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量
一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程
一般只在系统配置文件中使用,在脚本中较少使用

##声明并赋值
export name=VALUE
declare -x name=VALUE

#或者分两步实现
name=VALUE
export name

#变量引用:
$name
${name}


#显示所有环境变量:
env
printenv
export
declare -x


#查看指定进程的环境变量
cat /proc/$PID/environ


#删除变量:
unset name


#bash内建的环境变量
PATH
SHELL
USER
UID
HOME
PWD
SHLVL #shell的嵌套层数,即深度
LANG
MAIL
HOSTNAME
HISTSIZE
_   #下划线,表示前一命令的最后一个参数



例: 查看进程的环境变量
[root@centos8 ~]#cat /proc/1235/environ
USER=rootLOGNAME=rootHOME=/rootPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/us
r/binSHELL=/bin/bashTERM=linuxSSH_AUTH_SOCK=/tmp/ssh-
iIeuAxdLiY/agent.1234XDG_SESSION_ID=1XDG_RUNTIME_DIR=/run/user/0DBUS_SESSION_BUS
_ADDRESS=unix:path=/run/user/0/busSSH_CLIENT=10.0.0.1 13258
22SSH_CONNECTION=10.0.0.1 13258 10.0.0.8 22SSH_TTY=/dev/pts/0[root@centos8 ~]#
[root@centos8 ~]#
[root@centos8 ~]#cat /proc/1235/environ |tr '\0' '\n'
USER=root
LOGNAME=root
HOME=/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
SHELL=/bin/bash
TERM=linux
SSH_AUTH_SOCK=/tmp/ssh-iIeuAxdLiY/agent.1234
XDG_SESSION_ID=1
XDG_RUNTIME_DIR=/run/user/0
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/0/bus
SSH_CLIENT=10.0.0.1 13258 22
SSH_CONNECTION=10.0.0.1 13258 10.0.0.8 22
SSH_TTY=/dev/pts/0

7 只读变量

只读变量:只能声明定义,但后续不能修改和删除,即常量

#只读变量:只能声明定义,但后续不能修改和删除,即常量
#声明只读变量:
readonly name
declare -r name


#查看只读变量:
readonly [-p]
declare -r


#例:
[root@centos8 ~]#readonly PI=3.14159
[root@centos8 ~]#echo $PI
3.14159
[root@centos8 ~]#PI=3.14
-bash: PI: readonly variable
[root@centos8 ~]#unset PI
-bash: unset: PI: cannot unset: readonly variable
[root@centos8 ~]#echo $PI
3.14159
[root@centos8 ~]#exit
logout
Connection closed by foreign host.
Disconnected from remote host(10.0.0.8) at 14:27:04.
Type `help' to learn how to use Xshell prompt.
[c:\~]$
Reconnecting in 1 seconds. Press any key to exit local shell.
.
Connecting to 10.0.0.8:22...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
WARNING! The remote SSH server rejected X11 forwarding request.
Last login: Wed Apr  1 13:51:28 2020 from 10.0.0.1
[root@centos8 ~]#echo $PI
[root@centos8 ~]#

8 位置变量

位置变量:在bash shell中内置的变量, 在脚本代码中调用通过命令行传递给脚本的参数

$1, $2, ... 对应第1个、第2个等参数,shift [n]换位置
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合为一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
注意:$@ $* 只在被双引号包起来的时候才会有差异



#清空所有位置变量
set --


#例:
[root@centos8 ~]#cat /data/scripts/arg.sh
#!/bin/bash
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "10st arg is ${10}"
echo "11st arg is ${11}"
echo "The number of arg is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@centos8 ~]#bash /data/scripts/arg.sh {a..z}
1st arg is a
2st arg is b
3st arg is c
10st arg is j
11st arg is k
The number of arg is 26
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
The scriptname is arg.sh



#例:$*和$@的区别
[root@centos8 scripts]#cat f1.sh
#!/bin/bash
echo "f1.sh:all args are $@"
echo "f1.sh:all args are $*"
./file.sh "$*"
[root@centos8 scripts]#cat f2.sh
#!/bin/bash
echo "f2.sh:all args are $@"
echo "f2.sh:all args are $*"
./file.sh "$@"
[root@centos8 scripts]#cat file.sh
#!/bin/bash
echo "file.sh:1st arg is $1"
[root@centos8 scripts]#./f1.sh a b c
f1.sh:all args are a b c
f1.sh:all args are a b c
file.sh:1st arg is a b c
[root@centos8 scripts]#./f2.sh a b c
f2.sh:all args are a b c
f2.sh:all args are a b c
file.sh:1st arg is a


#例: 利用软链接实现同一个脚本不同功能
[root@centos8 ~]#cat test.sh
#!/bin/bash
#********************************************************************
echo $0
[root@centos8 ~]#ln -s test.sh a.sh
[root@centos8 ~]#ln -s test.sh b.sh
[root@centos8 ~]#./a.sh
./a.sh
[root@centos8 ~]#./b.sh
./b.sh

9 退出状态码变量

当我们浏览网页时,有时会看到下图所显示的数字,表示网页的错误信息,我们称为状态码,在shell脚本中也有相似的技术表示程序执行的相应状态。

#进程执行后,将使用变量 $? 保存状态码的相关数字,不同的值反应成功或失败,$?取值范例 0-255
$?的值为0 #代表成功
$?的值是1到255   #代表失败


#例:
ping -c1 -W1 hostdown &> /dev/null
echo $?


#用户可以在脚本中使用以下命令自定义退出状态码
exit [n]

 注意:
脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果
如果没有exit命令, 即未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后
一条命令的状态码

10 展开命令行

#展开命令执行顺序
把命令行分成单个命令词
展开别名
展开大括号的声明{}
展开波浪符声明 ~
命令替换$() 和 ``
再次把命令行分成命令词
展开文件通配符*、?、[abc]等等
准备I/0重导向 <、>
运行命令


#防止扩展
反斜线(\)会使随后的字符按原意解释


#例:
[root@centos8 ~]#echo Your cost: \$5.00
Your cost: $5.00
[root@rocky8 ~]#echo "The book's price is \$10"
The book's price is $10


#加引号来防止扩展
单引号(’’)防止所有扩展
双引号(”“)也可防止扩展,但是以下情况例外:$(美元符号)


#变量扩展
`` : 反引号,命令替换
\:反斜线,禁止单个字符扩展
!:叹号,历史命令替换


#例: ``和$() 区别
[root@centos8 ~]#echo `echo \`
> ^C
[root@centos8 ~]#echo `echo \\`
[root@centos8 ~]#echo `echo \\\`
> ^C
[root@centos8 ~]#echo `echo \\\\`
\
[root@centos8 ~]#echo $(echo \)
> ^C
[root@centos8 ~]#echo $(echo \\)
\
[root@centos8 ~]#echo $(echo \\\)
> ^C
[root@centos8 ~]#echo $(echo \\\\)
\\

11 脚本安全和 set

#set 命令:可以用来定制 shell 环境
$- 变量
h:hashall,打开选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选
项关闭
i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的 shell。所谓的交互式shell,
在脚本中,i选项是关闭的
m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等
B:braceexpand,大括号扩展
H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的
一个历史命令,“!n”返回第 n 个历史命令



#例:
[root@centos8 ~]#echo $-
himBHs
[root@centos8 ~]#set +h
[root@centos8 ~]#echo $-
imBHs
[root@centos8 ~]#hash
-bash: hash: hashing disabled
[root@centos8 ~]#echo {1..10}
1 2 3 4 5 6 7 8 9 10
[root@centos8 ~]#echo $-
imBHs
[root@centos8 ~]#set +B
[root@centos8 ~]#echo $-
imHs
[root@centos8 ~]#echo {1..10}
{1..10}





#set 命令实现脚本安全
-u 在扩展一个没有设置的变量时,显示错误信息, 等同set -o nounset
-e 如果一个命令返回一个非0退出状态值(失败)就退出, 等同set -o errexit
-o option 显示,打开或者关闭选项
显示选项:set -o
打开选项:set -o 选项
关闭选项:set +o 选项
-x 当执行命令时,打印命令及其参数,类似 bash -x


#例:
[root@centos8 ~]#set -o
allexport   off
braceexpand  on
emacs     on
errexit    off
errtrace    off
functrace   off
hashall    on
histexpand   on
history    on
ignoreeof   off
interactive-comments on
keyword    off
monitor    on
noclobber   off
noexec     off
noglob     off
nolog     off
notify     off
nounset    off
onecmd     off
physical    off
pipefail    off
posix     off
privileged   off
verbose    off
vi       off
xtrace     off


#例:
DIR=/data
cd $DIR
rm -rf *
#rm -rf $DIr/*

3格式化输出 printf

相当于增强版的 echo, 实现丰富的格式化输出

#格式:printf "指定的格式" "文本1" ”文本2“……

常用格式替换符

替换符功能
%s 字符串
%d,%i 十进制整数
%f 浮点格式
%c ASCII字符,即显示对应参数的第一个字符
%b相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义
%o八进制值
%u 不带正负号的十进制值
%x 十六进制值(a-f)
%X 十六进制值(A-F)
%% 表示%本身

注释:

%#s 中的数字代表此替换符中的输出字符宽度,不足补空格,默认是右对齐,%-10s表示10个字符宽,- 表示
左对齐
%03d 表示3位宽度,不足前面用0补全,超出位数原样输出
%.2f 中的2表示小数点后显示的小数位数

转义符功能
\a警告字符,通常为ASCII的BEL字符
\b         后退
\f                换页
\n 换行
\r回车
\t水平制表符
\v 垂直制表符
\表示\本身
#例:
[root@centos8 ~]#printf "%s" 1 2 3 4
1234[root@centos8 ~]#
[root@centos8 ~]#printf "%s\n" 1 2 3 4
1
2
3
4
[root@centos8 ~]#printf "%f\n" 1 2 3 4
1.000000
2.000000
3.000000
4.000000
#.2f 表示保留两位小数
[root@centos8 ~]#printf "%.2f\n" 1 2 3 4
1.00
2.00
3.00
4.00
[root@centos8 ~]#printf "(%s)" 1 2 3 4;echo
(1)(2)(3)(4)
[root@centos8 ~]#printf " (%s) " 1 2 3 4;echo ""
(1) (2) (3) (4)
[root@centos8 ~]#printf "(%s)\n" 1 2 3 4
(1)
(2)
(3)
(4)
[root@centos8 ~]#printf "%s %s\n" 1 2 3 4
1 2
3 4
[root@centos8 ~]#printf "%s %s %s\n" 1 2 3 4
1 2 3
4 
#%-10s 表示宽度10个字符,左对齐
[root@centos8 ~]#printf "%-10s %-10s %-4s %s \n" 姓名 性别 年龄 体重 小明 男 20 70
小红 女 18 50
姓名   性别   年龄  体重
小明   男     20  70
小红   女     18  50
#将十进制的17转换成16进制数
[root@centos8 ~]#printf "%X" 17
11[root@centos8 ~]#
#将十六进制C转换成十进制
[root@centos8 ~]#printf "%d\n" 0xC
12
[root@rocky8 ~]#VAR="welcome to liang";printf "\033[1;32m%s\033[0m " $VAR
welcome to liang
[root@centos8 ~]#VAR="welcome to liang";printf "\033[1;32m%s\033[0m\n" $VAR
welcome
to
liang
[root@centos8 ~]#VAR="welcome to liang";printf "\033[1;32m%s\033[0m\n" "$VAR"
welcome to liang

4算术运算

Shell允许在某些情况下对算术表达式进行求值,比如:let和declare 内置命令,(( ))复合命令和算术扩展。求值以固定宽度的整数进行,不检查溢出,尽管除以0 被困并标记为错误。运算符及其优先级,关联性和值与C语言相同。以下运算符列表分组为等优先级运算符级别。级别按降序排列优先。

注意:bash 只支持整数,不支持小数

+ -  addition, subtraction
* / % multiplication, division, remainder, %表示取模,即取余数,示例:9%4=1,5%3=2
i++ i-- variable post-increment and post-decrement
++i --i variable pre-increment and pre-decrement
= *= /= %= += -= <<= >>= &= ^= |=     assignment
- +  unary minus and plus
! ~  logical and bitwise negation
**   exponentiation 乘方,即指数运算
<< >> left and right bitwise shifts
<= >= < >    comparison
== != equality and inequality
&   bitwise AND
|   bitwise OR
^   bitwise exclusive OR
&&   logical AND
||   logical OR
expr?expr:expr    conditional operator
expr1 , expr2   comma


#乘法符号有些场景中需要转义
#实现算术运算:
(1) let var=算术表达式
(2) ((var=算术表达式)) 和上面等价
(3) var=$[算术表达式]
(4) var=$((算术表达式))
(5) var=$(expr arg1 arg2 arg3 ...)
(6) declare -i var = 数值
(7) echo '算术表达式' | bc


#内建的随机数生成器变量:
$RANDOM   取值范围:0-32767


#例:#生成 0 - 49 之间随机数
echo $[$RANDOM%50]
#随机字体颜色
[root@centos8 ~]#echo -e "\033[1;$[RANDOM%7+31]mhello\033[0m"
liang


#增强型赋值:
+= i+=10 相当于 i=i+10
-= i-=j  相当于 i=i-j
*=
/=
%=
++ i++,++i   相当于 i=i+1
-- i--,--i   相当于 i=i-1


#格式:
let varOPERvalue


#例:
[root@centos8 ~]#let i=10*2
[root@centos8 ~]#echo $i
20
[root@centos8 ~]#((j=i+10))
[root@centos8 ~]#echo $j
30


#例:
#自加3后自赋值
let count+=3
[root@centos8 ~]#i=10
[root@centos8 ~]#let i+=20 #相当于let i=i+20
[root@centos8 ~]#echo $i
30
[root@centos8 ~]#j=20
[root@centos8 ~]#let i*=j
[root@centos8 ~]#echo $i
600


#例
#自增,自减
let var+=1
let var++
let var-=1
let var--
[root@centos8 ~]#unset i j ; i=1; let j=i++; echo "i=$i,j=$j"
i=2,j=1
[root@centos8 ~]#unset i j ; i=1; let j=++i; echo "i=$i,j=$j"
i=2,j=2


#例:
[root@centos8 ~]#expr 2 * 3
expr: syntax error: unexpected argument ‘anaconda-ks.cfg’
[root@centos8 ~]#ls
anaconda-ks.cfg
[root@centos8 ~]#expr 2 \* 3
6



#例:
[root@centos8 ~]#echo "scale=3;20/3"|bc
6.666


#例:
[root@centos8 ~]#i=10
[root@centos8 ~]#j=20
[root@centos8 ~]#declare -i result=i*j
[root@centos8 ~]#echo $result
200


#例:例:鸡兔同笼,是中国古代著名典型趣题之一,记载于《孙子算经》之中。今有雉兔同笼,上有三十
五头,下有九十四足,问雉兔各几何?
[root@centos8 scripts]#cat chook_rabbit.sh
#!/bin/bash
HEAD=$1
FOOT=$2
RABBIT=$(((FOOT-HEAD-HEAD)/2))
CHOOK=$[HEAD-RABBIT]
echo RABBIT:$RABBIT
echo CHOOK:$CHOOK
[root@centos8 scripts]#./chook_rabbit.sh 30 80
RABBIT:10
CHOOK:20
[root@centos8 scripts]#

5逻辑运算

5.1 与或非

#true, false
1,真
0,假
#注意,以上为二进制


#与:& 和0相与结果为0,和1相与结果保留原值, 一假则假,全真才真
0 与 0 = 0
0 与 1 = 0
1 与 0 = 0
1 与 1 = 1


#例:
[root@ubuntu1804 ~]#x=$[2&6]
[root@ubuntu1804 ~]#echo $x
2
[root@ubuntu1804 ~]#x=$[7&3]
[root@ubuntu1804 ~]#echo $x
3



#或:| 和1相或结果为1,和0相或结果保留原值,一真则真,全假才假
0 或 0 = 0
0 或 1 = 1
1 或 0 = 1
1 或 1 = 1


#例:
[root@ubuntu1804 ~]#x=$[7|3]
[root@ubuntu1804 ~]#echo $x
7
[root@ubuntu1804 ~]#x=$[2|5]
[root@ubuntu1804 ~]#echo $x
7


#非:!
! 1 = 0  !  true
! 0 = 1 !  false


#异或:^
异或的两个值,相同为假,不同为真。两个数字X,Y异或得到结果Z,Z再和任意两者之一X异或,将得出
另一个值Y
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0


#例:
[root@centos8 ~]#true
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#false
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#! true
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#! false
[root@centos8 ~]#echo $?
0


#例: 变量互换
[root@centos8 ~]#x=10;y=20;temp=$x;x=$y;y=$temp;echo x=$x,y=$y
x=20,y=10
[root@centos8 ~]#x=10;y=20;x=$[x^y];y=$[x^y];x=$[x^y];echo x=$x,y=$y
x=20,y=10

 5.2 短路运算

#*短路与 &&
CMD1 && CMD2
第一个CMD1结果为真(1),第二个CMD2必须要参与运算,才能得到最终的结果
第一个CMD1结果为假(0),总的结果必定为0,因此不需要执行CMD2


#短路或 ||
CMD1 || CMD2
第一个CMD1结果为真(1),总的结果必定为1,因此不需要执行CMD2
第一个CMD1结果为假(0),第二个CMD2 必须要参与运算,才能得到最终的结果


#短路与和或组合
CMD1 && CMD2 || CMD3
当CMD1执行成功时,会执行CMD2
当CMD1执行失败时,会执行CMD3
注意: CMD1 || CMD2 && CMD3 逻辑不通,不使用

6条件测试命令

条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成
测试过程,实现评估布尔声明,以便用在条件性环境下进行执行
若真,则状态码变量 $? 返回0
若假,则状态码变量 $? 返回1

条件测试命令
test EXPRESSION
[ EXPRESSION ] #和test 等价,建议使用 [ ]
[[ EXPRESSION ]] 相关于增强版的 [ ], 支持[]的用法,且支持扩展正则表达式和通配符
注意:EXPRESSION前后必须有空白字符

6.1变量测试

##判断 NAME 变量是否定义
[ -v NAME ]

#例:
[root@centos8 ~]#unset x
[root@centos8 ~]#test -v x
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#x=10
[root@centos8 ~]#test -v x
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#y=
[root@centos8 ~]#test -v y
[root@centos8 ~]#echo $?
0
#注意 [ ] 中需要空格,否则会报下面错误
[root@centos8 ~]#[-v name]
-bash: [-v: command not found
[root@centos8 ~]#[ -v name ]
[root@centos8 ~]#echo $?
0

6.2 数值测试

#数值测试
-eq 是否等于
-ne 是否不等于
-gt 是否大于
-ge 是否大于等于
-lt 是否小于
-le 是否小于等于


#例:
[root@centos8 ~]#i=10
[root@centos8 ~]#j=8
[root@centos8 ~]#[ $i -lt $j ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ $i -gt $j ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ i -gt j ]
-bash: [: i: integer expression expected



#算术表达式比较
==  相等
!=  不相等
<=
>=
<
>


#例:
[root@centos8 ~]#i=10
[root@centos8 ~]#j=8
[root@centos8 ~]#[ $i -lt $j ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ $i -gt $j ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ i -gt j ]
-bash: [: i: integer expression expected


#算术表达式比较
==  相等
!=  不相等
<=
>=
<
>


#例:
[root@centos8 ~]#x=10;y=10;(( x == y ));echo $?
0
[root@centos8 ~]#x=10;y=20;(( x == y ));echo $?
1
[root@centos8 ~]#x=10;y=20;(( x != y ));echo $?
0
[root@centos8 ~]#x=10;y=10;(( x != y ));echo $?
1


#例:
[root@centos8 ~]#x=10;y=20;(( x > y ));echo $?
1
[root@centos8 ~]#x=10;y=20;(( x < y ));echo $?
0

6.3 字符串测试

#test和 [ ] 字符串测试用法
-z STRING 字符串是否为空,没定义或空为真,不空为假,
-n STRING 字符串是否不空,不空为真,空为假
 STRING  同上
STRING1 = STRING2 是否等于,注意 = 前后有空格
STRING1 != STRING2 是否不等于
> ascii码是否大于ascii码
< 是否小于



#[[]] 字符串测试用法
[[ expression ]] 用法
== 左侧字符串是否和右侧的PATTERN相同
注意:此表达式用于[[ ]]中,PATTERN为通配符
=~ 左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配
注意: 此表达式用于[[ ]]中为扩展的正则表达式


#建议:当使用正则表达式或通配符使用[[ ]],其它情况一般使用 [ ]


#例:使用 [ ]
[root@centos8 ~]#unset str
[root@centos8 ~]#[ -z "$str" ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#str=""
[root@centos8 ~]#[ -z "$str" ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#str=" "
[root@centos8 ~]#[ -z "$str" ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ -n "$str" ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#unset str
[root@centos8 ~]#[ -n "$str" ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ "$str" ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#str=liang
[root@centos8 ~]#[ "$str" ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#str=liang
[root@centos8 ~]#[ "$str" ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#str1=liang
[root@centos8 ~]#str2=xin
[root@centos8 ~]#[ $str1 = $str2 ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#str2=liang
[root@centos8 ~]#[ $str1 = $str2 ]
[root@centos8 ~]#echo $?
0




#例:在比较字符串时,建议变量放在“ ”中
[root@centos8 ~]#[ "$NAME" ]
[root@centos8 ~]#NAME="I love linux"
[root@centos8 ~]#[ $NAME ]
-bash: [: love: binary operator expected
[root@centos8 ~]#[ "$NAME" ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ I love linux ]
-bash: [: love: binary operator expected


#例: [[ ]] 和通配符
[root@centos8 ~]#FILE="a*"
[root@centos8 ~]#echo $FILE
a*
[root@centos8 ~]#[[ $FILE == a* ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#FILE="ab"
[root@centos8 ~]#[[ $FILE == a* ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#FILE="a*"
#[[]]中如果不想使用通配符*,只想表达*本身,可以用" "引起来
[root@centos8 ~]#[[ $FILE == a"*" ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#FILE="ab"
[root@centos8 ~]#[[ $FILE == a"*" ]]
[root@centos8 ~]#echo $?
1
#[[]]中如果不想使用通配符*,只想表达*本身,也可以使用转义符
[root@centos8 ~]#[[ $FILE == a\* ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#FILE="a\b"
[root@centos8 ~t]#[[ $FILE == a\* ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#FILE="a*"
[root@centos8 ~]#[[ $FILE == a\* ]]
[root@centos8 ~]#echo $?
0


#通配符?
[root@centos8 script]#FILE=abc
[root@centos8 script]#[[ $FILE == ??? ]]
[root@centos8 script]#echo $?
0
[root@centos8 script]#FILE=abcd
[root@centos8 script]#[[ $FILE == ??? ]]
[root@centos8 script]#echo $?
1
#通配符
[root@centos8 ~]#NAME="linux1"
[root@centos8 ~]#[[ "$NAME" == linux* ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[[ "$NAME" == "linux*" ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#NAME="linux*"
[root@centos8 ~]#[[ "$NAME" == "linux*" ]]
[root@centos8 ~]#echo $?
0
#结论:[[ == ]] == 右侧的 * 做为通配符,不要加“”,只想做为*符号使用时, 需要加 “” 或转义



#例: 判断合理的考试成绩
[root@centos8 script]#SCORE=101
[root@centos8 script]#[[ $SCORE =~ 100|[0-9]{1,2} ]]
[root@centos8 script]#echo $?
0
[root@centos8 script]#[[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]
[root@centos8 script]#echo $?
1
[root@centos8 script]#SCORE=10
[root@centos8 script]#[[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]
[root@centos8 script]#echo $?
0
[root@centos8 script]#SCORE=abc
[root@centos8 script]#[[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]
[root@centos8 script]#echo $?
1


#例:使用 [[ ]] 判断文件后缀
#通配符
[root@centos8 ~]#FILE=test.log
[root@centos8 ~]#[[ "$FILE" == *.log ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#FILE=test.txt
[root@centos8 ~]#[[ "$FILE" == *.log ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[[ "$FILE" != *.log ]]
[root@centos8 ~]#echo $?
0
#正则表达式
[root@centos8 ~]#[[ "$FILE" =~ \.log$ ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#FILE=test.log
[root@centos8 ~]#[[ "$FILE" =~ \.log$ ]]
[root@centos8 ~]#echo $?
0


#例: 判断合法的非负整数
[root@centos8 ~]#N=100
[root@centos8 ~]#[[ "$N" =~ ^[0-9]+$ ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#N=xin10
[root@centos8 ~]#[[ "$N" =~ ^[0-9]+$ ]]
[root@centos8 ~]#echo $?
1


#例: 判断合法IP
[root@centos8 ~]#IP=1.2.3.4
[root@centos8 ~]#[[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#IP=1.2.3.4567
[root@centos8 ~]#[[ "$IP" =~ ^([0-9]{1,3}.){3}[0-9]{1,3}$ ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}
([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]
[root@centos8 ~]#echo $?
1


#[root@centos7 ~]#cat check_ip.sh
#!/bin/bash
IP=$1
[[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]
{2}|2[0-4][0-9]|25[0-5])$ ]] && echo $IP is valid || echo $IP is invalid

6.4 文件测试

#存在性测试-a FILE:同 -e
-e FILE: 文件存在性测试,存在为真,否则为假
-b FILE:是否存在且为块设备文件
-c FILE:是否存在且为字符设备文件
-d FILE:是否存在且为目录文件
-f FILE:是否存在且为普通文件
-h FILE 或 -L FILE:存在且为符号链接文件
-p FILE:是否存在且为命名管道文件
-S FILE:是否存在且为套接字文件


#例: -e和-a 表示判断文件的存在性,建议使用-e
#文件是否不存在
[root@centos8 ~]#[ -a /etc/nologin ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#! [ -a /etc/nologin ]
[root@centos8 ~]#echo $?
0
#文件是否存在
[root@centos8 ~]# [ -a /etc/issue ]
[root@centos8 ~]#echo $?
0
#取反后结果却没有变化
[root@centos8 ~]# [ ! -a /etc/issue ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#! [ -a /etc/issue ]
[root@centos8 ~]#echo $?
1
#文件是否存在
[root@centos8 ~]#! [ -e /etc/issue ]
[root@centos8 ~]#echo $?
1
#此为推荐写法
[root@centos8 ~]#[ ! -e /etc/issue ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ -d /etc ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ -d /etc/issue ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ -L /bin ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ -L /bin/ ]
[root@centos8 ~]#echo $?
1



#文件权限测试:
-r FILE:是否存在且可读
-w FILE: 是否存在且可写
-x FILE: 是否存在且可执行
-u FILE:是否存在且拥有suid权限
-g FILE:是否存在且拥有sgid权限
-k FILE:是否存在且拥有sticky权限

#注意:最终结果由用户对文件的实际权限决定,而非文件属性决定


#例
[root@centos8 ~]#[ -w /etc/shadow ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ -x /etc/shadow ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[ -w test.txt ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#chattr +i test.txt
[root@centos8 ~]#lsattr test.txt
----i-------------- nianling.txt
[root@centos8 ~]#[ -w test.txt ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#chattr -i test.txt
[root@centos8 ~]#[ -w test.txt ]
[root@centos8 ~]#echo $?
0




#文件属性测试
-s FILE #是否存在且非空
-t fd #fd 文件描述符是否在某终端已经打开
-N FILE #文件自从上一次被读取之后是否被修改过
-O FILE #当前有效用户是否为文件属主
-G FILE #当前有效用户是否为文件属组
FILE1 -ef FILE2 #FILE1是否是FILE2的硬链接
FILE1 -nt FILE2 #FILE1是否新于FILE2(mtime)
FILE1 -ot FILE2 #FILE1是否旧于FILE2

6.5关于 () 和 {}

CMD1;CMD2;...)和 { CMD1;CMD2;...; } 都可以将多个命令组合在一起,批量执行

[root@centos8 ~]#man bash

#( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境
#帮助参看:man bash 搜索(list)
#{ list; } 不会启子shell, 在当前shell中运行,会影响当前shell环境
#帮助参看:man bash 搜索{ list; }


#例: () 和 {}
[root@centos8 ~]#name=xin;(echo $name;name=xin;echo $name );echo $name
liang
xin
liang
[root@centos8 ~]#name=xin;{ echo $name;name=xin;echo $name; } ;echo $name
liang
xin
xin
[root@centos8 ~]#umask
0022
[root@centos8 ~]#(umask 066;touch f1.txt)
[root@centos8 ~]#ll f1.txt
-rw------- 1 root root 0 Dec 23 16:58 f1.txt
[root@centos8 ~]#umask
0022
[root@centos8 ~]#( cd /data;ls )
test.log
[root@centos8 ~]#pwd
/root
[root@centos8 ~]#{ cd /data;ls; }
test.log
[root@centos8 data]#pwd
/data
[root@centos8 data]#
#()会开启子shell
[root@centos8 ~]#echo $BASHPID
1920
[root@centos8 ~]#( echo $BASHPID;sleep 100)
1979
[root@centos8 ~]#pstree -p
├─sshd(719)───sshd(1906)───sshd(1919)─┬─bash(1920)───bash(1979)───sleep(1980)
#{ } 不会开启子shell
[root@centos8 ~]#echo $BASHPID
1920
[root@centos8 ~]#{ echo $BASHPID; }
1920

6.6组合测试条件

6.6.1 第一种方式

[ EXPRESSION1 -a EXPRESSION2 ] #并且,EXPRESSION1和EXPRESSION2都是真,结果才为真
[ EXPRESSION1 -o EXPRESSION2 ] #或者,EXPRESSION1和EXPRESSION2只要有一个真,结果就为
真
[ ! EXPRESSION ] #取反


#说明: -a 和 -o 需要使用测试命令进行,[[ ]] 不支持


#例:
[root@centos8 ~]#FILE="/data/scrips/test.sh"
[root@centos8 ~]#ll /data/scrips/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh
[root@centos8 ~]#[ -f $FILE -a -x $FILE ]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#chmod +x /data/scripts/test.sh
[root@centos8 ~]#ll /data/scripts/test.sh
-rwxr-xr-x 1 root root 382 Dec 23 09:32 /data/script40/test.sh
#并且
[root@centos8 ~]#[ -f $FILE -a -x $FILE ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#chmod -x /data/scripts/test.sh
[root@centos8 ~]#ll /data/scripts/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh
#或者
[root@centos8 ~]#[ -f $FILE -o -x $FILE ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ -x $FILE ]
[root@centos8 ~]#echo $?
1
#取反
[root@centos8 ~]#[ ! -x $FILE ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#! [ -x $FILE ]
0

6.6.2 第二种方式

COMMAND1 && COMMAND2 #并且,短路与,代表条件性的AND THEN
如果COMMAND1 成功,将执行COMMAND2,否则,将不执行COMMAND2

COMMAND1 || COMMAND2 #或者,短路或,代表条件性的OR ELSE
如果COMMAND1 成功,将不执行COMMAND2,否则,将执行COMMAND2

! COMMAND   #非,取反



#[root@centos7 ~]#[ $[RANDOM%6] -eq 0 ] && rm -rf /* || echo "click"


#例:
[root@centos8 ~]#test "A" = "B" && echo "Strings are equal"
[root@centos8 ~]#test "A"-eq "B" && echo "Integers are equal"
[root@centos8 ~]#[ "A" = "B" ] && echo "Strings are equal"
[root@centos8 ~]#[ "$A" -eq "$B" ] && echo "Integers are equal"
[root@centos8 ~]#[ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
[root@centos8 ~]#[ -z "$HOSTNAME" -o "$HOSTNAME" = "localhost.localdomain" ]&&
hostname www.baidu.com
[root@centos8 ~]#id xin &> /dev/null ||  useradd xin
[root@centos8 ~]#id zhang &> /dev/null ||  useradd zhang
[root@centos8 ~]#getent passwd zhang
zhang:x:1002:1002::/home/zhang:/bin/bash
[root@centos8 ~]#grep -q no_such_user /etc/passwd || echo 'No such user'
No such user


#例:
[root@centos8 ~]#[ -f “$FILE” ] && [[ “$FILE”=~ .*\.sh$ ]] && chmod +x $FILE
[root@centos8 ~]#ping -c1 -W1 172.16.0.1 &> /dev/null && echo '172.16.0.1 is
up' || (echo '172.16.0.1 is unreachable'; exit 1)
172.16.0.1 is up
[root@centos8 ~]#IP=10.0.0.111;ping -c1 -W1 $IP &> /dev/null && echo $IP is up
|| echo $IP is down
10.0.0.111 is down
[root@centos8 ~]#IP=10.0.0.1;ping -c1 -W1 $IP &> /dev/null && echo $IP is up ||
echo $IP is down
10.0.0.1 is up


#例:&& 和 || 组合使用
[root@centos8 ~]#NAME=xin; id $NAME &> /dev/null && echo "$NAME is exist"
xin is exist
[root@centos8 ~]#NAME=xin; id $NAME &> /dev/null || echo "$NAME is not
exist"
xin  is not exist
[root@centos8 ~]#NAME=xinxin; id $NAME &> /dev/null && echo "$NAME is exist" ||
echo "$NAME is not exist"
xinxin is not exist
[xin @centos8 ~]#NAME=xin; id $NAME &> /dev/null && echo "$NAME is exist" ||
echo "$NAME is not exist"
xin is exist
[root@centos8 ~]#NAME=xinxin; id $NAME &> /dev/null && echo "$NAME is exist" ||
echo "$NAME is not exist"
xin is exist
[root@centos8 ~]#NAME=xinxin; id $NAME &> /dev/null || echo "$NAME is not exist"
&& echo "$NAME is exist"
xin is exist

[root@centos8 ~]#NAME=xinxin; id $NAME &> /dev/null || echo "$NAME is not
exist" && echo "$NAME is exist"
xinxin is not exist
xinxin is exist
#结论:如果&& 和 || 混合使用,&& 要在前,|| 放在后
[root@centos8 ~]#NAME=xinxin; id $NAME &> /dev/null && echo "$NAME is exist" ||
useradd $NAME
[root@centos8 ~]#id xinxin
uid=1002(xinxin) gid=1002(xinxin) groups=1002(xinxin)
[root@centos8 ~]#NAME=xinxin; id $NAME &> /dev/null && echo "$NAME is exist" ||
( useradd $NAME; echo $NAME is created )
xinxin is created
[root@centos8 ~]#id xinxin
uid=1003(xinxin) gid=1003(xinxin) groups=1003(xinxin)
[root@centos8 ~]#NAME=xinxinxin; id $NAME &> /dev/null && echo "$NAME is exist"
|| { useradd $NAME; echo $NAME is created; }
xinxinxin is created



#例:网络状态判断
[root@centos8 ~]#cat /data/scripts/ping.sh
#!/bin/bash
IP=172.16.0.1
ping -c1 -W1 $IP &> /dev/null && echo "$IP is up" || {  echo "$IP is
unreachable"; exit; }
echo "Script is finished"
[root@centos8 ~]#bash /data/scripts/ping.sh
172.16.0.1 is up
Script is finished


#例:
[root@centos8 ~]#. /etc/os-release; [[ $ID == "rocky" ]] && [[ $VERSION_ID ==
8* ]] && echo Rocky8 || echo CentOS8
[root@rocky8 ~]#. /etc/os-release; [[ $ID == "rocky" ]] && [[ $VERSION_ID == 8*
]] && echo Rocky8 || echo CentOS8
Rocky8


#例:磁盘空间的判断
[root@centos8 ~]#cat /data/script/disk_check.sh
#!/bin/bash
WARNING=80
SPACE_USED=`df|grep '^/dev/sd'|tr -s ' ' %|cut -d% -f5|sort -nr|head -1`
[ "$SPACE_USED" -ge $WARNING ] && echo "disk used is $SPACE_USED,will be full"
| mail -s diskwaring root


#例:磁盘空间和Inode号的检查脚本
[root@centos8 scripts]#cat disk_check.sh
#!/bin/bash
WARNING=80
SPACE_USED=`df | grep '^/dev/sd'|grep -oE '[0-9]+%'|tr -d %| sort -nr|head -1`
INODE_USED=`df -i | grep '^/dev/sd'|grep -oE '[0-9]+%'|tr -d %| sort -nr|head
-1`
[ "$SPACE_USED" -gt $WARNING -o "$INODE_USED" -gt $WARNING ] && echo "DISK
USED:$SPACE_USED%, INODE_USED:$INODE_USED,will be full" | mail -s "DISK Warning"
root@wxx.com 

6.7使用read命令来接受输入

使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置变量REPLY

#格式:
read [options] [name ...]


#常用选项:
-p  指定要显示的提示
-s   静默输入,一般用于密码
-n N 指定输入的字符长度N
-d '字符'  输入结束符
-t N TIMEOUT为N秒



#[root@centos8 ~]#read
xiaoming
[root@centos8 ~]#echo $REPLY
xiaoming
[root@centos8 ~]#read NAME TITLE
xin cto
[root@centos8 ~]#echo $NAME
xin 
[root@centos8 ~]#echo $TITLE
cto
[root@centos8 ~]#read -p "Please input your name: " NAME
Please input your name: xin 
[root@centos8 ~]#echo $NAME
xin 



#例
[root@centos8 ~]#read x y z <<< "I love you"
[root@centos8 ~]#echo $x
I
[root@centos8 ~]#echo $y
love
[root@centos8 ~]#echo $z
you
[root@centos8 ~]#


#例:
[root@centos8 ~]#man bash
#Pipelines:A pipeline is a sequence of one or more commands separated by one of
the control operators | or |&
Each command in a pipeline is executed as a separate process (i.e., in a
subshell).

[root@centos8 ~]#echo baidu | read NAME
[root@centos8 ~]#echo $NAME
[root@centos8 ~]#echo baidu  | { read NAME; echo $NAME; }
baidu 



#例:read和输入重定向
[root@centos8 scripts]#cat test.txt
1 2
[root@centos8 scripts]#read i j < test.txt ; echo i=$i j=$j
i=1 j=2
[root@centos8 scripts]#echo 1 2 | read x y ; echo x=$x y=$y
x= y=
[root@centos8 ~]#echo 1 2 | ( read x y ; echo x=$x y=$y )
x=1 y=2
[root@centos8 ~]#echo 1 2 | { read x y ; echo x=$x y=$y; }
x=1 y=2


#例:判断用户输入的是否为 YES
[root@centos8 scripts]#cat read.sh
#!/bin/bash
read -p "Are you rich?yes or no: " ANSWER
[[ $ANSWER =~ ^([Yy]|[Yy][Ee][Ss])$ ]] && echo "You are rich" || echo "Good Good
Study,Day Day Up!"


#例: 判断用户输入的是否为 YES
root@ubuntu2004:~# cat yesorno.sh
#!/bin/bash
read -p "Please input yes or no: " input
answer=`echo $input| tr 'A-Z' 'a-z'`
[ $answer = 'yes' -o $answer = 'y' ] && echo YES
[ $answer = 'no' -o $answer = 'n' ] && echo NO

root@ubuntu2004:~# cat yesorno2.sh
#!/bin/bash
read -p "Please input yes or no: " input
[[ $input =~ ^([Yy][Ee][Ss]|[Yy])$ ]] && echo YES
[[ $input =~ ^([Nn][Oo]|[Nn])$ ]] && echo NO


#例: 检查主机的网络状态
[root@centos8 script]#cat check_host.sh
#!/bin/bash
read -p "Please input a IP: " IP
[[  "$IP" =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-
9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]] || { echo "IP is invalid";exit; }
ping -c1 -W1 $IP &> /dev/null && echo $IP is up || echo $IP is down


#例:
read -p “Enter a filename: “ FILE


#例:鸡兔同笼算法,今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何?
[root@centos8 ~]#cat /data/script40/chook_rabbit.sh
#!/bin/bash
read -p "请输入头的数量: " HEAD
read -p "请输入脚的数量: " FOOT
RABBIT=$[FOOT/2-HEAD]
CHOOK=$[HEAD-RABBIT]
echo "兔子: " $RABBIT
echo "鸡: " $CHOOK


#例:实现运维工作菜单
[root@centos8 scripts]#cat work_menu.sh
#!/bin/bash
. /etc/init.d/functions
echo -en "\E[$[RANDOM%7+31];1m"
cat <<EOF
请选择:
1)备份数据库
2)清理日志
3)软件升级
4)软件回滚
5)删库跑路
EOF
echo -en '\E[0m'
read -p  "请选择上面项对应的数字1-5: " MENU
[ $MENU -eq 1 ] && ./backup.sh
[ $MENU -eq 2 ] && action "清理日志"
[ $MENU -eq 3 ] && action "软件升级"
[ $MENU -eq 4 ] && action "软件回滚"
[ $MENU -eq 5 ] && action "删库跑路"

bash shell的配置文件很多,可以分成下面类别

7.1按生效范围划分两类

*全局配置:针对所有用户皆有效

/etc/profile
/etc/profile.d/*.sh
/etc/bashrc

*个人配置:只针对特定用户有效

~/.bash_profile
~/.bashrc

7.2shell登录两种方式分类

1 交互式登录

直接通过终端输入账号密码登录
使用 su - UserName 切换的用户

#配置文件生效和执行顺序:
#放在每个文件最前
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
~/ .bash_ profile
~/ .bashrc
/etc/bashrc
#放在每个文件最后
/etc/profile.d/*.sh
/etc/bashrc
/etc/profile
/etc/bashrc   #此文件执行两次
~/.bashrc
~/.bash_profile


#注意:文件之间的调用关系,写在同一个文件的不同位置,将影响文件的执行顺序

2 非交互式登录

        su UserName
        图形界面下打开的终端
        执行脚本
        任何其它的bash实例

#执行顺序:
/etc/profile.d/*.sh
/etc/bashrc
~/.bashrc


#例: 将命令放在最前面
Last login: Wed Jun 10 11:24:03 2020 from 10.0.0.1
/etc/profile
/etc/profile.d/test.sh
/etc/bashrc
~/.bash_profile
~/.bashrc
/etc/bashrc

[root@centos8 ~]#su - root
Last login: Wed Jun 10 12:26:31 CST 2020 from 10.0.0.1 on pts/0
/etc/profile
/etc/profile.d/test.sh
/etc/bashrc
~/.bash_profile
~/.bashrc
/etc/bashrc
[root@centos8 ~]#exit
logout

[root@centos8 ~]#su root
~/.bashrc
/etc/bashrc
/etc/profile.d/test.sh



#例:将命令放在最后面
Last login: Wed Jun 10 12:29:20 2020 from 10.0.0.1
/etc/profile.d/test.sh
/etc/bashrc
/etc/profile
/etc/bashrc
~/.bashrc
~/.bash_profile

[root@centos8 ~]#su - root
Last login: Wed Jun 10 12:30:03 CST 2020 from 10.0.0.1 on pts/3
/etc/profile.d/test.sh
/etc/bashrc
/etc/profile
/etc/bashrc
~/.bashrc
~/.bash_profile

[root@centos8 ~]#su root
/etc/profile.d/test.sh
/etc/bashrc
~/.bashrc

7.3按功能划分分类

profile类和bashrc类

1 Profile类

profile类为交互式登录的shell提供配置

功用:
(1) 用于定义环境变量
(2) 运行命令或脚本

全局:/etc/profile, /etc/profile.d/*.sh
个人:~/.bash_profile

2 Bashrc类

bashrc类:为非交互式和交互式登录的shell提供配置

功用:
(1) 定义命令别名和函数
(2) 定义本地变量

全局:/etc/bashrc
个人:~/.bashrc

7.4 编辑配置文件生效

修改profile和bashrc文件后需生效两种方法:
        1. 重新启动shell进程
        2. source|. 配置文件
注意:source 会在当前shell中执行脚本,所有一般只用于执行置文件,或在脚本中调用另一个脚本的场景

.  ~/.bashrc

7.5 Bash 退出任务

保存在~/.bash_logout文件中(用户),在退出登录shell时运行
功能:
        创建自动备份
        清除临时文件

8流程控制

8.1 条件判断分绍

单分支条件

多分支条件

 

8. 2 选择执行 if 语句

#格式:
if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else
COMMANDS; ] fi


#单分支:
if 判断条件;then
   条件为真的分支代码
fi


#双分支:
if 判断条件; then
    条件为真的分支代码
else
    条件为假的分支代码
fi


#多分支:
if 判断条件1; then
    条件1为真的分支代码
elif 判断条件2; then
    条件2为真的分支代码
elif 判断条件3; then
    条件3为真的分支代码
...
else
    以上条件都为假的分支代码
fi




#注释:
多个条件时,逐个条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句
if 语句可嵌套



#例:
[root@centos6 ~]#declare -f
__udisks ()
{
 local IFS='
';
 local cur="${COMP_WORDS[COMP_CWORD]}";
  if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--show-info" ]; then
    COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" -- $cur));
  else
    if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--inhibit-polling" ]; then
      COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" --
$cur));
    else
      if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--mount" ]; then
        COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" --
$cur));
else
        if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--unmount" ]; then
          COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)"
-- $cur));
        else
          if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--detach" ];
then
            COMPREPLY=($(compgen -W "$(udisks --enumerate-device-
files)" -- $cur));
          else
            if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--ata-
smart-refresh" ]; then
              COMPREPLY=($(compgen -W "$(udisks --enumerate-
device-files)" -- $cur));
            else
              if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--ata-
smart-simulate" ]; then
               _filedir || return 0;
              else
                if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--
set-spindown" ]; then
                  COMPREPLY=($(compgen -W "$(udisks --
enumerate-device-files)" -- $cur));
                else
                  if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" =
"--poll-for-media" ]; then
                    COMPREPLY=($(compgen -W "$(udisks --
enumerate-device-files)" -- $cur));
                  else
                    COMPREPLY=($(IFS=: compgen -W "--dump:--
inhibit-polling:--inhibit-all-polling:--enumerate:--enumerate-device-files:--
monitor:--monitor-detail:--show-info:--help:--mount:--mount-fstype:--mount-
options:--unmount:--unmount-options:--detach:--detach-options:--ata-smart-
refresh:--ata-smart-wakeup:--ata-smart-simulate:--set-spindown:--set-spindown-
all:--spindown-timeout:--poll-for-media" -- $cur));
                  fi;
                fi;
              fi;
            fi;
          fi;
        fi;
      fi;
    fi;
  fi
}
[root@centos6 ~]#


#例:
#根据命令的退出状态来执行命令
if ping -c1 -W2 station1 &> /dev/null; then  
    echo 'station1 is UP' 
elif  grep -q 'station1' ~/maintenance.txt;  then  
    echo 'station1 is undergoing maintenance'
else 
    echo 'station1 is unexpectedly DOWN!'  
    exit 1
fi


#例:身体质量指数 (BMI)
[root@centos8 ~]#cat if_bmi.sh
#!/bin/bash
read -p "请输入身高(m为单位): " HIGH
if [[ ! "$HIGH" =~ ^[0-2](\.[0-9]{,2})?$ ]];then
    echo "输入错误的身高!"
    exit 1
fi
read -p "请输入体重(kg为单位): " WEIGHT
if [[ ! "$WEIGHT" =~ ^[0-9]{1,3}$ ]];then echo "输入错误的体重!"; exit 2; fi
BMI=`echo $WEIGHT/$HIGH^2|bc`
if [ $BMI  -le 18 ] ;then
    echo "太瘦了,多吃点!"
elif [ $BMI  -lt 24 ] ;then
    echo "身材很棒!"
else
    echo "太胖了,注意节食,加强运动!"
fi

8.3 条件判断 case 语句

#格式:
case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac


case 变量引用 in
PAT1)
    分支1
    ;;
PAT2)
    分支2
    ;;
...
*)
    默认分支
    ;;
esac


#case支持glob风格的通配符:
*     任意长度任意字符
?     任意单个字符
[]     指定范围内的任意单个字符
|      或者,如: a|b


#例:
[root@centos8 scripts]#cat case_yesorno.sh
#!/bin/bash
read -p "Do you agree(yes/no)? " INPUT

INPUT=`echo $INPUT | tr 'A-Z' 'a-z'`

case $INPUT in
y|yes)
    echo "You input is YES"
  ;;
    echo "You input is YES"
  ;;
*)
    echo "Input fales,please input yes or no!"
esac


[root@centos8 scripts]#cat case_yesorno2.sh
#!/bin/bash
read -p "Do you agree(yes/no)? " INPUT
case $INPUT in
[yY]|[Yy][Ee][Ss])
    echo "You input is YES"
 ;;
[Nn]|[Nn][Oo])
    echo "You input is NO"
 ;;
*)
    echo "Input fales,please input yes or no!"                 
              
esac



#例: 文件后缀处理
[root@centos8 script]#cat suffix.sh
#!/bin/bash
read -p "please input a file: " FILE

SUFFIX=`echo $FILE | grep -Eo "[^.]+$"`
case $SUFFIX in
gz)
    echo gzip
 ;;
bz2)
    echo bzip2
 ;;
xz)
    echo xz
 ;;
Z)
    echo compress
 ;;
zip)
    echo zip
 ;;
*)
    echo other
 ;;
esac



#例:运维菜单实现版本2
[root@centos8 scripts]#cat work_menu.sh
#!/bin/bash
echo -en "\E[$[RANDOM%7+31];1m"
cat <<EOF
请选择:
1)备份数据库
2)清理日志
3)软件升级
4)软件回滚
5)删库跑路
EOF
echo -en '\E[0m'
read -p  "请输入上面数字1-5: " MENU
case $MENU in
1)
echo "执行备份数据库"
 #./backup.sh
 ;;
2)
  echo "清理日志"
 ;;
3)
  echo "软件升级"
 ;;
4)
  echo "软件回滚"
 ;;
5)
  echo "删库跑路"
 ;;
*)
  echo "INPUT FALSE!"
esac

9循环

9.1 循环执行介绍

将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件
重复运行次数
        循环次数事先已知
        循环次数事先未知
常见的循环的命令:for, while, until

9.2 循环 for 

#CentOS7的for帮助比CentOS8全面
[root@centos7 ~]#help for
for: for NAME [in WORDS ... ] ; do COMMANDS; done
     Execute commands for each member in a list.
 
     The `for' loop executes a sequence of commands for each member in a
     list of items. If `in WORDS ...;' is not present, then `in "$@"' is
     assumed. For each element in WORDS, NAME is set to that element, and
     the COMMANDS are executed.
 
     Exit Status:
     Returns the status of the last command executed.
for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
     Arithmetic for loop.
 
     Equivalent to
         (( EXP1 ))
          while (( EXP2 )); do
             COMMANDS
             (( EXP3 ))
      done
     EXP1, EXP2, and EXP3 are arithmetic expressions. If any expression is
     omitted, it behaves as if it evaluates to 1.
 
     Exit Status:
     Returns the status of the last command executed.
[root@centos7 ~]#


#格式1:

#格式:for NAME [in WORDS ... ] ; do COMMANDS; done

#方式1
for 变量名  in 列表;do
    循环体
done


#方式2
for 变量名  in 列表
do
    循环体
done

执行机制:
        依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
        如果省略 [in WORDS ... ] ,此时使用位置参数变量 in "$@"
for 循环列表生成方式:
        直接给出列表
        整数列表:

{start..end}
$(seq [start [step]] end)

返回列表的命令:

$(COMMAND)

使用glob,如:*.sh
变量引用,如:$@,$*,$#

#例:计算1+2+3+...+100 的结果
[root@centos8 ~]#sum=0;for i in {1..100};do let sum+=i;done ;echo sum=$sum
sum=5050
[root@centos8 ~]#seq -s+ 100|bc5050
5050
[root@centos8 ~]#echo {1..100}|tr ' ' +|bc
5050
[root@centos8 ~]#seq 100|paste -sd +|bc
5050


#例: 100以内的奇数之和
[root@centos8 ~]#sum=0;for i in {1..100..2};do let sum+=i;done;echo sum=$sum
sum=2500
[root@centos8 ~]#seq -s+ 1 2 100| bc
2500
[root@centos8 ~]#echo {1..100..2}|tr ' ' + | bc
2500


#例:
[root@centos8 ~]#cat /data/scripts/for_sum.sh
#!/bin/bashsum=0
for i in $* ; do
     let sum+=i
done
echo sum=$sum

[root@centos8 ~]#bash /data/scripts/for_sum.sh 1 2 3 4 5 6
sum=21


#例:
例: 批量创建用户
[root@centos8 ~]#cat createuser.sh
#!/bin/bash
[ $# -eq 0 ] && { echo "Usage: createuser.sh USERNAME ..." ;exit 1 ; }
for user ;do
    id $user &> /dev/null && echo $user is exist || { useradd $user ; echo $user
is created; }
done


#例: 批量创建用户和并设置随机密码
[root@centos8 script]#cat user_for.sh
#!/bin/bash
for i in {1..10};do
     useradd user$i
      PASS=`cat /dev/urandom | tr -dc '[:alnum:]' |head -c12`
      echo $PASS | passwd --stdin user$i &> /dev/null
      echo user$i:$PASS >> /data/user.log
      echo "user$i is created"
done



#例: 九九乘法表
[root@centos8 script]#cat 9x9_for.sh
#!/bin/bash
for i in {1..9};do
      for j in `seq $i`;do
        echo -e "${j}x${i}=$[j*i]\t\c"
  done
  echo
done


#例: printf 实现九九乘法表
for i in {1..9};do
    for j in {1..9};do
        printf "%sx%s=%s\t" $j $i $[i*j]
       [ $i -eq $j ] && break
  done
 printf '\n'
done


#例: 实现九九乘法表
[root@centos8 ~]#vim 9x9.sh
#!/bin/bash
for j in {1..9};do
    for i in `seq $j`;do
        echo -e "\E[1;$[RANDOM%7+31]m${i}x${j}=$[i*j]\E[0m\t\c"
  done
  echo
done
echo
for((i=1;i<=9;i++));do
  for((j=1;j<=i;j++));do
   printf "\E[1;$[RANDOM%7+31]m${i}x${j}=$[i*j]\E[0m\t"
  done
 printf "\n"
done


#例: 倒状的九九乘法表
[root@ubuntu1804 ~]#cat 9x9_v2.sh
#!/bin/bash
for i in {1..9};do
  for j in $(seq `echo $[10-$i]`);do
    echo -ne "${j}x`echo $[10-i]`=$(((10-i)*j))\t"
  done
  echo
done


#例:将指定目录下的文件所有文件的后缀改名为 bak 后缀
#cat /data/scripts/for_rename.sh
#!/bin/bash
DIR=/data/test
cd $DIR || {  echo 无法进入 $DIR;exit 1; }
for FILE in * ;do
PRE=`echo $FILE|grep -Eo ".*\."`
  mv $FILE ${PRE}bak
#  PRE=`echo $FILE|rev|cut -d. -f 2-|rev`
#  PRE=`echo $FILE | sed -nr 's/(.*)\.([^.]+)$/\1/p'
#  SUFFIX=`echo $FILE | sed -nr 's/(.*)\.([^.]+)$/\2/p'`
#  mv $FILE $PRE.bak
done


#例:要求将目录YYYY-MM-DD/中所有文件,移动到YYYY-MM/DD/下
[root@centos8 ~]#cat for_dir.sh
#!/bin/bash
PDIR=/data/test
for i in {1..365};do
    DIR=`date -d "-$i day" +%F`
    mkdir -pv $PDIR/$DIR
    cd $PDIR/$DIR
    for j in {1..10};do
        touch $RANDOM.log
done
done


##2.将上面的目录移动到YYYY-MM/DD/下 
#!/bin/bash
#
DIR=/data/test
cd $DIR || {  echo 无法进入 $DIR;exit 1; }
for subdir in * ;do
                YYYY_MM=`echo $subdir |cut -d"-" -f1,2`
                DD=`echo $subdir |cut -d"-" -f3`
                [ -d $YYYY_MM/$DD ] || mkdir -p $YYYY_MM/$DD &> /dev/null
                mv $subdir/*  $YYYY_MM/$DD
done
rm -rf $DIR/*-*-*


#例:
扫描一个网段:10.0.0.0/24,判断此网段中主机在线状态,将在线的主机的IP打印出来
#cat /data/scripts/for_scan_host.sh
#!/bin/bash
NET=10.0.0
for ID in {1..254};do
    {
  ping -c1 -W1 $NET.$ID &> /dev/null && echo $NET.$ID is up| tee -a
host_list.log || echo $NET.$ID is down
 }&
done
wait

#格式2

双小括号方法,即((…))格式,也可以用于算术运算,双小括号方法也可以使bash Shell实现C语言风格的变量操作
I=10;((I++))

#

#for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done

for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
    循环体
done

#注释:
控制变量初始化:仅在运行到循环代码段时执行一次
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断


#例:
cat sum.sh

#!/bin/bash
for((sum=0,i=1;i<=100;i++));do
        let sum+=i
done
echo sum=$sum

for((sum=0,i=1;i<=100;sum+=i,i++));do  
        true
done
echo sum=$sum


[root@ubuntu1804 ~]#bash sum.sh
sum=5050
sum=5050 


#例:九九乘法表
#语法1实现
for i in {1..9};do
  for j in `seq $i`;do
    echo -e "${j}x$i=$((j*i))\t\c"
  done
  echo
done
#语法2实现
for((i=1;i<10;i++));do
  for((j=1;j<=i;j++));do
    echo -e "${j}x$i=$((j*i))\t\c"
  done
  echo
done


#例:等腰三角形
[root@centos8 scripts]#cat for_triangle.sh
#!/bin/bash
read -p "请输入三角形的行数: " line
for((i=1;i<=line;i++));do
    for((k=0;k<=line-i;k++));do
        echo -e ' \c'
  done
  for((j=1;j<=2*i-1;j++));do
        echo -e '*\c'
  done
  echo
done
[root@centos8 scripts]#bash for_triangle.sh


#例:生成进度
[root@centos8 ~]#for ((i = 0; i <= 100; ++i)); do printf "\e[4D%3d%%" $i;sleep
0.1s; done
100%[root@centos8 ~]#




#例:
[root@centos8 ~]#for((;;));do echo for;sleep 1;done
for
for
for
for
for
for
for

 9.3循环 while

CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“true”,则执行一次循环;直到条件测试状态为“false”终止循环,因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正

        进入条件:CONDITION为 true
        退出条件:CONDITION为 false

格式:

while COMMANDS; do COMMANDS; done

while CONDITION; do
    循环体
done


#例:无限循环
while true; do
    循环体
done

while : ; do
    循环体
done


#例:
[root@centos8 ~]#sum=0;i=1;while ((i<=100));do let sum+=i;let i++;done;echo $sum
5050


#例:
#配置发邮件的邮箱
[root@centos8 ~]#cat .mailrc
set from=29308620@qq.com
set smtp=smtp.qq.com
set smtp-auth-user=29308620@qq.com
set smtp-auth-password=esvnhbnqocirbicf
set smtp-auth=login
set ssl-verify=ignore
[root@centos8 ~]#cat while_diskcheck.sh
#!/bin/bash
WARNING=80
while :;do
   USE=`df | sed -rn '/^\/dev\/sd/s#.* ([0-9]+)%.*#\1#p' |sort -nr|head -n1`
   if [ $USE -gt $WARNING ];then
       echo Disk will be full from `hostname -I` | mail  -s "disk warning"
29308620@qq.com
   fi
  sleep 10
done


#例: 防止Dos攻击的脚本
[root@centos8 ~]#cat check_link.sh
#!/bin/bash
WARNING=10
touch deny_hosts.txt
while true;do
 ss -nt | sed  -nr '1!s#.* ([0-9.]+):[0-9]+ *#\1#p'|sort |uniq -c|sort |
while read count ip;do
    if [ $count -gt $WARNING ];then
      echo $ip is deny
      grep -q "$ip" deny_hosts.txt || { echo $ip >> deny_hosts.txt;
iptables -A INPUT -s $ip -j REJECT; }
    fi
  done
  sleep 10
done

9.4 while 特殊用法 while read

while 循环的特殊用法,遍历文件或文本的每一行

注释:依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line

格式:

while read line; do
    循环体
done < /PATH/FROM/SOMEFILE


#例:
[root@centos8 ~]#echo baidu | read X ; echo $X
[root@centos8 ~]#echo baidu | while read X ; do echo $X;done
baidu
[root@centos8 ~]#echo baidu | { read X ; echo $X; }
baidu
[root@centos8 ~]#echo baidu | ( read X ; echo $X )
baidu
[root@centos8 ~]#echo xin wen zhang | ( read X Y Z; echo $X $Y $Z )
xin  wen zhang
[root@centos8 ~]#echo xin wen zhang | while read X Y Z; do echo $X $Y $Z;done
xin  wen zhang


#例:
cat while_read_diskcheck.sh
#!/bin/bash
WARNING=80
MAIL=root@wxc.com
df |sed -nr  "/^\/dev\/sd/s#^([^ ]+) .* ([0-9]+)%.*#\1 \2#p"|while read DEVICE
USE;do
if [ $USE -gt $WARNING ] ;then
  echo "$DEVICE will be full,use:$USE" | mail -s "DISK WARNING" $MAIL
fi
done


#例:
cat while_read_check_dos.sh
#!/bin/bash
MAX=3
lastb | sed -rn '/ssh:/s@.* ([0-9.]{1,3}{3}[0-9]{1,3}) .*@\1@p'|sort |uniq -c
|while read count ip ;do
    if [ $count -gt $MAX ];then
        iptables -A INPUT -s $ip -j REJECT
    fi
done


#例:查看/sbin/nologin的shell类型的用户名和UID
cat while_read_passwd.sh
#!/bin/bash
while read line ;do
    if [[ "$line" =~ /sbin/nologin$ ]] ;then
        echo $line | cut -d: -f1,3
    fi                                   
               
done < /etc/passwd

9.5循环 until

进入条件: CONDITION 为false
退出条件: CONDITION 为true

格式:

until COMMANDS; do COMMANDS; done

until CONDITION; do
    循环体
done


#例:
[root@centos8 ~]#sum=0;i=1;until ((i>100));do let sum+=i;let i++;done;echo $sum
5050


#无限循环
until false; do
    循环体
Done

9.6 循环控制语句 continue

continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层

格式:

while CONDITION1; do
    CMD1
    ...
    if CONDITION2; then
        continue
    fi
    CMDn
    ...
done


#例:
[root@centos8 script40]#cat continue_for.sh
#!/bin/bash
for ((i=0;i<10;i++));do
    for((j=0;j<10;j++));do
     [ $j -eq 5 ] && continue 2
     echo $j
    done
    echo ---------------------------
done
[root@centos8 script40]#bash continue_for.sh
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
...

9.7 循环控制语句 break

break [N]:提前结束第N层整个循环,最内层为第1层

while CONDITION1; do
    CMD1
    ...
    if CONDITION2; then
        break
    fi
    CMDn
    ...
done


#例:
[root@centos8 script40]#cat break_for.sh
#!/bin/bash
for ((i=0;i<10;i++));do
    for((j=0;j<10;j++));do
         [ $j -eq 5 ] && break
          echo $j
    done
    echo ---------------------------
done


#例:
[root@centos8 script40]#cat break_for.sh
#!/bin/bash
for ((i=0;i<10;i++));do
    for((j=0;j<10;j++));do
         [ $j -eq 5 ] && break 2
          echo $j
    done
echo ---------------------------
done



#例:
[root@centos8 script40]#cat menu.sh
#!/bin/bash
sum=0
COLOR='echo -e \033[1;31m'
COLOR2='echo -e \033[1;32m'
END="\033[0m"

while true;do
    echo -e "\033[33;1m\c"
    cat <<-EOF

1) 鲍鱼
2) 满汉全席
3) 龙虾
4) 燕窝
5) 帝王蟹
6) 点菜结束,结帐
 EOF
 echo -e "\033[0m"
read -p "请点菜(1-6): " MENU
case $MENU in
1|4)
  $COLOR'菜价: $10'$END
 let sum+=10
 ;;
3|5)
  $COLOR'菜价: $20'$END
 let sum+=20
 ;;
2)
  $COLOR'菜价: $200000'$END
 let sum+=200000
 ;;
6)
  $COLOR2"你点的菜总价格是 \$$sum"$END
 break
 ;;
*)
  echo "点错了,没有这道菜"
 ;;
esac
$COLOR2"你点的菜总价格是 \$$sum"$END
done


#例:
[root@centos8 script40]#cat guess.sh
#!/bin/bash
NUM=$[RANDOM%10]
while read -p "输入 0-9 之间的数字: " INPUT ;do
    if [ $INPUT -eq $NUM ];then
        echo "恭喜你猜对了!"
        break
    elif [ $INPUT -gt $NUM ];then
        echo "数字太大了,重新猜!"
    else
        echo "数字太小了,重新猜!"
    fi
done

9.8 循环控制 shift 命令

shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。
参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift

#例:doit.sh
#!/bin/bash
# Name: doit.sh
# Purpose: shift through command line arguments
# Usage: doit.sh [args]
while [ $You can't use 'macro parameter character #' in math mode# -gt 0 ] # or
(( $# > 0 ))
do
echo  $*
shift
done
./doit.sh a b c d e f g h

#例:shift.sh
#!/bin/bash
#step through all the positional parameters
until [ -z "$1" ]
do
echo "$1"
shift
done
echo 
./shfit.sh a b c d e f g h


#例:
[root@centos8 ~]#vim shift_batch_user.sh
#!/bin/bash
if [ $# -eq 0 ];then
  echo "Usage: `basename $0` user1 user2 ..."
  exit
fi

while [ "$1" ];do
    if id $1 &> /dev/null;then
      echo $1 is exist
    else
     useradd $1
     echo "$1 is created"
    fi
   shift
done
echo "All user is created"

9.9 循环与菜单 select

select 循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准错误上,并显示 PS3 提示
符,等待用户输入
用户输入菜单列表中的某个数字,执行相应的命令
用户输入菜单列表中的某个数字,会将对应的WORD值赋值给NAME变量
用户输入被保存在内置变量 REPLY 中
select 是个无限循环,因此要用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c
退出循环
select 经常和 case 联合使用
与 for 循环类似,可以省略 in list,此时使用位置参量

#格式:
select NAME [in WORDS ... ;] do COMMANDS; done

select NAME in list ;do
    循环体命令
done


#例:
cat select.sh
#!/bin/bash
#
sum=0
PS3="请点菜(1-6): "
select MENU in 北京烤鸭 佛跳墙 小龙虾 羊蝎子 火锅 点菜结束;do
case $REPLY in
1)
echo $MENU 价格是 100
let sum+=100
;;
2)
echo $MENU 价格是 88
let sum+=88
;;
3)
echo $MENU价格是 66
let sum+=66
;;
4)
echo $MENU 价格是 166
let sum+=166
;;
5)
echo $MENU 价格是 200
let sum+=200
;;
6)
echo "点菜结束,退出"
break
;;
*)
echo "点菜错误,重新选择"
;;
esac
done
echo "总价格是: $sum"

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值