Shell 入门(一):变量和流程控制
刚入职新公司,碰到一个棘手的线上莫名宕机问题。我需要查看应用日志,应用端口等信息,以前,只是知道tail -f service.log | grep '关键词'
这样的简单查询日志操作。另外,需要频繁打开应用目录,启动应用,然后切换到日志目录,查看日志,比较繁琐,后面我通过 Shell 的alias
功能,让操作得到一定程度的简化。但是,为了写出更好的执行脚本,于是有了这系列文章。
本系列文章分为两小节:
1,Shell 是什么?
2,Shell 基础
- 变量
- 流程控制
- 数组
- 函数
1,Shell 是什么?
简单来说,Shell 就是操作系统上运行的一个应用程序。
普通用户接触最多的操作系统是 Windows,而程序员使用更频繁的是 Linux 的服务端系统。
Windows 提供图形化界面,让我们可以通过移动光标,鼠标右键等一系列操作完成文件的编辑工作,降低普通用户的使用难度。
而程序员是以软件开发为职业,操作的是很少提供图形化界面的服务端系统,而 Shell 可以让用户更方便的操作 Linux 系统,
如文本或字符串检索,文件的查找或创建,大规模软件的自动部署,监控服务器性能,压缩文件等。
1.2,初识 Shell
- test.sh
#!/bin/bash
echo "Hello World!"
1.3 shell 脚本 Debug 调试
- 基本写法:
sh [-nxv] 脚本
-n
: 读一遍脚本中的命令但不执行,用于检查脚本中的语法错误;-v
: 一边执行脚本,一边将执行过的脚本命令打印到标准输出;-x
:提供跟踪执行信息,将执行的每一条命令和结果依次打印出来;
2, Shell 基础
- 变量
- 流程控制
- 数组
- 函数
2.1 变量
- 自定义变量
- 系统环境变量
- 预先定义变量
2.1.1 自定义变量
- 定义变量:
变量名=变量值
- 引用变量:
$变量名
或${变量名}
- 查看变量:
echo $变量名
或者set
显示所有变量,包括自定义变量和环境变量
2.1.2 系统环境变量
- 定义环境变量:
export 变量名=变量值
- 引用环境变量:
$变量名
- 查看环境变量:
echo $变量名
或者env | grep 变量名
2.1.3 预先定义变量
$0
: 脚本文件名称$*
: 当前脚本传入的所有参数$@
: 当前脚本传入的所有参数$#
: 当前脚本传入参数的个数$$
: 执行当前脚本进程的 PID$!
: 上一个后台进程的 PID$?
: 上一个命令的返回值,0 表示成功执行
2.1.4 单引号和双引号的困惑
[root@roclinux ~]$ echo $PWD
/root
[root@roclinux ~]$ alias dirA="echo work directory is $PWD"
[root@roclinux ~]$ alias dirB='echo work directory is $PWD'
# 正确显示
[root@roclinux ~]$ dirA
work directory is /root
# 正确显示
[root@roclinux ~]$ dirB
work directory is /root
# 显示不正确, 怎么回事?
[root@roclinux ~]$ cd /
[root@roclinux /]$ dirA
work directory is /root
# 正确显示
[root@roclinux /]$ dirB
work directory is /
# 查看 alias 的真实内容
[roc@roclinux ~]$ alias dirA
alias dirA="echo work directory is /root"
[roc@roclinux ~]$ alias dirB
alias dirB='echo work directory is $PWD'
- 示例来源:单引号和双引号的困惑
- 使用双引号的 dirA,通过 Shell 的变量转换后已经变成了字符串
echo work directory is /root
,当目录切换后,显示的还是原目录内容; - 使用单引号的 dirB,由于不受 Shell 的影响,仍然保留着原来的位置
echo work directory is $PWD
,当切换目录后再执行,变量$PWD
被 Shell 替换掉,因此内容被正确显示。 - 换句话说,双引号是弱引用,会解析变量,单引号是强引用,不会解析变量。
# 示例二:
[xiaoa@linux ~]$ name=zhangsan
[xiaoa@linux ~]$ echo 'My name is ${name}'
My name is ${name}
[xiaoa@linux ~]$ echo "My name is ${name}"
My name is zhangsan
2.1.5 命令替换
\``\
命令等价于$()
# 示例一,创建文件
touch `date +%F`_file1.txt
touch $(date +%F)_file2.txt
# 示例二,查看可用磁盘容量
disk_free3="df -Ph | grep '/$' | awk '{print $4}'" # 错误
disk_free4=$(df -Ph | grep '/$' | awk '{print $4}')
disk_free5=`df -Ph | grep '/$' | awk '{print $4}'`
2.1.6 变量数值运算
# 示例一: 整数运算 expr + - \* / %
[xiaoa@linux ~]$ a=1
[xiaoa@linux ~]$ b=2
[xiaoa@linux ~]$ expr $a + $b
3
[xiaoa@linux ~]$ expr $a \* $b
# 示例二:整数运算 let + - * / %
[xiaoa@linux ~]$ let sum=2+3;
[xiaoa@linux ~]$ echo $sum
5
# 示例三: 小数运算 bc + - * / %
[xiaoa@linux ~]$ echo "1+2" |bc
[xiaoa@linux ~]$ echo "scale=2;4/8" |bc
2.1.7 默认变量
${变量名-新的变量值}
:- 变量没有被赋值,会使用“新的变量值”替代;
- 变量有被赋值(包括空值):不会被替代
echo ${var1-test}
${变量名:-新的变量值}
:- 变量没有被赋值(包括空值),会使用“新的变量值”替代;
- 变量有被赋值:不会被替代
echo ${var1:-test}
2.2 流程控制
- 条件测试与逻辑运算表达式
- 流程控制语句
if
- 流程控制语句
case
- 循环语句
for
,while
2.2.1 条件测试 test
条件测试与逻辑运算表达式
- 基本语法
- 文件测试
- 数值比较
- 字符串比较
- 正则比较
2.2.1.1 基本语法
# 条件表达式
1,`test 条件表达式`
2,`[ 条件表达式 ]`
3,`[[ 条件表达式 ]]`
# 逻辑运算表达式
命令 1 ; 命令 2 无论命令 1 是否执行成功都会执行命令 2
命令 1 && 命令 2 命令 1 必须执行成功,才会执行命令 2
命令 1 || 命令 2 命令 1 必须执行失败,才会执行命令 2
2.2.1.1 文件测试
man [
: 查看[
表达式用法;[ -e dir|file ]
: 目录或文件是否存在;[ -d dir ]
: 目录是否存在;[ -f file ]
: 文件是否存在;[ -r file ]
: 当前用户对该文件是否具有读权限;
# 判断目录是否存在,不存在,则创建该目录
test -d ~/Desktop/test || mkdir -p ~/Desktop/test
2.2.1.2 数值比较
[ 整数1 操作符 整数 2 ]
- 大于:
[ 1 -gt 10 ]
- 小于:
[ 1 -lt 10 ]
- 等于:
[ 1 -eq 10 ]
- 不等于:
[ 1 -ne 10 ]
- 小于等于:
[ 1 -le 10 ]
- 大于等于:
[ 1 -ge 10 ]
# 示例一:磁盘使用率大于 80, 打印输出
#!/bin/bash
Disk_Free=$(df -h|grep "$"|awk '{print $5)'|awk -F '%' '{print $1}'})
if [ $Disk_Free -ge 80 ];then
echo "Disk is Use: $Disk_Free%" > /tmp/disk_use.txt
fi
# 示例二:多整数比对
# -a: and
[ 1 -lt 2 -a 5 -gt 10 ];echo $?
# -o: or
[ 1 -lt 2 -o 5 -gt 10 ];echo $?
# &&
[ 1 -lt 2 && 5 -gt 10 ];echo $?
# ||
[ 1 -lt 2 || 5 -gt 10 ];echo $?
2.2.1.3 字符串比较
[xiaoa@linux ~]$ name=zhangsan
[xiaoa@linux ~]$ [ $name = "zhangsan" ]; echo $?
0
[xiaoa@linux ~]$ [ $name == "zhangsan" ]; echo $?
0
[xiaoa@linux ~]$ echo ${#name} # 计算字符长度
8
[xiaoa@linux ~]$ BBB=""
[xiaoa@linux ~]$ echo ${#BBB}
0
[xiaoa@linux ~]$ [ -z "$BBB" ] 字符长度为 0
0
[xiaoa@linux ~]$ [ -n "$BBB" ] 字符长度不为 0
1
# 小结:变量为空或未定义,长度都为 0
2.2.1.4 正则比对
# 判断 name 是不是以 z 开头
[xiaoa@linux ~]$ [[ $name =~ ^z ]];echo $?
0
# 判断变量是不是数字
[xiaoa@linux ~]$ num=123
[xiaoa@linux ~]$ num2=123abc
[xiaoa@linux ~]$ [[ $num =~ ^[0-9]+$ ]];echo $?
0
[xiaoa@linux ~]$ [[ $num2 =~ ^[0-9]+$ ]];echo $?
1
2.2.2 流程控制语句 if
# 单分支结构
if [ 分数大于 60 ];then
echo "及格"
fi
# 双分支结构
if [ 分数大于 60 ];then
echo "及格"
else
echo "不及格"
fi
# 多分支结构
if [ 分数大于 80 ];then
echo "优秀"
elif [ 分数大于 60 ];then
echo "及格"
else
echo "不及格"
fi
2.2.3 流程控制语句 case
case 变量 in
模式 1)
执行命令 1;;
模式 2)
执行命令 2;;
模式 3)
执行命令 3;;
*)
执行默认命令
esac
# 示例 1
#!/bin/bash
case $1 in
Linux)
echo "linux ..."
;;
Shell)
echo "shell ..."
;;
MySQL)
echo "mysql ..."
;;
*)
# echo "USAGE $0 [Linux|Shell|MySQL]"
echo "USAGE `basename $0` [Linux|Shell|MySQL]"
esac
# 示例 2
#!/bin/bash
read -p "Are you sure?[y/n]:" action
case "$action" in
y|Y|yes|YES)
echo "user is deleted!"
;;
*)
echo "error"
esac
# 示例 3: 系统工具
#!/bin/bash
cat <<-EOF
===================
h 显示命令帮助
f 显示磁盘分区
d 显示磁盘挂载
m 查看内存使用
u 查看系统负载
q 退出程序
===================
EOF
while true
do
read -p "请输入你想查看的系统状态对应码[d/m/u/q] " sys
case $sys in
h)
help|less
;;
f)
clear
lsblk
;;
d)
clear
df -h
;;
m)
clear
free -m
;;
u)
clear
uptime
;;
q)
break
;;
*)
echo "error"
exit 1;
esac
done
2.2.4 循环语句
2.2.4.1 for
循环
# 基本语法
for 变量名 [ in 取值列表 ]
do
循环体
done
# 示例 1: 计算 1 到 100 的和
#!/bin/bash
sum=0
for i in {1..100}
do
let sum=sum+$i
done
echo $sum
# 示例 2:计算 1 到 100 之间所有奇数的和
#!/bin/bash
sum=0
for i in `seq 1 2 100`
do
let sum=sum+$i
done
echo $sum
2.2.4.2 while
循环
# 基本语法
while 条件测试
do
循环体
done
# 示例 1:计算 1~100 的和
#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
let sum=sum+$i # 注意:此处没有空格
let i++
done
echo $sum
# 示例 2: 猜数字游戏
#!/bin/bash
sum=$((RANDOM%51))
sleep 1
echo "请输入1-50之间的数,开始猜吧!"
count=0
function type_num(){
read -p "请输入一个数吧:" n
expr $n + 1 &>/dev/null
if [ $? -ne 0 ]; then
echo "请输入一个数字"
type_num
fi
}
function guess(){
(( count++ ))
if [ $n -eq $sum ]; then
echo "你猜中了,你的次数是:"${count}
if [ $count -lt 3 ]; then
echo "你太厉害了"
elif [ $count -ge 3 -a $count -lt 6 ]; then
echo "还是不错的,加油"
else
echo "你有点水啊"
fi
exit 0
elif [ $n -gt $sum ]; then
echo "猜大了"
type_num
else
echo "猜小了"
type_num
fi
}
function main(){
type_num
while true
do
guess
done
}
main
**参考资料:**