Linux Shell 脚本编程(4)—控制流结构

本文介绍了Shell脚本中的控制流结构,包括条件测试、循环控制、分支选择等关键概念,并通过具体实例展示了if-then-else语句、case语句、until及while循环的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

shell控制流结构


控制结构    
  if then else语句    
  case语句    
  until循环

  while循环   
  break控制   
  confinue控制

一、 流控制的目的

#!/bin/bash
#创建一个目录
make /home/jianliu/shelltest/txt
#复制所有txt文件到指定目录
cp *.txt /home/jianliu/shelltest/txt
rm -f *.txt

1.该脚本运行存在问题:很可能创建目录失败,后续操作无法执行。
2. 若目录创建失败,该如何处理;成功又该如何处理;
3. 文件拷贝失败如何处理
—-》因此引用流控制结构来控制脚本的有效运行。

1. 条件测试(判断)—test

  • 有时判断字符串是否相等或检查文件状态或是数字测试等。Test命令用于测试字符串,文件状态和数字。

  • Test 判断的结果如何正确的判断?不要直接用直觉,注意test后面的表达式是一个整体,注意这个整体的结果是成功还是失败,成功返回0,失败返回1

  • 【1】 文件状态测试

===== 格式(两种写法)

格式注意事项
test condition
[ condition ]使用方括号时,要注意在条件两边加上空格!!!
格式(两种写法)   
     test condition    
     [    condition   ]  #使用方括号时,要注意在条件两边加上空格。
         eg: [ -x file ] (正确)    [-x file](错误!!)

文件测试状态 :测试的结果可以用$?的值来判断,0表示成功,其他为失败

-d       目录                -s         文件长度大于0、非空
-f       正规文件            -w         可写
-L       符号链接            -u         文件有suid位设置
-r       可读                -x         可执行
-z        字符串为空          -e         文件是否存在

—–实例:

jianliu@ubuntu:~/aa$ ls -a
.  ..  2.c  a1.c  app2  b1.c  b3.c  b4.c  bb  hello.c  shell02.sh

jianliu@ubuntu:~/aa$ ls -a -ls
total 36
 4 drwxrwxr-x  3 jianliu jianliu 4096 Oct  7 20:07 .
 4 drwxr-xr-x 24 jianliu jianliu 4096 Oct  7 19:51 ..
 0 lrwxrwxrwx  1 jianliu jianliu    3 Oct  7 20:07 2.c -> 1.c
 4 -rw-rw-r--  1 jianliu jianliu   79 Oct  3 05:05 a1.c
12 -rwxrwxr-x  1 jianliu jianliu 8501 Oct  6 19:26 app2
 0 lrwxrwxrwx  1 jianliu jianliu    4 Oct  7 20:07 b3.c -> b1.c
 0 lrwxrwxrwx  1 jianliu jianliu    4 Oct  7 20:07 b4.c -> b1.c
 4 drwxrwxr-x  3 jianliu jianliu 4096 Oct  3 06:44 bb
 4 -rw-rw-r--  1 jianliu jianliu   94 Oct  4 04:20 hello.c
 4 -rwxr-xr-x  1 jianliu jianliu  109 Oct  6 19:15 shell02.sh

jianliu@ubuntu:~/aa$ test -d bb
jianliu@ubuntu:~/aa$ echo $?
0    #bb为目录文件

jianliu@ubuntu:~/aa$ test -L 2.c
jianliu@ubuntu:~/aa$ echo $?
0   #2.c为link文件

jianliu@ubuntu:~/aa$ test -x shell02.sh
jianliu@ubuntu:~/aa$ echo $?
0  #shell02.sh为可执行文件

jianliu@ubuntu:~/aa$ [ -x shell02.sh ]
jianliu@ubuntu:~/aa$ echo $?
0
#脚本文件  test.sh  测试文件($1所指向的文件)是否可写。
#!/bin/bash
echo "test use 1"
test -w $1
if [ $? -eq "0" ]; then
        echo "success ";
