条件判断

if 是最基本的条件判断关键字,其用法如下:

if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi

其中, test commands 命令可以被替换为 [ commands ]

> [ 2 -eq 2 ]; echo $?
0  # 返回 0 表示为真
> [ 2 -eq 1 ]; echo $?
1

> test 2 -gt 2; echo $?
1
> test 2 -gt 1; echo $?
0

# 当然也可以用 [[ expr ]]
# 这种用法支持更多的符号
> [[ 2 < 2 ]]; echo $?  # test 和 [] 中只能用 -lt 表示小于
1
> [[ 2 = 2 ]]; echo $?  # 等价于 ==, 判断是否相等
0

[info] 提示: test , /usr/bin/test , [ , 和 /usr/bin/[ 都是等价命令, ] 只是该命令的一个参数.

条件判断一般被用于判断文件的属性与状态、当前 shell 中是否开启了某个选项、比较字符串、比较参数,其用法如下表所示:

表达式 含义
[ -a FILE ] 判断文件是否存在
[ -b FILE ] 判断文件是否为设备文件
[ -c FILE ] 判断文件是否为字符设备文件
[ -d FILE ] 判断路径是否存在
[ -e FILE ] 判断文件是否存在,必须为常规文件
[ -r FILE ] 判断当前用户对该文件是否可读
[ -x FILE ] 判断当前用户对该文件是否可执行
[ -w FILE ] 判断当前用户对该文件是否可写
[ -L FILE ] 判断当前文件是否为符号链接
[ -p FILE ] 判断当前用户对该文件是 FIFO
[ -S FILE ] 判断当前用户对该文件是 SOCKET
[ FILE1 -nt FILE2 ] True ifFILE1has been changed more recently thanFILE2, or ifFILE1exists andFILE2does not.
[ FILE1 -ot FILE2 ] True ifFILE1is older thanFILE2, or isFILE2exists andFILE1does not.
[ FILE1 -ef FILE2 ] True ifFILE1andFILE2refer to the same device and inode numbers.
[ -o OPTIONNAME ] 如果 shell 中开启了 OPTIONNAME 选项则返回真
[ -z STRING ] 字符串为空或长度为 0 则返回真
[ -n STRING ] or [ STRING ] 字符串非空则返回真
[ STRING1 OP STRING2 ] “OP”用于字典顺序比较字符串,包括 ==!=>=<=
[ ARG1 OP ARG2 ] "OP"用于整数比较,包括 -gt-ge-lt-le-eq-ne
[ ! EXPR ] EXPR的值取反.
[ ( EXPR ) ] 判断EXPR的返回值.
[ EXPR1 -a EXPR2 ] 等价于 [[ EXPR1 && EXPR2 ]]
[ EXPR1 -o EXPR2 ] 等价于 `[[ EXPR EXPR ]]`

应用案例

  1. 判断文件是否存在:
> cat 7.sh
#!/usr/bin/bash
echo "This scripts checks the existence of the messages file."
echo "Checking..."
if [ -f $1 ]
  then
    echo "$1 is an exists regular file."
fi
# 这里先不用 elif
if [ -a $1 ]
  then
    echo "$1 is an exists file."
fi
echo "...done."

> bash 7.sh ~/t1.awk
This scripts checks the existence of the messages file.
Checking...
/home/remilia/t1.awk is an exists regular file.
/home/remilia/t1.awk is an exists file.
...done.

> bash 7.sh ~/..  # 指向父目录的特殊文件
This scripts checks the existence of the messages file.
Checking...
/home/remilia/.. is an exists file.
...done.
  1. 判断 shell 是否启用了重定向:
if [ -o noclobber ]
  then
    echo "Your files are protected against accidental overwriting using redirection."
fi
# 注意考虑在 child shell 中不能继承父 shell 的环境变量。
# 若果你父 shell 中设置了停用重定向
# 然后通过 bash ./script 的方式运行上述脚本
# 可能得不到你期望的结果
  1. 判断上一条命令的返回状态:
# 可以拆开写
> grep $USER /etc/passwd
remilia:x:1000:1000:Remilia Scarlet,,,:/home/remilia:/bin/bash
> if [ $? -ne 0 ]; then echo "not a local account"; fi

# 也可以直接写在 if 后面
> if ! grep $USER /etc/passwd > /dev/null; then echo "not a local account"; fi
  1. 数值比较
> num= `wc -l work.txt` ; echo $num
201
> if [ "$num" -gt "15" ]; then echo ; echo "you've worked hard enough for today."; echo ; fi

you've worked hard enough for today.
  1. 字符串比较
# 可以直接使用比较符
> if [ `whoami` != root ]; then echo "how dare you?"; fi
how dare you?

# 支持 shell 元字符
> if [[ $gender == f* ]]; then echo "Pleasure to meet you, Madame."; fi
Pleasure to meet you, Madame.

更复杂的条件判断需要使用 if/elif/else 结构,其用法如下:

if TEST-COMMANDS; then

CONSEQUENT-COMMANDS;

elif MORE-TEST-COMMANDS; then

MORE-CONSEQUENT-COMMANDS;

else ALTERNATE-CONSEQUENT-COMMANDS;

fi

更复杂的应用案例

  1. 判断体重是否合适:
if [ ! $# == 2 ]; then
  echo "Usage: $0 weight_in_kilos length_in_centimeters"
  exit
fi

weight="$1"
height="$2"
idealweight=$[$height - 110]

if [ $weight -le $idealweight ] ; then
  echo "You should eat a bit more fat."
else
  echo "You should eat a bit more fruit."
fi
  1. 查看文件的信息:
#!/bin/bash

FILENAME="$1"

echo "Properties for $FILENAME:"

if [ -f $FILENAME ]; then
  echo "Size is $(ls -lh $FILENAME | awk '{ print $5 }')"
  echo "Type is $(file $FILENAME | cut -d":" -f2 -)"
  echo "Inode number is $(ls -i $FILENAME | cut -d" " -f1 -)"
  echo "$(df -h $FILENAME | grep -v Mounted | awk '{ print "On",$1", \
which is mounted as the",$6,"partition."}')"
else
  echo "File does not exist."
fi

> bash filepro.sh abc
Properties for abc:
Size is 31
Type is ASCII text
Inode number is 3487604
On /dev/mapper/vgkubuntu-root which is mounted as the / partition.
  1. 判断今年是否为闰年:
#!/usr/bin/bash

year= `date +%Y` 
echo `date` 

if [ $[ $year % 400 ] -eq 0 ]; then
    echo "This is a leap year. February has 29 days."
    exit
elif [ $[ $year % 4 ] -eq 0 -a $[ $year % 100 ] -ne 0 ]; then
    echo "This is a leap year. February has 29 days."
else
    echo "This is not a leap year."
fi
  1. 检查路径是否存在,不存在则创建:
> dir=~/new_dir
> if ! test -d $dir; then mkdir -p $dir; fi

# 也可以用条件运算符
> [ ! -d ~/ccc ] && mkdir /ccc
> [ -d ~/ccc ] || mkdir /ccc
  1. 内存空间报警:
#!/usr/bin/bash

mem_used= `free -m | grep '^Mem:' | awk '{ print $3 }'` 
mem_total= `free -m | grep '^Mem:' | awk '{ print $2 }'` 
mem_percent=$[ mem_used * 100 / mem_total ]
war_file=/tmp/mem_war.txt

if [ $mem_percent -ge $1 ]; then
    echo " `date +%F-%H` memory:${mem_percent}%" > $war_file
fi

if [ -f $war_file ]; then
    cat $war_file
fi
  1. 批量创建用户并初始化密码:
#!/usr/bin/bash

read -p "Please input number: " num
if [[ ! "$num" =~ ^[1-9][0-9]*$ ]]; then
    exit
fi

read -p "Please input prefix: " prefix
if [ -z $prefix ]; then
    exit
fi

for i in `seq $num` 
do
    username=$prefix$i
    # echo $username
    useradd username
    echo "123" | passwd $username
    if [ $? -eq 0 ]; then
        echo "$username has been created."
    fi
done
  1. 磁盘空间报警:
#!/bin/bash

space= `df -h | awk '{print $5}' | grep % | grep -v Use | sort -n | tail -1 | cut -d "%" -f1 -` 
alertvalue="80"

if [ "$space" -ge "$alertvalue" ]; then
  echo "At least one of my disks is nearly full!" | mail -s "daily diskcheck" root
else
  echo "Disk space normal" | mail -s "daily diskcheck" root
fi
  1. case 实现磁盘空间报警:
#!/bin/bash

space= `df -h | awk '{print $5}' | grep % | grep -v Use | sort -n | tail -1 | cut -d "%" -f1 -` 

case $space in
[1-6]*)  # 匹配条件可以用正则
  Message="All is quiet."
  ;;
[7-8]*)
  Message="Start thinking about cleaning out some stuff.  There's a partition that is $space % full."
  ;;
