SHELL-函数

作者dmxsp

5月 29, 2024

1.Shell 函数概述

Shell 和其他语言一样,也有函数,其本质就是一段可以复用的代码。将数据进行抽离处理,传递不同的值输出不同的结果,在指定的地方进行调用即可。

2.为什么要用函数

如果我们要重复执行一批相同的操作,不想重复去写,可以将这一系列操作抽象为一个函数,后期可以利用变量传值调用该函数,从而大大减少重复性劳动,提升效率减少代码量,使得 Shell 脚本更加的灵活和通用

3.定义函数的三种方法

在 Shell 脚本中,有三种方法可以定义函数,包括使用 function 关键字、使用函数名和圆括号、以及使用两个大括号。以下是这三种方法的示例:

方法一:使用function关键字和函数名

function my_function1 {
# 函数内的代码
}

方法二:省略function关键字,直接使用函数名和大括号

my_function2() {
# 函数内的代码
}

方法三:使用function关键字、函数名和大括号

function my_function3() {
# 函数内的代码
}

在上述示例中,my_function1、my_function2和my_function3分别是三个函数的名称。

这三种方法都可以用来定义函数,选择哪一种取决于个人喜好和习惯。通常来说,第二种方法是最常见和推荐的用法;即省略function关键字而直接使用函数名和大括号来定义函数。

下面是一个包含上述三种方法定义函数的示例Shell脚本:

#!/bin/bash

# 方法一
function my_function1 {
echo “Method 1”
}

# 方法二
my_function2() {
echo “Method 2”
}

# 方法三
function my_function3() {
echo “Method 3”
}

# 调用函数
my_function1
my_function2
my_function3

运行该脚本将会依次调用这三个方法定义的函数,并输出相应的信息。

[root@shell /server/scripts]$ sh 3.sh
Method 1
Method 2
Method 3
[root@shell /server/scripts]$ cat 3.sh
#!/bin/bash

# 方法一
function my_function1 {
echo “Method 1”
}

# 方法二
my_function2() {
echo “Method 2”
}

# 方法三
function my_function3() {
echo “Method 3”
}

# 调用函数直接写函数名称即可 必须先定义在调用
my_function1
my_function2
my_function3

[root@shell /server/scripts]$ sh 3.sh
Method 1
Method 2
Method 3

其他脚本中调用

[root@shell /server/scripts]$ cat 4.sh
#!/bin/sh
. ./3.sh
my_function1
[root@shell /server/scripts]$ cat 3.sh
#!/bin/bash

# 方法一
function my_function1 {
echo “Method 1”
}

# 方法二
my_function2() {
echo “Method 2”
}

# 方法三
function my_function3() {
echo “Method 3”
}

# 调用函数
#my_function1
#my_function2
#my_function3
[root@shell /server/scripts]$ sh 4.sh
Method 1

source

source 命令(在某些 shell 中也可以用 . 表示)主要用于在当前 shell 环境中执行指定的脚本。

source命令用于在当前Shell环境中执行指定的脚本文件,这样可以使被调用的脚本中的变量、函数、以及其他Shell命令在当前Shell中生效。当使用source命令执行脚本时,脚本文件中的内容会在当前Shell环境中直接执行,而不会创建一个新的子Shell。

[root@shell /server/scripts]$ source 3.sh
[root@shell /server/scripts]$ my_function1
Method 1
[root@shell /server/scripts]$ my_function2
Method 2
[root@shell /server/scripts]$ my_function3
Method 3

4.fun函数定义的前缀

在Shell脚本编程中,fun关键字通常用作函数的前缀,尽管它本身并不是一个强制性的关键字。fun可以用来定义函数,使得函数的开始更加醒目。函数在Shell脚本中是一组命令的集合,可以被赋予一个名称,并在需要时通过这个名称来调用。函数的定义允许代码的复用,提高了脚本的模块化和可维护性。

fun关键字的使用语法

在Bash Shell中,定义函数的语法可以有多种形式,其中包括使用function关键字或不使用。以下是一些定义函数的常见语法:

function funName() { 
# 函数体 
}

或者

funName() {
    # 函数体
}

在这两种形式中,funName是函数的名称,圆括号内可以包含函数的参数列表(如果有的话),而花括号包围的部分是函数的主体,即执行特定任务的一系列命令

