if 是条件语句,elif 是条件分支,else 是不成立的情况;then 是执行语句
if commands; then
commands
[elif commands; then
commands...]
[else
commands]
fi
# 如果 then 不和 if 写在同一行,可以不用 ;
if true
then
echo 'hello world'
fi
if 后可以跟命令,如果命令成功(返回
0
),则执行 then 中的语句
if echo 'hi'; then echo 'hello world'; fi
# hi
# hello world
if 后面也可以跟任意多的命令,所有命令都会执行,但是只有最后一个命令用于判断真伪(如果返回 0,就会执行 then 之后的内容)
if false; true; then echo 'hello world'; fi
#hello world
if 结构的判断条件一般使用 test 命令,test命令执行成功返回 0,失败返回 1
# one
test expressions
# two
[ exporessions ]
# three(只有此写法支持正则)
[[ expressions ]]
- 注意
⚠️ :建议使用[[]]
,这允许更复杂的表达式
[[ -b file ]]
:如果 file 存在并且是一个块(设备)文件,为 true。[[ -c file ]]
:如果 file 存在并且是一个字符(设备)文件,为 true。[[ -d file ]]
:如果 file 存在并且是一个目录,为 true。[[ -e file ]]
:如果 file 存在,为 true。(不建议使用-a
[[ -f file ]]
:如果 file 存在并且是一个普通文件,为 true。[[ -g file ]]
:如果 file 存在并且设置了组 ID,为 true。[[ -G file ]]
:如果 file 存在并且属于有效的组 ID,为 true。[[ -h file ]]
:如果 file 存在并且是符号链接,为 true。[[ -k file ]]
:如果 file 存在并且设置了它的“sticky bit”,为 true。[[ -L file ]]
:如果 file 存在并且是一个符号链接,为 true。[[ -N file ]]
:如果 file 存在并且自上次读取后已被修改,为 true。[[ -O file ]]
:如果 file 存在并且属于有效的用户 ID,为 true。[[ -p file ]]
:如果 file 存在并且是一个命名管道,为 true。[[ -r file ]]
:如果 file 存在并且可读(当前用户有可读权限),为 true。[[ -s file ]]
:如果 file 存在且其长度大于零,为 true。[[ -S file ]]
:如果 file 存在且是一个网络 socket,为 true。[[ -t fd ]]
:如果 fd 是一个文件描述符,并且重定向到终端,为 true。这可以用来判断是否重定向了标准输入/输出/错误。[[ -u file ]]
:如果 file 存在并且设置了 setuid 位,为 true。[[ -w file ]]
:如果 file 存在并且可写(当前用户拥有可写权限),为 true。[[ -x file ]]
:如果 file 存在并且可执行(有效用户有执行/搜索权限),为 true。[[ FILE1 -nt FILE2 ]]
:如果 FILE1 比 FILE2 的更新时间更近,或者 FILE1 存在而 FILE2 不存在,为 true。[[ FILE1 -ot FILE2 ]]
:如果 FILE1 比 FILE2 的更新时间更旧,或者 FILE2 存在而 FILE1 不存在,为 true。[[ FILE1 -ef FILE2 ]]
:如果 FILE1 和 FILE2 引用相同的设备和 inode 编号,为 true。
[ string ]
:如果string不为空(长度大于0),为 true。[ -n string ]
:如果字符串string的长度大于零,为 true。[ -z string ]
:如果字符串string的长度为零,为 true。[ string1 = string2 ]
:如果string1和string2相同,为 true。[ string1 == string2 ]
: 等同于[ string1 = string2 ]。[ string1 != string2 ]
:如果string1和string2不相同,为 true。[ string1 '>' string2 ]
:如果按照字典顺序string1排列在string2之后,为 true[ string1 '<' string2 ]
:如果按照字典顺序string1排列在string2之前,为 true
注意:如果是 test 命令内部的 <
或 >
,则需要使用 ''
包裹或 \
转义
[ integer1 -eq integer2 ]
:如果相等,则为 true。[ integer1 -ne integer2 ]
:如果不等,则为 true。[ integer1 -le integer2 ]
:如果integer1 <= integer2,则为 true。[ integer1 -lt integer2 ]
:如果integer1 < integer2,则为 true。[ integer1 -ge integer2 ]
:如果integer1 >= integer2,则为 true。[ integer1 -gt integer2 ]
:如果integer1 > integer2,则为 true。
[[ string1 =~ regex ]]
#!/bin/bash
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
echo "INT is an integer."
exit 0
else
echo "INT is not an integer." >&2
exit 1
fi
&&
: 也可使用参数 -a
。
||
: 也可使用参数 -o
。
!
: 取反。
注意:使用 !
最好使用 ()
if [[ ! \( $INT -ge $MIN_VAL -a $INT -le $MAX_VAL \) ]]; then
echo "$INT is outside $MIN_VAL to $MAX_VAL."
else
echo "$INT is in range."
fi
利用逻辑运算搭配普通的命令,进行运算
# 一般情况,只有第一个成功后才可以进入第二个命令
mkdir temp && cd temp
# 使用 test 命令
[ -d temp ] || mkdir temp
# 如果 temp 命令不存在,则终止
[ ! -d temp ] && exit 1
使用
(())
作为算术条件进行判断。算术判断不需要使用test
命令,这个结构由返回值决定
- 在算术表达式中,true 代表 1(成功条件,执行 then),false 代表 0
if ((3 > 2)); then
echo "true"
fi
# 但是这种不属于算术判断
if [[ 3 -gt 2 ]]; then
echo "true"
fi
- 也可以使用变量赋值,下面的条件会先赋值,再判断;如果单独运行
(( foo = 5 ))
,那么会返回 5
if (( foo = 5 ));then echo "foo is $foo"; fi
#foo is 5
如果条件判断中,有多个 if 或者 elif,可以使用 case
case expression in
pattern )
commands ;;
pattern )
commands ;;
...
esac
case 匹配中可以使用任意通配符
a)
:匹配a。a|b)
:匹配a或b。[[:alpha:]])
:匹配单个字母。???)
:匹配3个字符的单词。*.txt)
:匹配.txt结尾。*)
:匹配任意输入,通过作为case结构的最后一个模式
#!/bin/bash
echo -n "输入一个字母或数字 > "
read character
case $character in
[[:lower:]] | [[:upper:]] ) echo "输入了字母 $character"
;;
[0-9] ) echo "输入了数字 $character"
;;
* ) echo "输入不符合要求"
esac
Bash 4.0之后,允许匹配多个条件,这时可以用
;;&
终止每个条件块
#!/bin/bash
# test.sh
read -n 1 -p "Type a character > "
echo
case $REPLY in
[[:upper:]]) echo "'$REPLY' is upper case." ;;&
[[:lower:]]) echo "'$REPLY' is lower case." ;;&
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
[[:digit:]]) echo "'$REPLY' is a digit." ;;&
[[:graph:]]) echo "'$REPLY' is a visible character." ;;&
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
esac
while condition; do
commands
done
- while 循环中也可以使用
test
命令
number=0
while [ "$number" -lt 10 ]; do
echo "Number = $number"
number=$((number + 1))
done
until 循环与 while 循环相反,只要不符合条件,就继续执行括号中的内容
until condition
do
commands
done
until false; do echo 'Hi, until looping ...'; done
for...in 循环用于遍历列表的每一项
for variable in list
do
commands
done
- 列表可以由通配符产生,也可以由子命令产生
for i in *.png; do
ls -l $i
done
####
count=0
for i in $(cat ~/.bash_profile); do
count=$((count + 1))
echo "Word $count ($i) contains $(echo -n $i | wc -c) characters"
done
in list
的部分可以省略,这时list
默认等于脚本的所有参数$@
for filename; do
echo "$filename"
done
# 等同于
for filename in "$@" ; do
echo "$filename"
done
for (( expression1; expression2; expression3 )); do
commands
done
for (( i=0; i<5; i=i+1 )); do
echo $i
done
Bash 提供了两个内部命令 break
和 continue
,用来在循环内部跳出循环。
select 结构主要用来生成简单的菜单。它的语法与 for...in
循环基本一致。
select name
[in list]
do
commands
done
Bash 会对select依次进行下面的处理。
select 生成一个菜单,内容是列表
list
的每一项,并且每一项前面还有一个数字编号。
- Bash 提示用户选择一项,输入它的编号。
- 用户输入以后,Bash 会将该项的内容存在变量name,该项的编号存入环境变量REPLY。如果用户没有输入,就按回车键,Bash 会重新输出菜单,让用户选择。
- 执行命令体commands。
- 执行结束后,回到第一步,重复这个过程。
#!/bin/bash
# select.sh
select brand in Samsung Sony iphone symphony Walton
do
echo "You have chosen $brand"
done
执行上面的脚本,Bash 会输出一个品牌的列表,让用户选择。
$ ./select.sh
# 1) Samsung
# 2) Sony
# 3) iphone
# 4) symphony
# 5) Walton
#?
如果用户没有输入编号,直接按回车键。Bash 就会重新输出一遍这个菜单,直到用户按下Ctrl + c,退出执行。
select
可以与case
结合,针对不同项,执行不同的命令。
#!/bin/bash
echo "Which Operating System do you like?"
select os in Ubuntu LinuxMint Windows8 Windows10 WindowsXP
do
case $os in
"Ubuntu"|"LinuxMint")
echo "I also use $os."
;;
"Windows8" | "Windows10" | "WindowsXP")
echo "Why don't you try Linux?"
;;
*)
echo "Invalid entry."
break
;;
esac
done
上面例子中,case针对用户选择的不同项,执行不同的命令。