else
        echo "error: not success";
fi
echo "return value:" $?
echo "test use 2 : using [] -- begin"
[ -w $1 ]
echo  "return value:" $?


#测试---------------------------------------------
#新建一个自读文件 :rd_file
-r--r--r--  1 jianliu jianliu    0 Oct  7 20:25 rd_file
#新建一个可写文件:hello.c
4 -rw-rw-r--  1 jianliu jianliu   94 Oct  4 04:20 hello.c

jianliu@ubuntu:~/aa$ ./test.sh rd_file
test use 1
error: not success
return value: 0
test use 2 : using [] -- begin
return value: 1

jianliu@ubuntu:~/aa$ ./test.sh hello.c
test use 1
success 
return value: 0
test use 2 : using [] -- begin
return value: 0

—–测试时使用逻辑操作符

操作符作用说明
-a逻辑与(and)操作符两边均为真,结果为真,否则为假。
-o逻辑或(or)操作符两边一边为真,结果为真,否则为假。
!逻辑否条件为假,结果为真。
#测试实例: 
   #逻辑与操作: 测试文件a1.c 与b1.c是否均可读
   jianliu@ubuntu:~/aa$ [ -w b1.c -a -w a1.c ]
   jianliu@ubuntu:~/aa$ echo $?
   0

   #逻辑或操作 :  测试test.sh 是否可执行 或者 a1.c是否可写
    jianliu@ubuntu:~/aa$ [ -x test.sh -o -w a1.c ]
    jianliu@ubuntu:~/aa$ echo $?
    0
  • 【2】 字符状态测试

    测试两个字符是否相等。退出状态变量 $?,0表示成功,1表示失败。

    #格式-----------------------: 1) test string2) test [operator] string3) test string_1 [operator] string_2
     (4) [ [operator] string ]
     (5) [ string_1 [operator] string_2 ]

—- 操作数 operator 说明

操作数作用说明
=两个字符串相等
!=两个字符串不等
-z空串
-n非空串

*注意!!! =,!=都是针对字符串操作,而不是数值*


—–测试实例

jianliu@ubuntu:~/aa$  test "string"
jianliu@ubuntu:~/aa$ echo $?
0

jianliu@ubuntu:~/aa$ test -n "string"
jianliu@ubuntu:~/aa$ echo $?
0

jianliu@ubuntu:~/aa$ test  "string1" = "string2"
jianliu@ubuntu:~/aa$ echo $?
1

jianliu@ubuntu:~/aa$ [ -n "string" ]
jianliu@ubuntu:~/aa$ echo $?
0

jianliu@ubuntu:~/aa$ [ "string1" = "string2" ]
jianliu@ubuntu:~/aa$ echo $?
1

jianliu@ubuntu:~/aa$ declare AAA="aaa"
jianliu@ubuntu:~/aa$ declare BBB="bbb"
jianliu@ubuntu:~/aa$ [ $AAA = $BBB ]
jianliu@ubuntu:~/aa$ echo $?
1

jianliu@ubuntu:~/aa$ [ 1 = 1 ]    #看做字符
jianliu@ubuntu:~/aa$ echo $?
0

  • 【3】 数值测试
----格式---------------------
  (1) number1 [operator] number2
  (2) [ number1  [operator] number2 ]

—- 操作数 operator 说明

操作数作用说明
-eq数值相等
-ne数值不相等
-gt第一个数大于第二个数。
-lt第一个数小于第二个数。
-le第一个数小于等于第二个数。
-ge第一个数大于等于第二个数。

—–实例—————-

jianliu@ubuntu:~/aa$ NUM1=12
jianliu@ubuntu:~/aa$ NUM2=34

jianliu@ubuntu:~/aa$ test $NUM1 -ne $NUM2
jianliu@ubuntu:~/aa$ echo $?
0

jianliu@ubuntu:~/aa$ [ $NUM1 -gt $NUM2 ]
jianliu@ubuntu:~/aa$ echo $?
1