fun关键字的功能和作用

fun关键字的主要功能是标识一个代码块作为函数。在Shell脚本中,函数可以接受输入参数,执行一系列操作,并可能返回一个值或状态码。通过定义函数,可以将复杂的任务分解成更小、更易于管理的部分,从而提高脚本的清晰度和效率。

fun关键字的最佳实践和注意事项

清晰命名:为函数选择描述性强的名称,以便于理解函数的功能。
单一职责原则:确保每个函数只负责一个明确的任务。
参数传递:合理使用位置参数和命名参数,以便于函数的灵活性和可读性。
返回值:使用return语句返回函数的状态码,如果需要返回数据,可以通过echo命令输出,并在函数外部捕获这些数据。
局部变量:使用local关键字定义局部变量,以防止变量污染全局环境

示例

以下是一个简单的Shell函数定义和使用示例:

解释
#!/bin/bash

function greet() {
    echo "Hello, $1!"
}

greet "World"

在这个例子中,greet函数定义了一个简单的问候语,并通过传递一个参数来个性化问候。当函数被调用时,它会输出”Hello, World!”。

5.函数传参

函数传参的方式比较简单直接

#!/bin/bash

# 定义函数
function my_function {
    echo "第一个参数: $1"
    echo "第二个参数: $2"
    echo "所有参数: $@"
}

# 调用函数并传递参数
my_function "参数 1" "参数 2"
在上述示例中,$1 表示第一个参数,$2 表示第二个参数,$@ 表示所有的参数。
您还可以指定更多的参数,比如 $3 、$4 等。
另外,如果您不确定传递的参数个数,可以使用 $# 来获取参数的数量。

[root@shell /server/scripts]$ cat fun.sh
#!/bin/bash
fun(){
if [ -f $1 ];then
echo “$1 存在”
else
echo “$1 不存在”
fi
}
fun /etc/passwd
[root@shell /server/scripts]$ sh fun.sh
/etc/passwd 存在

计算

[root@shell /server/scripts]$ cat fun1.sh
#!/bin/bash
fun(){
for i in `seq 10`
do
total=$[$1+$i]
done
echo 计算结果为 $total
}
fun $3
fun $2
fun $1

[root@shell /server/scripts]$ sh fun1.sh 10 20 30
计算结果为 40
计算结果为 30
计算结果为 20

6.函数的本地变量 local

local语句在编程中指的是用来声明变量为局部作用域的关键字。局部变量仅在定义它们的函数或代码块内部有效,一旦超出这个作用域,变量就不再可用。使用local关键字可以帮助避免变量名的冲突,并使得程序的结构更加清晰和易于维护24.

local语句的使用场景

限制变量作用域:在函数内部使用local声明变量,确保变量不会影响到函数外部的环境。

提高代码的可读性和可维护性:通过明确变量的作用范围,可以让代码的意图更加明显,便于其他开发者理解和维护。

避免全局变量污染:过度使用全局变量可能导致程序状态难以追踪,使用局部变量可以减少这种风险。

local语句的语法和工作机制

在不同的编程语言中,local关键字的语法和使用方式有所不同。例如,在Python中,变量默认在函数内部是局部的,除非明确声明为global;而在Lua和某些Shell脚本语言中,需要显式使用local关键字来声明局部变量234.

local语句的最佳实践

尽可能使用局部变量,以减少潜在的命名冲突和提高代码的封装性。

在函数或代码块内部使用local声明变量,确保变量的生命周期和作用域受到限制。

避免在循环或条件语句中重新声明local变量,以免造成混淆。

local语句与其他作用域声明关键字的关系

local关键字与其他作用域声明关键字如global和nonlocal一起使用,分别用于管理变量在不同层级作用域中的可见性和生命周期。global用于在函数内部访问全局变量,而nonlocal用于在函数内部访问闭包外部的变量14.

在 Shell 脚本中,local 关键字用于在函数内部定义本地变量。
本地变量的作用范围仅限于定义它的函数内部。这意味着在函数外部无法访问这些变量,并且在函数内部对本地变量的修改不会影响到函数外部同名的变量。

#!/bin/bash

function my_function {
    local local_variable="This is a local variable"  # 定义本地变量
    echo "Local variable inside function: $local_variable"
}

my_function  # 调用函数

