[ Content contribution] 基于deepin的shell编程-循环与处理用户输入
Tofloor
poster avatar
TIANGESEC工作室
deepin
2023-12-30 01:43
Author

循环

7.1 for语句

与其他的编程语言一样,shell编程也支持循环语句。

语法

 for varlue in list 
 do 
     commands
 done

第二种语法当然也可以直接在list后面加上一个;直接接上do。

 zql@zql-PC:~$ cat for1.sh
 #!/bin/bash
 ​
 echo "This is a simple for setence."
 for name in Lisa Dave Phplip
 do
         echo "Hell0 $name!"
 done
 echo "Progame exit."
 zql@zql-PC:~$ ./for1.sh
 This is a simple for setence.
 Hell0 Lisa!
 Hell0 Dave!
 Hell0 Phplip!
 Progame exit.
 zql@zql-PC:~$

读取列表里的复杂值

 zql@zql-PC:~$ ./for2.sh
 这是一个复杂的列表读取.
 Word:I
 Word:Love
 Word:?
 Word:Whats your name? Iam
 Word:is
 Word:Lisa.
 Progame exit.

sh源码

 zql@zql-PC:~$ cat for2.sh
 #!/bin/bash
 echo "这是一个复杂的列表读取." ; sleep 0.2
 for value in I Love ? What's your name? I'am is Lisa.
 do
         echo "Word:$value"
 done
 echo "Progame exit."

发现他无法识别引号。它会把引号包括的内容和前后的字符进行输出。只有使用转义符\或者双引号来定义单引号的都内容。

修改后如下:

 zql@zql-PC:~$ ./for2.sh
 这是一个复杂的列表读取.
 Word:I
 Word:Love
 Word:?
 Word:What's
 Word:your
 Word:name?
 Word:I'am
 Word:is
 Word:Lisa.

注意:for循环各个值之间使用 空格隔开的,当值里面含有空格就麻烦了,必须使用双引号来将其空格输出

从变量中读取值

 zql@zql-PC:~$ ./for3.sh
 读取变量例的值.
 I love Name:Lisa.
 I love ZhangFei.
 I love XiaHouDun.
 Progame exit.
 zql@zql-PC:~$ cat for3.sh
 #!/bin/bash
 ​
 echo "读取变量例的值." ; sleep 0.2
 namelist="Lisa ZhangFei XiaHouDun"
 namelist="Name:"$namelist
 ​
 for value in $namelist
 do
 echo "I love $value."
 done
 echo "Progame exit."

从命令中读取列表的值

 zql@zql-PC:~$ cat for4.sh
 #!/bin/bash
 echo "从命令中读取列表的值."
 name="for1.sh"
 for value in $(cat $name)
 do
         echo "value=$value"
 done
 echo "Progame exit."
 zql@zql-PC:~$ ./for4.sh
 从命令中读取列表的值.
 value=#!/bin/bash
 value=echo
 value="This
 value=is
 value=a
 value=simple
 value=for
 value=setence."
 value=for
 value=name
 value=in
 value=Lisa
 value=Dave
 value=Phplip
 value=do
 value=echo
 value="Hell0
 value=$name!"
 value=done
 value=echo
 value="Progame
 value=exit."
 Progame exit.

更改字段分隔符

若要修改空格、制表符、换行符作为shell的字段分隔符,就必须要用到IFS,IFS是特殊的环境变量,用来定义内部字段的分隔符。如果只想让换行符作为为能够识别的分隔符只需要把\n加入IFS中即可。

 zql@zql-PC:~$ ./for5.sh
 更改分隔符为只换行符.
 value=#!/bin/bash
 value=echo "This is a simple for setence."
 value=for name in Lisa Dave Phplip
 value=do
 value=  echo "Hell0 $name!"
 value=done
 value=echo "Progame exit."
 Progame exit.
 ​
 ​
 zql@zql-PC:~$ cat for5.sh
 #!/bin/bash
 ​
 file="for1.sh"
 echo "更改分隔符为只换行符."
 IFS=$'\n'
 for value in $(cat $file)
 do
         echo "value=$value"
 done
 echo "Progame exit."

注意:在写大量脚本的时候,需要IFS的值进行保存恢复

保存IFS值,便于恢复

 IFS.OLD=$IFS
 ​
 IFS=#"\n"

定义新的IFS值

 IFS=$IFS.OLD

通配符读取目录