jianliu@ubuntu:~/aa$ NUM3=130
jianliu@ubuntu:~/aa$ [ "$NUM3" -eq "130" ]
jianliu@ubuntu:~/aa$ echo $?
0

  • 【4】expr数值运算

    格式: expr argument1 [operator] argument2

    ——-注意!——-
    (1) [operator] 前后都要有空格!!
    (2) 这个表达式是命令!!!!

        #eg------------------------:      
        VAR3=`expr $VAR1  \*  $VAR2`    #正确!!
        VAR3=expr $VAR1  \*  $VAR2      #错误!!

——-(3) 乘法操作: [operator] = \*


==========实例test===================

#加法操作
jianliu@ubuntu:~/aa$ expr 10 + 10
20

#减法操作
jianliu@ubuntu:~/aa$ expr 15 - 10
5

#除法操作
jianliu@ubuntu:~/aa$ expr 30 / 3
10
jianliu@ubuntu:~/aa$ expr 30 /2   #错误!!!
expr: syntax error
jianliu@ubuntu:~/aa$ expr 30/3  
30/3
jianliu@ubuntu:~/aa$ expr 30 / 3 / 2
5

#乘法操作
jianliu@ubuntu:~/aa$ expr 30 \* 3   #\* 转义字符
90
jianliu@ubuntu:~/aa$ expr 30 * 3   #错误
expr: syntax error


#----------------------------------------------------
jianliu@ubuntu:~/aa$ MY_VAR1=21
jianliu@ubuntu:~/aa$ MY_VAR2=34
jianliu@ubuntu:~/aa$ MY_VAR3=`expr $MY_VAR1 + $MY_VAR2`  #反引号!!,命令!!
jianliu@ubuntu:~/aa$ MY_VAR4=`expr $MY_VAR1 \* $MY_VAR2`

jianliu@ubuntu:~/aa$ echo $MY_VAR3
55
jianliu@ubuntu:~/aa$ echo $MY_VAR4
714

  • 【5】 理论提高(man test)
man test

TEST(1)                          User Commands                         TEST(1)

       -n STRING
              the length of STRING is nonzero

       STRING equivalent to -n STRING

       -z STRING
              the length of STRING is zero

       STRING1 = STRING2
              the strings are equal

       STRING1 != STRING2
              the strings are not equal

       INTEGER1 -eq INTEGER2
              INTEGER1 is equal to INTEGER2

       INTEGER1 -ge INTEGER2
              INTEGER1 is greater than or equal to INTEGER2

       INTEGER1 -gt INTEGER2
              INTEGER1 is greater than INTEGER2

       INTEGER1 -le INTEGER2
              INTEGER1 is less than or equal to INTEGER2

       INTEGER1 -lt INTEGER2
              INTEGER1 is less than INTEGER2

       INTEGER1 -ne INTEGER2
              INTEGER1 is not equal to INTEGER2

       FILE1 -ef FILE2
              FILE1 and FILE2 have the same device and inode numbers

       FILE1 -nt FILE2
              FILE1 is newer (modification date) than FILE2

       FILE1 -ot FILE2
              FILE1 is older than FILE2

       -b FILE
              FILE exists and is block special

       -c FILE
              FILE exists and is character special

       -d FILE
              FILE exists and is a directory

       -e FILE
              FILE exists

       -f FILE
              FILE exists and is a regular file

       -g FILE
              FILE exists and is set-group-ID

       -G FILE
              FILE exists and is owned by the effective group ID

       -h FILE
              FILE exists and is a symbolic link (same as -L)

       -k FILE
              FILE exists and has its sticky bit set

       -L FILE
              FILE exists and is a symbolic link (same as -h)

       -O FILE
              FILE exists and is owned by the effective user ID

       -p FILE
              FILE exists and is a named pipe

       -r FILE
              FILE exists and read permission is granted

       -s FILE
              FILE exists and has a size greater than zero

       -S FILE
              FILE exists and is a socket

       -t FD  file descriptor FD is opened on a terminal

       -u FILE
              FILE exists and its set-user-ID bit is set

       -w FILE
              FILE exists and write permission is granted

       -x FILE
              FILE exists and execute (or search) permission is granted