# 尝试在函数外部访问本地变量会失败
echo "Local variable outside function: $local_variable"# 这里会提示变量未定义
在上述示例中,local_variable 是在 my_function 函数内部使用 local 关键字定义的本地变量。在函数外部尝试访问它时,会提示该变量未定义。
使用 local 定义本地变量有助于避免变量名冲突,并使函数的逻辑更加清晰和独立。

再比如,如果函数外部已经有一个同名变量,在函数内部使用 local 定义的同名变量不会影响到外部的那个变量:

#!/bin/bash

outer_variable="Outer"

function my_function {
    local outer_variable="Inner"  # 定义本地变量,与外部的同名变量无关
    echo "Local outer_variable inside function: $outer_variable"
}

my_function  # 调用函数
echo "Outer_variable outside function: $outer_variable"  # 输出仍然是 "Outer"

计算

[root@shell /server/scripts]$ cat fun2.sh
#!/bin/bash
fun(){
local num=10
for i in `seq 10`
do
total=$[$num+$i]
done
echo 计算结果为 $total
}
fun
echo $num
[root@shell /server/scripts]$ sh fun2.sh
计算结果为 20

7.函数返回值 return

return语句在编程中用于从函数或方法中返回一个值,并结束该函数或方法的执行。当执行到return语句时,程序控制流会返回到函数或方法被调用的位置,继续执行后续的代码。如果函数或方法声明了返回类型,return语句后面必须跟随一个符合该类型的表达式。如果函数或方法声明为void类型,即不返回任何值,return语句可以省略,或者仅包含return关键字本身1234.

return语句的使用场景

返回计算结果:当函数需要计算并返回一个值时,return语句用来返回这个计算结果。

提前退出方法:在某些逻辑判断后,如果不需要继续执行函数剩余的代码,可以使用return语句提前退出方法。

递归方法中的返回:在递归函数中,return语句用于返回递归调用的结果,直至达到递归的基础情形。

return语句的注意事项

返回值类型匹配:确保return语句中的表达式与函数声明的返回类型相匹配。

void方法中的用法:在声明为void的方法中,return语句不应有任何表达式,仅用于指示方法的结束。

多个return语句:虽然一个方法可以包含多个return语句,但过多的return语句可能会降低代码的可读性。

return语句后的代码:一旦执行到return语句,后续的代码将不会被执行。

return语句与其他控制语句的区别

break语句用于立即退出循环或switch语句,而不会返回任何值。

continue语句用于结束当前循环迭代,并开始下一次迭代,而不是完全退出循环。

return语句不仅可以结束当前函数的执行,还可以返回一个值给调用者

[root@shell /server/scripts]$ cat fun3.sh
#!/bin/bash
fun(){
echo 100
return 1
}
result=`fun`
echo “当前函数的返回值是: ” $?
echo “当前函数的直接结果是: ” $result
[root@shell /server/scripts]$ sh fun3.sh
当前函数的返回值是: 1
当前函数的直接结果是: 100

在这个脚本中,定义了一个名为fun的函数,函数内部打印出了数字100,并通过return语句返回了值1。在调用函数fun时,使用反引号“来获取函数的输出值,结果保存在result变量中。然后分别输出函数的返回值和函数的直接结果。

在shell脚本中,函数的返回值通过return语句来定义,函数的直接输出是通过echo等命令输出的内容。在这个例子中,返回值是1,直接结果是100。

使用返回值来判断文件是否存在

[root@shell /server/scripts]$ cat fun4.sh
#!/bin/bash
fun(){ 
      if [ -f $1 ];then
         return 50
      else
         return 100
      fi 
}
fun $1

[root@shell /server/scripts]$ sh fun4.sh /etc/passwd
[root@shell /server/scripts]$ echo $?
50

[root@shell /server/scripts]$ sh fun4.sh /etc/passwdq
[root@shell /server/scripts]$ echo $?
100

fun():定义了一个名称为fun的函数。
if [ -f “$1” ]; then:使用if语句和[ -f “$ 1” ]测试条件来检查参数$1指定的文件是否是一个存在的普通文件。
return 50:如果文件存在,return语句将50作为函数的退出状态码返回。
return 100:如果文件不存在,return语句将100作为函数的退出状态码返回。
fun “$1″:在脚本的最后,调用fun函数,并将命令行参数$ 1作为参数传递给函