9[1-8])
  Message="Better hurry with that new disk...  One partition is $space % full."
  ;;
99)
  Message="I'm drowning here!  There's a partition at $space %!"
  ;;
*)
  Message="I seem to be running with an nonexistent amount of disk space..."
  ;;
esac

echo $Message | mail -s "disk report `date` " anny
  1. case 实现服务控制:
case "$1" in
        start)
            start
            ;;

        stop)
            stop
            ;;

        status)
            status anacron
            ;;
        restart)
            stop
            start
            ;;
        condrestart)
            if test "x `pidof anacron` " != x; then
                stop
                start
            fi
            ;;

        *)
            echo $"Usage: $0 {start|stop|restart|condrestart|status}"
            exit 1

esac
  1. 执行指定的程序,不存在则安装:
#!/usr/bin/bash
command1=nginx

if command -v command1 &>/dev/null; then
    :  # 等价于 true
else
    sudo apt install command1
fi
  1. 工具箱:
#!/usr/bin/bash

menu () {
    clear
    cat <<- EOF
##################################
#       h. help                  #
#       f. disk partition        #
#       d. filesystem mount      #
#       m. memory                #
#       u. system load           #
#       q. exxit                 #
##################################
EOF
}

trap "" HUB INT

menu

while true
do
    read -p "Switch Case:" action
    case "$action" in
    h)
        menu
        ;;
    f) fdisk -l ;;  # 也可以写在一行
    d) df -Th ;;
    m) free -m ;;
    u) uptime ;;
    q)
        # exit
        break
        ;;
    "") continue ;;
    *) echo error ;;
    esac