2. if then else fi 语句

  • 语法1
if [ 条件 ]
then 命令
fi  #终止符号
#注意if语句必须以fi终止

#---练习:----------------
#if test
if [ "13" -lt "12" ]  # "13" 前一个空格,“13”后也有一个空格。
then
        echo "yes 13 is less then 12"
else
        echo "NO"
fi
  • 语法2
if [ 条件1 ]
then 
     命令1
elif [ 条件2 ]
then 
     命令2
else
     命令3
fi
  • 综合练习——-
#脚本文件   判断输入字符是否空空串
#!/bin/bash
#if then fi

VAR=$1

if [ -z $VAR ]
then 
    echo "success1"
else
    echo "failure1"
fi

if test -z $VAR   #不建议使用这种格式!!!
then 
    echo "success2"
else
    echo "failure2"
fi

#运行结果
jianliu@ubuntu:~/aa$ ./test.sh 
success1
success2
jianliu@ubuntu:~/aa$ ./test.sh ""
success1
success2
jianliu@ubuntu:~/aa$ ./test.sh "haha"
failure1
failure2
#脚本  判断一个文件是否存在
#!/bin/bash
#if test
#this is a comment line
echo "Enter your filename:"
read myfile
if [ -e $myfile ] #判断文件是否存在
then
   if [ -s $myfile ]  #判断文件内容长度是否>=0
   then   
      echo "$myfile exist and size greater than zero"
   else
      echo "$myfile exist but size is zero"
   fi
else
echo "file no exist"
fi

#运行结果:-----------------------------------
jianliu@ubuntu:~/aa$ ./test.sh 
Enter your filename:
c1.c
file no exist

jianliu@ubuntu:~/aa$ touch efile  #新建一个空文件
jianliu@ubuntu:~/aa$ ./test.sh 
Enter your filename:
efile
efile exist but size is zero

jianliu@ubuntu:~/aa$ ./test.sh 
Enter your filename:
b1.c
b1.c exist and size greater than zero


3. case 多选择语句

—-类似于C语言中switch语句,但是在脚本中case处理的是字符串!!!

case多选择语句格式:

    case 值(变量$VARin         
    模式1 )
        命令1
        ;;         #用两个;;表示结束一个内部case,代表break.
    模式2 )
        命令2
        ;;
    esac

   #等价于switch语句---
   switch(int VAR)  //输入VAR是整型!!
   {
       case var1(对应于模式1) :
           do something;
           break;
       case var2(对应于模式2):
           do something;
           break;
       default :
           break;
   }
  • 1) case取值后面必须为单词 in;每一模式必须以右括号结束

  • 2) 取值可以为变量或常数。匹配发现取值符合某一模式后,其后的所有命令开始执行,直到;;

  • 3) 模式

模式匹配作用说明
*表示任意字符(字符串)
?表示任意单字符
[..]表示类或范围中任意字符

  • 4) 重要说明
   //【1】范围case  -----------------------------                           
   case val in           
   a|b|c)  
       echo "[ ]"
       ;;       

   //--等价于->
   switch(var){
       case a :
       case b :
       case c :
           printf("[ ]");
           break;
   }
//【2】 *来实现default功能  -----------------------------
 case val in           
   。。。。
   *)
       echo "default"
       ;;

   //--等价于->
   switch(var){
      。。。。。
       default :
           printf("default");
           break;
   }

  • case 的要点
    a)  条件处理的是字符串
    b)  每一个分支以“;;”作为结束
    c)  条件分支一esac结束
    d)  通配符*,?,[]   
  • 实例—————-
    通过read从键盘读入一个字符串,如果输入的是数字,如果范围在1~3,进入相应的处理分支,如果输入E或e ,屏幕输出一行:“EXIT!”,如果是其他字符,那么输出:“非法输入+输入的字符串”