[root@shell /server/scripts]$ cat fun5.sh 
#!/bin/bash
fun(){ 
     if [ -f $1 ];then
         return 50
     else
         return 100
       fi 
}
fun $1
[ $? -eq 50 ] && echo "$1 存在" || echo "$1 不存在"

[root@shell /server/scripts]$ sh fun5.sh /etc/passwd
/etc/passwd 存在
[root@shell /server/scripts]$ sh fun5.sh /etc/passwdd
/etc/passwdd 不存在

在函数调用 fun $1 之后,使用 $ ? 来捕获函数的退出状态码。$? 是一个特殊变量,用于存储上一个命令的退出状态。接下来的 [$ ? -eq 50 ] && echo “$1 存在” || echo “$ 1 不存在” 这条语句是一个条件语句,它根据 $? 的值输出不同的信息:

如果 $? 等于 50,则输出 “$1 存在”,意味着 $1 所指向的文件存在。

如果 $? 不等于 50(即等于 100,因为这是 fun 函数在文件不存在时返回的值),则输出 “$1 不存在”,意味着 $1 所指向的文件不存在

[root@shell /server/scripts]$ cat fun6.sh 
#!/bin/bash
fun(){ 
     if [ -f $1 ];then
         return 50
     else
         return 100
     fi 
}
fun $1
result=$?
if [ $result -eq 50 ];then
     echo "$1 文件存在"
elif [ $result -eq 100 ];then
      echo "$1 文件不存在"
fi
[root@shell /server/scripts]$ sh fun6.sh /etc/passwd
/etc/passwd 文件存在
[root@shell /server/scripts]$ sh fun6.sh /etc/passwdd
/etc/passwdd 文件不存在

fun 的函数来检查文件是否存在,并根据检查结果返回不同的退出状态码。这里,如果文件存在,函数返回 50;如果文件不存在,函数返回 100。

在调用 fun $1 后,使用 result=$ ? 将函数的退出状态码存储到变量 result 中,这使得在后续的条件判断中可以直接使用 result 变量,而不需要再次使用 $?。

接下来的 if [ $result -eq 50 ]; then echo “$ 1 文件存在” elif [ $result -eq 100 ]; then echo “$ 1 文件不存在” fi 这段语句根据 result 的值输出不同的信息:

如果 result 等于 50,则输出 “1文件存在”,意味着‘1` 所指向的文件存在。

如果 result 等于 100,则输出 “1文件不存在”,意味着‘1` 所指向的文件不存在

[root@shell /server/scripts]$ cat fun7.sh 
#!/bin/bash
fun(){ 
     if [ -f $1  ];then
        return 50
     else
        return 100
     fi 
}
fun $1
case $? in
         50)
   echo "$1 文件存在"
         ;;
    100)
  echo "$1 文件不存在"
;;
*)
         echo USAGE
esac

[root@shell /server/scripts]$ sh  fun7.sh  /etc/passwd
/etc/passwd 文件存在
[root@shell /server/scripts]$ sh  fun7.sh  /etc/passwdd
/etc/passwdd 文件不存在

fun 函数与之前讨论的相同,它检查一个文件是否存在,并返回 50 如果文件存在,或者返回 100 如果文件不存在。

fun $1 调用函数,然后 $ ? 捕获函数的返回值。接下来的 case $? in 语句检查 $ ? 的值,并根据匹配的值执行相应的代码块:

如果 $? 的值是 50,则执行第一种情况,输出 “$1 文件存在”,表示文件存在。

如果 $? 的值是 100,则执行第二种情况,输出 “$1 文件不存在”,表示文件不存在。

如果 $? 的值既不是 50 也不是 100,则执行默认情况(*)),输出 “USAGE”,这通常用于处理未预期的情况或错误。

使用 case 语句的结构可以让你更轻松地管理多个可能的返回值,并且使得脚本的逻辑更加清晰和易于维护。然而,正如之前所提到的,使用非标准的退出状态码(如 50 和 100)可能在与标准的 shell 脚本或函数交互时造成问题。通常,0 表示成功,非 0 的值(如 1)表示错误或特定状态。为了提高兼容性和通用性,推荐使用这些标准值。

作者 dmxsp

发表回复

您的电子邮箱地址不会被公开。