系统性学习Shell脚本(3)——流程控制与函数

192 阅读2分钟

(最近很喜欢的一张图)

前言

最近有点摆了属于是,稍微懒一下就会发现,懒起来真香,嘿嘿。

本文还是从语法到实践都过一遍。后面可能还有一篇文章用于深入了解shell的一些原理。

一、流程控制

if 语句

注意1:在shell中,不可以在分支中有空语句。也就是说,如果一个分支没有逻辑,就不要写else

错误示例,下面这种写法是错误的
if ...
then ...
else 空
fi

if else语法格式

if condition
then 
    command1
    command2
    ...
    commandN
 else
     command
 fi

if else-if else

if condition1
then 
    command1
elif condition2
then
    command2
else
    commandN
fi

注意点2 if else 中的判断语句中 如果condition使用的

是 [...] 大于必须使用 -gt 小于必须使用 -lt

如果是 ((...)) 可以使用 > < 或者 -gt -lt

示例

编写一个shell脚本,要求判断输入参数是大于100,还是小于100,实现如下: image.png

需要注意的点:

  1. 参数要从$1开始传,$0是代表文件名
  2. ((...)) 中可以使用 ><;中括号是不可以使用的。

循环语句

for 循环

for 语法一 for 变量 in
 for 变量 in 列表
 do
     command
 done

例子1:输出1到5

#!/bin/bash
for i in 1 2 3 4 5
do 
        echo $i
done

结果如下:

image.png

例子2:输出一段话中的每个单词

#!/bin/bash
for i in 1 2 3 4 5
do 
        echo $i
done

结果如下

image.png

for 语法二 常见写法

这种写法也是很多语言类似的

for((command1;condition;doAfterloop))
do
    command
done


这里的command1一般是变量初始化操作
condition一般是判断语句
doAfterloop一般是要进行下一个循环时要进行的操作

举个栗子

#!/bin/bash
for((i=0;i<5;i++))
do 
        echo $i
done

↑有没有发现一个问题:for中的判断语句i是没有加上$的。shell中的变量一般都是要加上$的,这里是个例外,当然加上了也没关系。

有一个点需要注意

从 ubuntu 6.10 开始,ubuntu 就将先前默认的 bash shell 更换成了dash shell,其表现为  /bin/sh 链接倒了  /bin/dash 而不是传统的  /bin/bash