#脚本
#!/bin/bash

echo "please input str:\n"
read STR

case $STR in
1|2|3)
    echo "your select: $STR"
    ;;
y|Y)
    echo "you agree"
    ;;
E|e)
    echo "exit!"
    exit
    ;;
*)
    echo "invalid input:$STR"
    ;;
esac


#运行结果
jianliu@ubuntu:~/aa$ ./test.sh 
please input str:\n
e 
exit!
jianliu@ubuntu:~/aa$ ./test.sh 
please input str:\n
2
your select: 2
jianliu@ubuntu:~/aa$ ./test.sh y
please input str:\n
y
you agree

4. for 循环

  • 语法格式
#该命令在/etc/profile中
#for循环一般格式
for 变量名 in 列表
do 
    命令1
    命令2
done
  1. 当变量值在列表里,for循环即执行依次所有命令,使用变量名访问列表中取值。

  2. 命令可为任何有效的shell命令和语句。变量名为任意单词。

  3. in 列表用法是可选择,如果不用它,for循环使用命令行的位置参数。
  4. in 列表可以包含替换、字符串和文件名

—-实例测试————-

jianliu@ubuntu:~/aa$ cat test.sh
#!/bin/bash

for i in 1 2 3 4 5
do
    echo $i
done

jianliu@ubuntu:~/aa$ ./test.sh
1
2
3
4
5
#留心观察输出结果的区别!!!!!!!
jianliu@ubuntu:~/aa$ cat test.sh
#!/bin/bash

echo "test 1"
for i in "aa bb cc dd"
do
    echo $i
done

echo "test 2"
for i in aaa bbb ccc ddd
do
    echo $i
done

jianliu@ubuntu:~/aa$ ./test.sh
test 1
aa bb cc dd
test 2
aaa
bbb
ccc
ddd
#注意 in后为 命令 反引号--------------------
jianliu@ubuntu:~/aa$ cat test.sh
#!/bin/bash

for loop in `cat test.sh`
do 
    echo $loop
done

#注意:打印文件内容按照 行+空格 为一行
jianliu@ubuntu:~/aa$ ./test.sh
#!/bin/bash
for
loop
in
`cat
test.sh`
do
echo
$loop
done
#如果in后没有语句,此时语句等价于: in $@ -------------
jianliu@ubuntu:~/aa$ cat test.sh
#!/bin/bash

#for_noion

i=1

for param
do  echo "param #$i is $param"
    i=$[$i+1]
done

jianliu@ubuntu:~/aa$ ./test.sh aa bbb ccc
param #1 is aa
param #2 is bbb
param #3 is ccc

5. until 循环

#语法格式   until循环格式

until [条件]
do  
    命令1
    命令2
done
#例子   test.sh
#!/bin/bash

VAR=0

until [ $VAR -gt 90 ]
do 
    echo $VAR
    sleep 1
    let VAR+=5
done 

jianliu@ubuntu:~/aa$ ./test.sh
0
5
10
15
20
25
30
35
40
45
50
55
60
65
70
75
80
85
90
  • 条件可以为任意测试条件,测试发生在循环末尾,因此循环至少执行一次
#脚本文件    循环等待直到指定文件exe.app能够被执行,停止程序
#!/bin/bash

until [ -x exe.app ]
do 
    sleep 2
    echo " file exe.app can not be executed!,wait..."
done 

echo "now this file can be executed!"

jianliu@ubuntu:~/aa$ ls -l
total 52
-rw-rw-r-- 1 jianliu jianliu    0 Oct 10 05:26 exe.app

jianliu@ubuntu:~/aa$ nohup ./test.sh &
[1] 3963

#查看所有任务(test.sh脚本在后台执行)
jianliu@ubuntu:~/aa$ jobs
[1]+  Running                 nohup ./test.sh & 

jianliu@ubuntu:~/aa$ chmod a+x exe.app   #改变exe.app的可执行位标志