done

循环控制

for 循环

语法:

for NAME [in LIST ]; do COMMANDS; done

如果省略 [in LIST ] 则用 $@ (参数列表)替换这部分, 就相当于是在遍历参数. for 循环可以写在一行, 也可以写成多行:

# 查询 /sbin 路径下的所有纯文本文件
> for i in `ls /sbin` ; do file /sbin/$i | grep ASCII; done

# 删除 html 文件的前 25 行和后 21 行并转换为 php
LIST="$(ls *.html)"
for i in "$LIST"; do
     NEWNAME=$(ls "$i" | sed -e 's/html/php/')
     cat beginfile > "$NEWNAME"
     cat "$i" | sed -e '1,25d' | tac | sed -e '1,21d'| tac >> "$NEWNAME"
     cat endfile >> "$NEWNAME"
done
# tac 命令用于翻转文件的行

# 类似 C 语言风格的写法
for((i=1;i<=5;i++));do
    echo "这是第 $i 次调用";
done;

while 循环

语法:

while CONTROL-COMMAND; do CONSEQUENT-COMMANDS; done

只要满足条件就会重复执行命令, 例如这个计算平均数脚本:

#!/bin/bash

SCORE="0"
AVERAGE="0"
SUM="0"
NUM="0"

while true; do

  echo -n "Enter your score [0-100%] ('q' for quit): "; read SCORE;

  if (("$SCORE" < "0"))  || (("$SCORE" > "100")); then
    echo "Be serious.  Common, try again: "
  elif [ "$SCORE" == "q" ]; then
    echo "Average rating: $AVERAGE%."
    break
  else
    SUM=$[$SUM + $SCORE]
    NUM=$[$NUM + 1]
    AVERAGE=$[$SUM / $NUM]
  fi

done

echo "Exiting."

while 可以配合重定向和 read 一起使用, 因此 while 更适合处理文件:

#!/usr/bin/bash

echo "UserName" "Password"
while read line
do
    if [ -z "$line" ]; then
#         echo "PASS"
        continue
    fi
    USERNAME= `echo $line | awk '{ print $1 }'` 
    PASSWORD= `echo $line | awk '{ print $2 }'` 
    echo -e "$USERNAME \t $PASSWORD"
done < data.txt

until 循环

语法:

until TEST-COMMAND; do CONSEQUENT-COMMANDS; done

while 循环相反, until 循环在满足条件后就跳出, 不满足条件则重复执行命令.

break 与 continue

break N 可以指定要跳出几层, continue 用法与 C 语言中完全相同, 例如:

# 把文件名中的大写字母全换成小写
#!/bin/bash

LIST="$(ls)"

for name in "$LIST"; do
    if [[ "$name" != *[[:upper:]]* ]]; then
        continue
    fi

    ORIG="$name"
    NEW= `echo $name | tr 'A-Z' 'a-z'` 

    mv "$ORIG" "$NEW"
    echo "new name for $ORIG is $NEW"
done

用 select 构建菜单选项

语法:

select WORD [in LIST]; do RESPECTIVE-COMMANDS; done

看起来非常像 for 的语法, select 默认使用 PS3 变量作为提示符, 在循环体中可以通过 $REPLY 获得你选择的序号:


#!/bin/bash

echo "This script can make any of the files in this directory private."
echo "Enter the number of the file you want to protect:"

PS3="Your choice: "
QUIT="QUIT THIS PROGRAM - I feel safe now."
touch "$QUIT"

select FILENAME in *;
do
  case $FILENAME in
        "$QUIT")
          echo "Exiting."
          break
          ;;
        *)
          echo "You picked $FILENAME ($REPLY)"
          chmod go-rwx "$FILENAME"
          ;;
  esac
done
rm "$QUIT"

用 shift 进行参数偏移

用法: shift N , 表示偏移 N 个参数, 前 N 个参数会被丢弃, 但要注意如果参数不足会发生死循环.

#!/bin/bash
if [ $# -lt 1 ]; then
        echo "Usage: $0 package(s)"
        exit 1
fi
while (($#)); do
    echo $1
    shift
done

对比 -exec 与 xargs

results matching ""

    No results matching ""