【Linux 命令行与 shell 脚本编程大全】 17 创建函数

导览

  1. 为了提高脚本中代码的重用性,可以将指定代码块封装为一个函数,然后在脚本中通过函数的名称对函数进行调用
  2. 脚本函数可以用函数最后一条命令的退出状态码或 return 命令来返回指定数值,范围在 0-255 之间
  3. 函数也可以使用 echo 语句输出返回值,这可以让函数的返回值不局限于数值
  4. 可以再函数中使用 shell 变量,函数的变量分为 全局变量局部变量局部变量 需要添加 local 前缀
  5. 函数可以调用包裹其自身在内的其他函数,函数调用自身的方式叫做 递归
    • 在使用递归时必须设置一个终结条件,否则会导致无限循环
  6. 使用 函数库文件 可以实现函数在不同脚本中的调用,只需要在调用的脚本中使用 source 命令引入指定 函数库文件 即可
  7. 可以直接在命令行创建和调用函数,也可以将函数写在 .bashrc ,实现函数的全局调用
  8. 函数库文件.bashrc 中进行 source 命令的调用,也可以实现函数的全局调用

17.1 基本的脚本函数

  1. 函数是一个脚本代码块,可以为其指定一个名称并在脚本中进行调用

17.1.1 创建函数

  1. 脚本中每个函数的名称都不能重复

17.1.1.1 基本语法

function name {
  commands
}

17.1.1.2 简写语法

name() {
  commands
}

17.1.2 使用函数

  1. 在脚本中创建函数后,就可以在后续代码中进行调用,如下图
  2. 需要注意的是,函数并不是必须得在脚本的开始位置进行创建 ,但是 函数必须在其被调用之前创建
  3. 如果函数的创建语句在其调用语句之后,shell 会因为找不到被调用的函数而抛出异常
[ttxie@41 part17]$ cat create-function.sh
#!/bin/bash

function echotip {
   echo "开始执行循环"
}

count=1

while [ $count -le 3 ]
do
  echotip
  count=$[ $count + 1 ]
done
image.png

17.1.2.1 函数名称的唯一性

  1. 如果在脚本中定义了重名的函数,那么新定义的函数会覆盖之前的函数,如下图
[ttxie@41 part17]$ cat function-name-unique.sh
#!/bin/bash

function echotip {
   echo "输出一句话"
}

echotip

#创建同名函数
function echotip {
   echo "函数名相同,之前的函数名会被覆盖"
 
}

echotip
image.png

17.2 返回值

17.2.1 默认退出状态码

  1. 11.8.1 查看退出状态码 中已经介绍过退出状态码的概念,常见的退出状态码可以参考下图

    image
  2. 默认情况下, 函数的退出状态码时函数中最后一条命令返回的退出状态码

  3. 在函数执行完毕后,可以立即用标准变量 $? 来确定函数的退出状态码,如下图

    • 可以看到,在 exitCode 函数的最后使用 ls badDir 查询了一个不存在的目录
    • 那么该命令的退出状态码肯定是 1 ,使用 $? 可以正常获取到该值
#!/bin/bash

function exitCode {
   echo "trying to display a non-existent file"
   ls badfile
}

exitCode

echo "The exit status is $?"
image.png
  1. 但是如果有异常的命令不是最后一条命令,则无法得知该命令的执行结果,如下图
    • 相对于上一个脚本,该脚本只是将函数内部的两个命令互换了位置
    • 但是得到的函数返回值却截然相反,所以这种直接通过 $? 获取函数返回值的方式,没有多少实用意义
[ttxie@41 part17]$ cat exit-code-new.sh 
#!/bin/bash

function exitCode {
   ls badfile
   echo "trying to display a non-existent file"
}

exitCode

echo "The exit status is $?"
image.png

17.2.2 使用 return 命令

  1. 使用 return 命令可以退出函数,并返回特定的退出状态码
  2. return 命令允许指定一个整数值来自定义函数的退出状态码,如下图
[ttxie@41 part17]$ cat return-exit.sh
#!/bin/bash

function customExitCode {
   read -p "请输入一个数字:"
   return $[ $REPLY * 2 ]
}

customExitCode

echo "输入值被乘2后的结果是:$?"
image.png

17.2.2.1 使用退出状态码作为函数返回值的缺陷

  1. 要获取退出状态码需要使用 $? ,而且需要在函数执行之后立即使用,如果在其中穿插了其他命令,就无法获取到函数最后一条命令的退出状态码
  2. 退出状态码的取值范围是 0-255 ,不在这个范围内的值无法作为退出状态码来赋予函数返回值

17.2.3 使用函数输出

  1. 函数可以将 echo 的输出内容作为函数的返回值,如下图
    • 函数中的 echo 语句虽然输出了一句话,但函数被调用后,这句话并没有立即输出
    • 它作为函数的返回值被保存到了 result 变量中,直到该变量被 echo 使用时,函数的返回值才被输出
    • 而且可以看到,使用这种方式时,函数的返回值不局限于数字,可以返回浮点值和字符串值。
[ttxie@41 part17]$ cat echo-return-value.sh
#!/bin/bash

function echoResultValue {
   read -p "请输入一个名字:"
   echo "$REPLY 是第一个用户"
}

result=$(echoResultValue)

echo "系统提示: $result"
image.png

17.3 在函数中使用变量

17.3.1 向函数传递参数

  1. 可以使用给脚本传参的方式给函数传参,函数内部对于传入参数的调用方式也和脚本中调用保持一致,如下图
[ttxie@41 part17]$ cat func-param.sh
#!/bin/bash