可以通过 ls -l /bin/*sh 命令看到:

所以在使用 sh 命令执行脚本的时候实际使用的是 dash,而 dash 不支持这种 C 语言格式的 for 循环写法。

解决方法: 使用 bash 代替 sh 运行脚本:

bash test.sh

while循环

while语法

while(( ... ))
do
    command
done

举个栗子

#!/bin/bash
a=5
while(( $a > 0 ))
do
        a=$(( $a-1 ))
        echo $a
done

注意运算的方式: $(()) 或者 $[] 或者 expr ` `

再举个例子

可以用于输入读取

echo '按下 <CTRL-D> 退出'  
echo -n '输入你最喜欢的网站名: '  
while read FILM  
do  
    echo "是的!$FILM 是一个好网站"  
done

until 循环

语法

until condition
do
    command1
    ...
    commandN
done

如果 condition是false 那么就执行里面的语句
如果是true就跳出语句

举例

#!/bin/bash
a=5
until [ $a -lt 1 ]
do
        echo "当前数字是$a"
        a=$[ $a - 1 ]
done

注意一个点:这里面的大于小于 不能用> <要用-lt -gt

case...esac

后面那个结束符其实就是case反过来。 这个也非常简单,类似于swich..case

语法如下

case 变量 in
    模式1) command1
    ;;
模式2)
    command2
    ;;
模式3)
command3
;;
...
*)
    commandN
    ;;
esac

注意:语法中 如果不在指定的模式,那么可以用默认的模式来捕获,就是*)来指定默认语句。

示例:

#!/bin/bash
read a
case "$a" in
"one") echo 'this is one';;
"two") echo 'this is two';;
*) echo 'this is default';;
esac

这个示例需要注意:read 后面是一个变量名。对于字符串case后面的变量需要加上 双引号。对应的case模式需要加上双引号,否则无法达到目标效果(我一个个试的。。。)。

跳出流程 break,continue

这里对于循环和 case而言,用于跳出流程控制。 非常简单,举两个例子吧。 continue例子

#!/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

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

二、函数

其实这个本质就是指令封装。 类似于大多数语言的函数或者mysql中的存储过程,为了提高代码复用与可读性,减少重复代码工作量。

定义函数

shell中函数的定义格式如下:

[ function ] funname [()]  
  
{  
  
    action;  
  
    [return int;]  
  
}  

说明:

  • 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
  • 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255

下面的例子定义了一个函数并进行调用:

示例

demoFun(){  
    echo "这是我的第一个 shell 函数!"  
}  
echo "-----函数开始执行-----"  
demoFun  
echo "-----函数执行完毕-----"  

输出结果:

-----函数开始执行-----
这是我的第一个 shell 函数!
-----函数执行完毕-----

下面定义一个带有return语句的函数:

示例

#!/bin/bash  
funWithReturn(){  
    echo "这个函数会对输入的两个数字进行相加运算..."  
    echo "输入第一个数字: "  
    read aNum  
    echo "输入第二个数字: "  
    read anotherNum  
    echo "两个数字分别为 $aNum 和 $anotherNum !"  
    return $(($aNum+$anotherNum))  
}  
funWithReturn  
echo "输入的两个数字之和为 $? !"  

输出类似下面:

这个函数会对输入的两个数字进行相加运算...
输入第一个数字: 
1
输入第二个数字: 
2
两个数字分别为 1 和 2 !
输入的两个数字之和为 3 !

函数返回值在调用该函数后通过 $? 来获得。

注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。

函数参数

在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...

带参数的函数示例:

示例

#!/bin/bash  
funWithParam(){  
    echo "第一个参数为 $1 !"  
    echo "第二个参数为 $2 !"  
    echo "第十个参数为 $10 !"  
    echo "第十个参数为 ${10} !"  
    echo "第十一个参数为 ${11} !"  
    echo "参数总数有 $# 个!"  
    echo "作为一个字符串输出所有参数 $* !"  
}  
funWithParam 1 2 3 4 5 6 7 8 9 34 73  

输出结果:

第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11 个!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !

注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

另外,还有几个特殊字符用来处理参数:

参数处理说明
$#传递到脚本或函数的参数个数
$*以一个单字符串显示所有向脚本传递的参数
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*相同,但是使用时加引号,并在引号中返回每个参数。
$-显示Shell使用的当前选项,与set命令功能相同。
$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

函数中几个注意的点

如何获得函数的返回值?

使用$?用来代替刚刚执行的函数的结果。

注意:调用的$? 和上一条函数调用的语句紧接着。中间不能有其他语句。这是因为$?仅对上一条语句有效。

举个栗子

#!/bin/bash
function1(){
        return 3;
}
function1
echo $?

function1
echo "中间插一句"
echo $?

结果如下:

3
中间插一句
0

函数返回值的true和false

与C语言不同的是 shell中 0 表示true,0以外其他数字表示false;

还是举个例子

#!/bin/bash
func(){
        return $1
}

if func $1
then
        echo "这是0 是true"
else
        echo "false"
fi

这个脚本可以用来印证上面结论。

后续内容

本文比较详细说了剩下的一些shell脚本的基本语法,总体而言,其实还是比较基础和简单的,这些基本够用了,但是写起脚本来,还是错误一堆?不知道怎么下手?

所以后面的文章主要围绕以下几个点写

  • 总结写shell脚本时常用到的Linux命令。
  • 写shell常用的骚操作以及常见错误写法。
  • shell的深入理解和原理相关内容。

本文基本原创,部分图文来自网络,如有错误,请指点。