02 . Shell变量和逻辑判断及循环使用
Shell变量
系统变量
在命令行提示符直接执行 env、set 查看系统或环境变量。env 显示用户环境变量,set 显示 Shell预先定义好的变量以及用户变量。可以通过 export 导出成用户变量。
一些写Shell脚本时常用的系统变量
$SHELL 默认 Shell
$HOME 当前用户家目录
$IFS 内部字段分隔符
$LANG 默认语言
$PATH 默认可执行程序路径
$PWD 当前目录
$UID 当前用户 ID
$USER 当前用户
$HISTSIZE 历史命令大小,可通过 HISTTIMEFORMAT 变量设置命令执行时间
$RANDOM 随机生成一个 0 至 32767 的整数
$HOSTNAME 主机名
普通变量与临时环境变量
普通变量定义:VAR=value
临时环境变量定义:export VAR=value
变量引用:$VAR
下面看下他们之间区别:
Shell 进程的环境变量作用域是 Shell 进程,当 export 导入到系统变量时,则作用域是 Shell 进程及其 Shell 子进程。
Shell 进程的环境变量作用域是 Shell 进程,当 export 导入到系统变量时,则作用域是 Shell 进程及其 Shell 子进程。
ps axjf 输出的第一列是 PPID(父进程 ID),第二列是 PID(子进程 ID)当SSH 连接 Shell 时,当前终端 PPID(-bash)是 sshd 守护程序的 PID(root@pts/0),因此在当前终端下的所有进程的 PPID 都是-bash 的 PID,比如执行命令、运行脚本。
所以当在-bash 下设置的变量,只在-bash 进程下有效,而-bash 下的子进程 bash 是无效的,当export 后才有效。
进一步说明:再重新连接 SSH,去除上面定义的变量测试下所以在当前 shell 定义的变量一定要 export,否则在写脚本时,会引用不到。
还需要注意的是退出终端后,所有用户定义的变量都会清除。
在/etc/profile 下定义的变量就是这个原理.
位置变量
位置变量指的是函数或脚本后跟的第 n 个参数。
1−1−n,需要注意的是从第 10 个开始要用花括号调用,例如${10}
shift 可对位置变量控制,例如:
#!/bin/bash
echo "1: $1"
shift
echo "2: $2"
shift
echo "3: $3"
sh test.sh 1 2 3
1: 1
2: 3
3:
# 每执行一次 shift 命令,位置变量个数就会减一,而变量值则提前一位。shift n,可设置向前移动n位。
特殊变量
$0 # 脚本自身名字
$? # 返回上一条命令是否执行成功,0 为执行成功,非 0 则为执行失败
$# # 位置参数总数
$* # 所有的位置参数被看做一个字符串
$@ # 每个位置参数被看做独立的字符串
$$ # 当前进程 PID
$! # 上一条运行后台进程的 PID
变量引用
赋值运算符 | 示例 |
---|---|
= | 变量赋值 |
+= | 两个变量相加 |
自定义变量与引用
a=123
echo $a
123
a+=456
echo $a
123456
# Shell 中所有变量引用使用$符,后跟变量名
# 有时个别特殊字符会影响正常使用,就需要使用${a},例如
[root@redis ~]# b=123
[root@redis ~]# echo $b
123
[root@redis ~]# echo ${b}
123
# 有时个别特殊字符会影响正常引用,那么需要使用${VAR}
[root@redis ~]# echo $b123
[root@redis ~]# echo ${b}123
123123
# 将命令结果作为变量值
[root@redis ~]# c=`echo 123`
[root@redis ~]# echo $c
123
[root@redis ~]# c=$(echo 123)
[root@redis ~]# echo $c
123
# 这里的反撇号等效于$(),都是用于执行 Shell 命令。
# 单引号和双引号
[root@redis ~]# d=1
[root@redis ~]# d="1 2 $d"
[root@redis ~]# echo $d
1 2 1
[root@redis ~]# c=1
[root@redis ~]# c='1 2 $c'
[root@redis ~]# echo $c
1 2 $c
# 单引号是告诉 Shell 忽略特殊字符,而双引号则解释特殊符号原有的意义,比如$ 、 !。
Shell变量的输入
Shell变量除了可以直接赋值或脚本传参外,还可以使用read命令从标准输入获得,read为bash内置命令,可以通过help read查看帮助
语法格式
# read [参数] [变量名]
常用参数
# -p prompt: 设置提示信息
# -t timeout: 设置输入等待的事件,单位默认为秒
read的基本读入
如果不加-t read就会一直等待
# read后面的参数是一个变量
[root@youmen ~]# read -p 'please input you num:' num
please input you num:234
[root@youmen ~]# echo $num
234
# 设置超时事件为3秒
read -t 3 -p "please input you num:" num
please input you num:
# 过3秒钟会脚本会自己执行结束
read在脚本中常用例子
[root@youmen ~]# sh test.sh
please input you num: 1 2
1-2 =-1
1+2 =3
1*2 =2
1/2 =0
1**2 =1
1%2 =1
[root@youmen ~]# cat abc.sh
#!/bin/bash
read -t 18 -p "please input you num:" a b
echo "$a-$b =$(( $a - $b ))"
echo "$a+$b =$(( $a + $b ))"
echo "$a*$b =$(( $a * $b ))"
echo "$a/$b =$(( $a / $b ))"
echo "$a**$b =$(( $a ** $b ))"
echo "$a%$b =$(( $a % $b ))"
# 利用echo 命令替代和read -p的功能
[root@youmen ~]# cat test.sh
#!/bin/bash
echo -n "请输入两个数字:"
read a b
echo "$a+$b =$(( $a + $b ))"
echo "$a*$b =$(( $a * $b ))"
echo "$a/$b =$(( $a / $b ))"
echo "$a**$b =$(( $a ** $b ))"
echo "$a%$b =$(( $a % $b ))"
[root@youmen ~]# bash test.sh
请输入两个数字:2 3
2+3 =5
2*3 =6
2/3 =0
2**3 =8
2%3 =2
条件测试与比较
介绍
在bash的各种流程控制结构中通常要进行各种测试,然后根据测试结果执行不同的操作,有时也会通过与if等条件语句相结合,更方便的完成判断
条件测试通常由如下3种语法形式
# 语法1:test<测试表达式>
# 语法2:[<测试表达式>]
# 语法3:[[<测试表达式>]]
# 说明
# 1.上述语法格式1和语法格式2的写法是相等的。语法格式3为扩展的test命令。推荐使用语法格式2.
# 2.在[[]]中可以使用通配符进行模式匹配。&&、||、>、<等操作可以应用于[[]]中,但不能应用于[]中.
# 3.对于整数的关系运算,也可以使用Shell的算术运算符(())
test测试表达式
# 判断是不是文件
[root@youmen ~]# test -f /etc/hosts
[root@youmen ~]# echo $?
0
[root@youmen ~]# test -f /etc/hostss
[root@youmen ~]# echo $?
1
# 判断文件是否可以执行
[root@youmen ~]# test -x /usr/bin/ssh
[root@youmen ~]# echo $?
0
[root@youmen ~]# test -x /etc/hosts
[root@youmen ~]# echo $?
1
# 判断是不是目录
[root@youmen ~]# test -d /etc/
[root@youmen ~]# echo $?
0
[root@youmen ~]# test -d /etc/hosts
[root@youmen ~]# echo $?
1
[]中括号表达式
# 判断是不是普通文件
[root@youmen ~]# [ -f /etc/hosts ]
[root@youmen ~]# echo $?
0
[root@youmen ~]# [ -f /etc/hostss ]
[root@youmen ~]# echo $?
1
# 判断是否是目录
[root@youmen ~]# [ -d /etc/hosts ]
[root@youmen ~]# echo $?
1
[root@youmen ~]# [ -d /etc/ ]
[root@youmen ~]# echo $?
0
# 判断是否可被执行
[root@youmen ~]# [ -x /etc/hosts ]
[root@youmen ~]# echo $?
1
[root@youmen ~]# [ -x /usr/bin/ssh ]
[root@youmen ~]# echo $?
0
[[双括号表达式]]
[root@youmen ~]# [[ -x /etc/hosts ]]
[root@youmen ~]# echo $?
1
[root@youmen ~]# [[ -x /usr/bin/ssh ]]
[root@youmen ~]# echo $?
0
# [[]] 和[]一样
# 区别是可以在多括号里面添加多个判断
# 例如判断是不是目录,并判断下一个文件是不是可执行
[root@youmen ~]# [[ -d /etc/ && -x /usr/bin/ssh ]]
[root@youmen ~]# echo $?
0
[root@youmen ~]# [[ -d /etc/ && -x /usr/bin/sshdd ]]
[root@youmen ~]# echo $?
1
# &&只在双括号里面有效,如果单括号里面需要使用-a,-o
文件测试表达式
操作符 说明 举例
# -b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
# -c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
# -d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
# -f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
# -g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
# -k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
# -p file 检测文件是否是具名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
# -u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
# -r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
# -w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
# -x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
# -s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
# -e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。
# 特别说明:这些操作符号对于[[]]、[]、test几乎都是通用的,
字符串表达式
字符串测试操作符的作用有:比较两个字符串是否相同、字符串的长度是否为零,字符串是否为NULL(注:bash区分零长度字符串和空字符串)等
常用字符串测试操作符 说明 -z “字符串” 若串长度为0则真,-z可以理解为zero -n ”字符串“ 若昂度不为0则真,-n 可以理解为no zero ”串1“ = ”串2“
若串1等于串2则真,可以使用”==“代替”=“
“串2” != “串2”
若串1不等于串2则真,不能用“!==“ 代替”!=“ 特别注意:
# 1. 以上表格中的字符串测试操作符号务必要用”“引起来。
# 2.比较符号两端有空格
字符串测试操作符提示
# 1)-n 比较字符串长度是否不为零,如果不为零则为真,如:[ -n “$myvar” ]
# 2)-z 比较字符串长度是否等于零,如果等于零则为真,如:[ -z “$myvar” ]
# 特别注意
# 对于以上表格中的字符串测试操作符号,如[ -n “$myvar” ],要把字符串用“”引起来。
# 1、字符串或字符串变量比较都要加双引号之后再比较。
# 2、字符串或字符串变量比较,比较符号两端最好都有空格,可以参考系统脚本
# “=”比较两个字符串是否相同,与“==”等价,如[ “$a” = “$b” ]其中$a这样的变量最好用“”括起来,因为如果中间由空格,*等符号就可能出错,更好的办法就是[ “${a}” = “${b}” ]
# “!=” 比较两个字符串是否相同,不同则为“是”
逻辑操作符
在[]和test中使用 在[[]]中使用 说明 -a && and与,两端都为真,则真 -o || or或,两端有一个为真则真 ! ! not非,相反则为真
# !中文意思是反:与一个逻辑值相关的逻辑值
# -a 中文意思是(and|&&):两个逻辑值都为“真”,返回值才为“真”,反之为“假”
# -o 中文意思是或(or| ||):两个逻辑值只要有一个为“真”,返回值就为“真”
# 逻辑操作运算规则
# -a和&& 的运算规则:只有两端都是1才为真
# 要想使用&&注意双括号
Shell流程控制
If
if 语句语法格式
if condition
then
command1
command2
...
commandN
fi
# 写成一行
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
If else
if else语法格式
if condition
then
command1
command2
...
commandN
else
command
fi
if else-if else
if else-if else语法格式
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
实例
a=10
b=20
if [ $a == $b ]
then
echo "a 等于 b"
elif [ $a -gt $b ]
then
echo "a 大于 b"
elif [ $a -lt $b ]
then
echo "a 小于 b"
else
echo "没有符合的条件"
fi
# a 小于 b
if else语句经常与test命令结合使用,如下所示
num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
echo '两个数字相等!'
else
echo '两个数字不相等!'
fi
# 输出结果
# 两个数字相等!
循环
for
语法格式
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
# 写成一行
for var in item1 item2 ... itemN; do command1; command2… done;
# 当变量值在列表里,for循环即执行一次所有命令,使用变量名获取列表中的当前取值。
# 命令可为任何有效的shell命令和语句。in列表可以包含替换、字符串和文件名。
# in列表是可选的,如果不用它,for循环使用命令行的位置参数。
# 顺序输出当前列表中的数字
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
# 输出
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
# 顺序输出字符串中的字符
for str in 'This is a string'
do
echo $str
done
# 输出结果
This is a string
多种语法
# 类c语言
for ((i=1; i<=100; i ++))
do
echo $i
done
# in使用
for i in {1..100}
do
echo $i
done
# seq使用
for i in `seq 1 100`
do
echo $i
don
While
while循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:
while condition
do
command
done
一下是一个基本的while循环,测试条件是,如果int小于等于5,那么条件返回真。int从0开始,每次循环处理时,int加1。运行上述脚本,返回数字1到5,然后终止。
#!/bin/bash
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done
# 输出
1
2
3
4
5
# 以上实例使用了 Bash let 命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量
# while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按<Ctrl-D>结束循环
echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
echo "是的!$FILM 是一个好网站"
done
# 运行脚本,输出类似下面
按下 <CTRL-D> 退出
输入你最喜欢的网站名: youmeblog
是的!youmenblog 是一个好博客
无限循环
无限循环语法格式
while :
do
command
done
# or
while true
do
command
done
# or
for (( ; ;))
until循环
until 循环执行一系列命令直至条件为 true 时停止。
until 循环与 while 循环在处理方式上刚好相反。
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
until 语法格式
until condition
do
command
done
condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
以下实例我们使用 until 命令来输出 0 ~ 9 的数字:
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done
# 运行结果
0
1
2
3
4
5
6
7
8
9
case
Shell case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。case语句格式如下:
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
case工作方式如上所示。取值后面必须为单词in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
下面的脚本提示输入1到4,与每一种模式进行匹配:
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac
# 输入不同的内容,会有不同的结果,例如:
输入 1 到 4 之间的数字:
你输入的数字为:
3
你选择了 3
break
break命令允许跳出所有循环(终止执行后面的所有循环)
下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done
# 执行以上代码,输出结果为
输入 1 到 5 之间的数字:3
你输入的数字为 3!
输入 1 到 5 之间的数字:7
你输入的数字不是 1 到 5 之间的! 游戏结束
continue
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。对上面的例子进行修改:
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字: "
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的!"
continue
echo "游戏结束"
;;
esac
done
Case..esac
case ... esac 与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。
case ... esac 语法格式如下
case 值 in
模式1)
command1
command2
command3
;;
模式2)
command1
command2
command3
;;
*)
command1
command2
command3
;;
esac
# case后为取值,值可以为变量或常数
# 值后为关键字 in,接下来是匹配的各种模式,每一模式最后必须以右括号结束,模式支持正则表达式。
#!/bin/sh
site="runoob"
case "$site" in
"runoob") echo "youmen博客"
;;
"google") echo "Google 搜索"
;;
"taobao") echo "淘宝网"
;;
esac