使用for循环来遍历目录,利用通配符的特点来实现。

 zql@zql-PC:~$ ./for6.sh
 /var/account is a directory.
 /var/adm is a directory.
 /var/cache is a directory.
 /var/crash is a directory.
 /var/db is a directory.
 /var/empty is a directory.
 /var/ftp is a directory.
 /var/games is a directory.
 /var/kerberos is a directory.
 /var/lib is a directory.
 /var/local is a directory.
 /var/lock is a directory.
 /var/log is a directory.
 /var/mail is a directory.
 /var/nis is a directory.
 /var/opt is a directory.
 /var/preserve is a directory.
 /var/run is a directory.
 /var/spool is a directory.
 /var/tmp is a directory.
 /var/www is a directory.
 /var/yp is a directory.
 ​
 zql@zql-PC:~$ cat for6.sh
 #!/bin/bash
 for file in /var/*
 do
         if [ -d "$file" ]
         then echo "$file is a directory."
         elif [ -f "$file" ]
         then "$file is a file."
         fi
 done

7.2 C风格的for语句

语法

 for (( variable assignment ; condition ; iteration process ))

注意:

  • 变量赋值可以有空格。
  • 迭代条件中的变量不以美元符号开头。
  • 迭代过程的算式不使用expr命令格式。

 zql@zql-PC:~$ ./for7.sh
 Number: --1
 Number: --2
 Number: --3
 Number: --4
 Number: --5
 Number: --6
 Number: --7
 Number: --8
 Number: --9
 Number: --10
 zql@zql-PC:~$ cat for7.sh
 #!/bin/bash
 ​
 for (( i=1; i <= 10; i++ ))
 do
         echo "Number: --$i"
 done
 ​

多变量循环

 #!/bin/bash
 # multiple variables
 ​
 for (( a=1, b=10; a <= 10; a++, b-- ))
 do
    echo "$a - $b"
 done
 zql@zql-PC:~$ ./for9.sh
 1 - 10
 2 - 9
 3 - 8
 4 - 7
 5 - 6
 6 - 5
 7 - 4
 8 - 3
 9 - 2
 10 - 1

7.3 while语句

**while命令在某种程度上糅合了if-then语句和for循环。while命令允许定义一个要测试的命令,只要该命令****返回的退出状态码为0,就循环执行一组命令。它会在每次迭代开始时测试test命令,如果test命令返回非0退出状态码,while命令就会停止执行循环。**一直为0就是死循环。

语法

 while test command
 do
     other commends
 done

 zql@zql-PC:~$ ./while1.sh
 10
 9
 8
 7
 6
 5
 4
 3
 2
 1
 zql@zql-PC:~$ cat while1.sh
 #!/bin/bash
 ​
 var1=10
 while [ $var1 -gt 0 ]
 do
         echo $var1
         var1=$[ $var1 -1 ]
 done

多个测试命令

while命令允许在while语句行定义多个测试命令。只有最后一个测试命令的退出状态码会被用于决定是否结束循环。

 zql@zql-PC:~$ ./while2.sh
 10
 这是循环的内部.
 9
 这是循环的内部.
 8
 这是循环的内部.
 7
 这是循环的内部.
 6
 这是循环的内部.
 5
 这是循环的内部.
 4
 这是循环的内部.
 3
 这是循环的内部.
 2
 这是循环的内部.
 1
 这是循环的内部.
 0
 这是循环的内部.
 -1
 zql@zql-PC:~$ cat while2.sh
 #!/bin/bash
 ​
 value=10
 while echo $value
         [ $value -ge 0 ]
 do
         echo "这是循环的内部."
         value=$[ $value -1 ]
 done

以上表明当在含有多个命令的while语句中,每次迭代都会被执行,包括最后一个执行命令失败的末次迭代。

7.4 untile

与while命令工作的方式完全相反,until命令要求指定一个返回非0退出状态码的测试命令。只要测试命令的退出状态码不为0,bash shell就会执行循环中列出的命令。一旦测试命令返回了退出状态码0,循环就结束了。

 until test commands
 do
    other commands
 done

 zql@zql-PC:~$ ./until1.sh
 5
 4
 3
 2
 1
 zql@zql-PC:~$ cat until1.sh
 #!/bin/bash
 ​
 value=5
 until [ $value -eq 0 ]
 do
         echo $value
         value=$[ $value - 1 ]
 done
 ​

与while语句一样,until语句多命令的时候每次迭代都会被执行,包括最后一个执行命令失败的末次迭代。

7.5 嵌套循环与文件数据

嵌套循环

循环的时候嵌套其他的语句就是嵌套循环,until与while也是可以嵌套在一起的。

注意:在使用嵌套的时候是在迭代中迭代,命令运行的次数是乘积关系。

 zql@zql-PC:~$ ./forfor.sh
 外层循环1:
 内循环:1
 内循环:2
 内循环:3
 外层循环2:
 内循环:1
 内循环:2
 内循环:3
 外层循环3:
 内循环:1
 内循环:2
 内循环:3
 zql@zql-PC:~$ cat forfor.sh
 #1/bin/bash
 ​
 for (( a = 1; a <= 3; a++ ))
 do
         echo "外层循环$a:"
         for (( b = 1; b <=3; b++ ))
         do
                 echo "内循环:$b"
         done
 done

until与while循环嵌套

 $ cat test16
 #!/bin/bash
 # using until and while loops
 ​
 var1=3
 ​
 until [ $var1 -eq 0 ]
 do
    echo "Outer loop: $var1"
    var2=1
    while [ $var2 -lt 5 ]
    do
       var3=$(echo "scale=4; $var1 / $var2" | bc)
       echo "   Inner loop: $var1 / $var2 = $var3"
       var2=$[ $var2 + 1 ]
    done
    var1=$[ $var1 - 1 ]
 done
 $ ./test16
 Outer loop: 3
    Inner loop: 3 / 1 = 3.0000
    Inner loop: 3 / 2 = 1.5000
    Inner loop: 3 / 3 = 1.0000
    Inner loop: 3 / 4 = .7500
 Outer loop: 2
    Inner loop: 2 / 1 = 2.0000
    Inner loop: 2 / 2 = 1.0000
    Inner loop: 2 / 3 = .6666
    Inner loop: 2 / 4 = .5000
 Outer loop: 1
    Inner loop: 1 / 1 = 1.0000
    Inner loop: 1 / 2 = .5000
    Inner loop: 1 / 3 = .3333
    Inner loop: 1 / 4 = .2500
 $
 ​
 #该段代码来源于:Linux命令行与shell脚本编程大全

处理数据

遍历文件中保存的数据,利用iFS和循环嵌套来实现。通过修改IFS环境变量,能强制for命令将文件中的每一行都作为单独的条目来处理,即便数据中有空格也是如此。从文件中提取出单独的行后,可能还得使用循环来提取行中的数据。

 zql@zql-PC:~$ cat No1.sh
 #!/bin/bash
 IFS.OLD=$IFS
 IFS=$'\n'
 for file in $(cat /etc/passwd)
 do
         echo "Values in $file ---"
         IFS=:
         for value in $file
         do
                 echo "  $value"
         done
 done
 ​
 zql@zql-PC:~$ ./No1.sh
 Values in root:x:0:0:root:/root:/bin/bash ---
   root
   x
   0
   0
   root
   /root
   /bin/bash
 Values in bin:x:1:1:bin:/bin:/sbin/nologin ---
   bin
   x
   1
   1
   bin
   /bin
   /sbin/nologin

7.6 循环控制

使用break和continue可以直接退出循环。

二者的区别就是break是直接跳出循环(内、外、单个),而continue是跳出某次循环。

 #break跳出单个循环
 #!/bin/bash
 # breaking out of a for loop
 ​
 for var1 in 1 2 3 4 5 6 7 8 9 10
 do
    if [ $var1 -eq 5 ]
    then
       break
    fi
    echo "Iteration number: $var1"
 done
 echo "The for loop is completed"
 $ ./test17
 Iteration number: 1
 Iteration number: 2
 Iteration number: 3
 Iteration number: 4
 The for loop is completed
 ​
 ​
 #continue跳出某次循环
 #!/bin/bash
 # using the continue command
 ​
 for (( var1 = 1; var1 < 15; var1++ ))
 do
    if [ $var1 -gt 5 ] && [ $var1 -lt 10 ]
    then
       continue
    fi
    echo "Iteration number: $var1"
 done
 $ ./test21
 Iteration number: 1
 Iteration number: 2
 Iteration number: 3
 Iteration number: 4
 Iteration number: 5
 Iteration number: 10
 Iteration number: 11
 Iteration number: 12
 Iteration number: 13
 Iteration number: 14
 ​
 #该段代码来源于:Linux命令行与shell脚本编程大全

break结束外层循环语法

 break n

continue语法

 continue n

处理用户输入

参数传递与参数读取

bash shell会将所有的命令行的参数指向位置参数。

什么是位置参数,$0对应的是脚本名字,一次类推到9.

 zql@zql-PC:~$ ./inputnumber.sh 5
 5的乘阶是120...
 Progame exit.
 zql@zql-PC:~$ ./inputnumber.sh 10
 10的乘阶是3628800...
 Progame exit.
 ​
 zql@zql-PC:~$ cat inputnumber.sh
 #!/bin/bash
 #传递参数与参数的读取
 a=1
 #注意这里将常量number改为了一个变量$1
 for (( number = 1; number <= $1; number++ ))
 do
         a=$[ $a * $number ]
 done
 sleep 0.2
 echo "$1的乘阶是$a..." ; sleep 0.2
 echo "Progame exit."
 exit
 ​

字符串也是可以传递进去的

 zql@zql-PC:~$ ./inputstring.sh World
 Hello World!
 ​
 zql@zql-PC:~$ cat inputstring.sh
 #!/bin/bash
 #传递字符串
 echo "Hello $1!"
 exit

8.1 读取脚本名

使用位置变量$0获取命令行中运行的shell脚本名。

 zql@zql-PC:~$ ./readsh.sh
 This is name is ./readsh.sh
 ​
 zql@zql-PC:~$ cat readsh.sh
 #!/bin/bash
 #读取脚本名
 echo "This is name is $0"
 exit

注意:命令会和脚本名混合在一起,出现在$0中。

8.2 统计参数

使用$#可以统计参数

 ┌──(zql㉿kali)-[~/sh]
 └─$ ./concat.sh Hello
 参数统计
 1 parameter was supplied
 ​
 ┌──(zql㉿kali)-[~/sh]
 └─$ ./concat.sh Hello World
 参数统计
 2 parameters were supplied
 ​
 ┌──(zql㉿kali)-[~/sh]
 └─$ ./concat.sh Hello World !
 参数统计
 3 parameters were supplied
 ​
 ┌──(zql㉿kali)-[~/sh]
 └─$ cat concat.sh
 #!/bin/bash
 #
 echo "参数统计"
 if [ $# -eq 1 ]
 then
         fragment="parameter was"
 else
         fragment="parameters were"
 fi
 echo $# $fragment supplied

8.3 获取所有的数据

$加上*或@可以轻松访问所有参数。

 ┌──(zql㉿kali)-[~/sh]
 └─$ ./anydata.sh 1234567
 获取所有的数据
 ​
 Using the $* method: 1234567
 ​
 Using the $@ method: 1234567
 ​
 ​
 ┌──(zql㉿kali)-[~/sh]
 └─$ ./anydata.sh I love You !
 获取所有的数据
 ​
 Using the $* method: I love You !
 ​
 Using the $@ method: I love You !
 ​
 ​
 ┌──(zql㉿kali)-[~/sh]
 └─$ cat anydata.sh
 #!/bin/bash
 #
 echo "获取所有的数据"
 echo
 echo "Using the \$* method: $*"
 echo
 echo "Using the \$@ method: $@"
 echo
 exit

两者的区别

 ┌──(zql㉿kali)-[~/sh]
 └─$ ./anydata.sh I love You !
 获取所有的数据
 ​
 Using the $* method: I love You !
 $ Parameter #1 =
 ​
 Using the $@ method: I love You !
 \@ Parameter #1 = I
 \@ Parameter #2 = love
 \@ Parameter #3 = You
 \@ Parameter #4 = !
 ​
 ​
 ┌──(zql㉿kali)-[~/sh]
 └─$ cat anydata.sh
 #!/bin/bash
 #
 echo "获取所有的数据"
 echo
 echo "Using the \$* method: $*"
 count=1
 for parm in "$*"
 do
         echo "\$ Parameter #$count = $param"
         count=$[ $count + 1 ]
 done
 echo
 echo "Using the \$@ method: $@"
 count=1
 for param in "$@"
 do
         echo "\@ Parameter #$count = $param"
         count=$[ $count + 1 ]
 done
 echo
 exit
Reply Favorite View the author
All Replies
兆兆嘟嘟嘟
deepin
2023-12-30 03:59
#1

后面部分在Kali下运行?

Reply View the author
TIANGESEC工作室
deepin
2023-12-30 04:10
#2
It has been deleted!
TIANGESEC工作室
deepin
2023-12-30 04:13
#3
兆兆嘟嘟嘟

后面部分在Kali下运行?

当时写的时候因为是通过ssh连接deepin写的,在Windows终端上也连接了kali,可能写的时候选错终端栏了

Reply View the author
TIANGESEC工作室
deepin
2023-12-30 04:14
#4
兆兆嘟嘟嘟

后面部分在Kali下运行?

kali和deepin都是基于Debian开发的,能直接在deepin上运行的,shell编程本质就是通用的,一种命令行编程语言,所以系统影响不大,我在centos、kylin、Raspberry上一样能运行

Reply View the author
TIANGESEC工作室
deepin
2023-12-30 04:17
#5
TIANGESEC工作室

kali和deepin都是基于Debian开发的,能直接在deepin上运行的,shell编程本质就是通用的,一种命令行编程语言,所以系统影响不大,我在centos、kylin、Raspberry上一样能运行

当然有些细节上可能不太一样,因为大多是Linux都是BASH、kali是ZSH,大部分情况都是通用的,你可以试一试,同样的代码在不同的Linux上面运行,基本上是能够完全运行的,但是调用BASH的时候可能不行,因为不同的Linux有点区别嘛

Reply View the author