function addition {
   if [ $# -ne 2 ]
   then
     echo "请输入两个参数"
   else
     echo $[ $1 + $2 ]
   fi
}

result=$(addition 1 1)
echo "1+1=$result"

##输入一个参数
result=$(addition 1)
echo "错误提示:$result"
image.png

17.3.2 在函数中处理变量

  1. 在函数中可以使用 全局变量 ,以及 局部变量
    • 函数中定义的 局部变量 对于函数以外的部分,是不可见的

17.3.2.1 全局变量

  1. 默认情况下,在脚本中定义的任何变量都是全局变量

17.3.2.2 局部变量

  1. 使用 local 关键字声明的变量就是局部变量,该变量只能在其声明的函数内部才能访问,如下图
    • 两个 result 变量,虽然名称相同,但由于函数内部的是局部变量,所以互不影响
[ttxie@41 part17]$ cat local-variable.sh
#!/bin/bash

function localVariable {
   local result=5
   echo $result
}

result=6

value=$(localVariable)

echo "脚本直接声明的result:$result"
echo "函数中声明的result:$value"
image.png

17.4 数组变量和函数

17.4.1 向函数传递数组参数

  1. 如果试图直接将数组作为参数传递到函数中,将无法得到完整的数组内容,只能得到数组的第一个值,如下图
    • 正常的使用 ${array[*]} 可以获取到数组的全部内容
    • 但是将数组作为一般参数传入到函数中后,不论如何操作,都只能获取到数组的第一个值,所以数组不能作为一般参数直接传入函数
[ttxie@41 part17]$ cat bad-array-param.sh
#!/bin/bash

function badArrayParam {
   echo "尝试使用$@ 获取传入的全部参数"
   result=$1
   echo "尝试接受参数后,使用标准的数组输出方式获取内容:${result[*]}"
}

array=(1 2 3)
echo "原始数组内容:${array[*]}"

badArrayParam $array
image.png

17.4.1.1 将数组内容分解后传入函数

  1. 想要让函数能够获取到完整的数组内容,需要先将数据的内容分解成为普通的参数列表
  2. 然后在函数中对传入的所有参数进行重新组装,获取一个新的数组,如下图
    • 将数组传入函数时使用的 arrayParam ${array[*]} ,其实就是将数组的输出结果 1 2 3 作为普通参数全部传给函数
      • 完整形式就是 arrayParam 1 2 3
    • 然后在函数内容使用 ($(echo "$@")) 将参数一次性输出,并包装成为数组内容
[ttxie@41 part17]$ cat array-param.sh
#!/bin/bash

function arrayParam {
   local newArray=($(echo "$@"))
   echo "重新组装的数组内容:${newArray[*]}"
}

array=(1 2 3)
echo "原始数据的数组内容:${array[*]}"

arrayParam ${array[*]}
image.png

17.4.2 从函数返回数组

  1. 其实和将数组作为普通参数传入到函数中一样

  2. 想要从函数内部返回数组,只需要将函数内部的数组作为普通参数输出即可,如下图

    image

17.5 函数递归

  1. 递归其实就是函数自己调用自己,但必须为递归准备一个最终条件,否则会导致无限循环

  2. 递归算法的经典例子是计算阶乘,例如 5! = 1 * 2 * 3 * 4 * 5 = 120 ,公式是 x! = (x * (x -1))!

  3. 用脚本来表示的话,如下图

    image

17.6 创建库

  1. 利用 函数库文件 可以实现一个函数在不同的脚本中被重复调用

  2. 使用 source 命令可以在指定脚本中调用 函数库文件

    • 默认情况下,由于 shell 函数作用域的限制,函数只会在其被定义的脚本中生效
    • 但是 source 命令会在当前 shell 上下文中执行命令,而不是创建一个新 shell ,相当于把目标脚本解析到了当前脚本中
  3. source 命令的语法分两种

    • 完整语法 source ./file
    • 简写语法 . ./file
  4. 写一个简单例子演示一下,如下图

    image

17.7 在命令行上使用函数

17.7.1 在命令行上创建函数

  1. 使用这种方式创建的函数都是临时的,一旦当前 shell 退出,这些被创建的函数都将不复存在
  2. 而且这种方式不但没有什么实际意义,还非常危险,如果误将函数的名称和当前系统中其他命令的名称重名了,那么这个系统命令将会被覆盖

17.7.1.1 单行定义函数

  1. 其实就是将一个简单的的函数直接在命令行上进行创建,如下图
    • 在每个命令的最后都需要添加分号,这样 shell 才会知道命令的开始和结束在什么时候发生

      image

17.7.1.2 多行定义函数

  1. 使用 shell 的 次提示符( > ) 来输入更多命令,如下图
    • 当输入 { 后按下回车,会直接进入函数的多行模式

    • 当输入 } 后按下回车,会直接退出函数的多行模式

      image

17.7.2 在 .bashrc 文件中定义函数

  1. 因为 .bashrc 文件是的特性,用户在开启每个 shell 进程时,系统都会扫描该文件
  2. 所以可以把该文件作为一个默认的 全局函数库文件 ,将函数直接写在这个文件中,将可以直接实现函数的全局调用
  3. 但该文件中也会存在一些系统预置的内容,所以直接添加各种自定义函数,容易导致文件内容变的混乱
  4. 所以也可以使用 source 命令将一些自定义的 函数库文件 ,在该文件的末尾进行引用

17.8 实例

  1. 介绍 GNU shtool shell 脚本函数库如何安装和使用,跳过了

转载来自:
作者:asing1elife
链接://www.greatytc.com/p/d99f5ee55edb
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355