#test.sh脚本执行完成。
jianliu@ubuntu:~/aa$ jobs
[1]+  Done                    nohup ./test.sh

6. while 循环

#语法格式
while 命令 (可以是一个命令也可以是多个,做条件测试)
do
      命令1
      命令2
      ...
done
  • 注:在while和do之间虽然通常是一个命令,但可以放几个命令。
    命令通常用作测试条件。
#输入名称,直到按下Ctrl+d按键,结束。
#!/bin/bash 

echo "ctrl+d quit"
while echo -n "please enter name:"; read Name
do 
    echo "yeah,i got your name: $Name"
done

jianliu@ubuntu:~/aa$ ./test.sh
ctrl+d quit
please enter name:kangkang
yeah,i got your name: kangkang
please enter name:bingbing
yeah,i got your name: bingbing

#从文本name.txt中读取每一行信息,输出到终端显示。
jianliu@ubuntu:~/aa$ cat test.sh
#!/bin/bash 

while read LINE
do 
    echo $LINE
done < names.txt
#------------------------------------------------------  

jianliu@ubuntu:~/aa$ ./test.sh
kangkang
bingbing
licaiting
wuhaoao
wanboa
dianfeng
kb536

#!!----错误写法----
#!/bin/bash
#这样写是不对的 ,需要把<name.txt 放在 while循环后面
while read LINE < names.txt   
do
        echo $LINE
done 
  • 备注:如果从文件中读入量<filename要放到done后.

7. break 和 continue

break [n]
    退出循环
    如果是在一个嵌入循环里,可以指定n来跳出循环个数
continue
    跳出循环步

注意:continue命令类似于break命令,只有一点重要差别,它不会跳出循环,只是跳出这个循环步
总结:break跳出 continue跳过
jianliu@ubuntu:~/aa$ ./test.sh
Enter any num[1...5:]3
you enter a num between 1 and 5
Enter any num[1...5:]7
wrong num, bye
jianliu@ubuntu:~/aa$ cat ./test.sh
#!/bin/bash
while :   #死循环
do
    echo -n "Enter any num[1...5:]"
    read num
    case $num in
    1|2|3|4|5)
        echo "you enter a num between 1 and 5"
        ;;
    *)
        echo "wrong num, bye"
        break;    #如果不加这一句,while就永远退不出去!!!!!
        ;;
    esac
done

#---------------------------------------------------------------   

jianliu@ubuntu:~/aa$ ./test.sh
Enter any num[1...5:]2
you enter a num between 1 and 5
Enter any num[1...5:]3
you enter a num between 1 and 5
Enter any num[1...5:]6
wrong num, bye
jianliu@ubuntu:~/aa$ 
jianliu@ubuntu:~/aa$ cat test.sh
#!/bin/bash
while :
do
    echo  -n "please input num[1..5]:"
    read num
    case $num in
    1|2|3|4|5)
        echo "your slection is $num, between 1 and 5"
        ;;
    *)
        echo "wrong num, is continue?(y/n)?"
        read IS_CONTINUE
        case $IS_CONTINUE in
        y|yes|Yes|YES)
            continue;
            ;;
        *)
            break;
            ;;
        esac
        ;;
    esac
done
#------------------------------------------------------

jianliu@ubuntu:~/aa$ ./test.sh
please input num[1..5]:2
your slection is 2, between 1 and 5
please input num[1..5]:3
your slection is 3, between 1 and 5
please input num[1..5]:5
your slection is 5, between 1 and 5
please input num[1..5]:7
wrong num, is continue?(y/n)?
n
jianliu@ubuntu:~/aa$ ./test.sh
please input num[1..5]:2
your slection is 2, between 1 and 5
please input num[1..5]:3
your slection is 3, between 1 and 5
please input num[1..5]:6
wrong num, is continue?(y/n)?
y
please input num[1..5]:7
wrong num, is continue?(y/n)?
n
jianliu@ubuntu:~/aa$ 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值