【Linux】函数

一、函数

1、创建函数

如果定义了同名函数,则新定义的函数就会覆盖原先的定义的函数,而且在运行时不会报错。

创建函数的语法:

方法1:使用关键字function

function name {

commands

}

shell脚本中的函数名不能重复

方法2

name() {

commands

}

函数名后的空括号name()表明正在定义的是一个函数。

2、使用函数

不能在函数定义之前调用函数,否则会报错。

bash 复制代码
#!/bin/bash

function test {
  echo "测试123456"
}

count=1
while [ $count -le 5 ]
do
  test
  count=$[ $count + 1 ]
done
echo "循环结束!"
test
echo "脚本运行结束!"
exit

3、函数返回值

使用退出状态码

默认情况下,函数的退出状态码是函数中最后一个命令返回的退出状态码。函数执行结束后,可以使用标准变量 $? 来确定函数的退出状态码。

bash 复制代码
#!/bin/bash

func1() {
   echo "尝试展示不存在的文件。"
   ls -l badfile
}

echo "测试函数:"
func1
echo "退出状态码是:$?"

如下所示,退出状态码是2,因为函数中的最后一个命令执行失败了,但 echo 命令却执行成功了。 所以,使用退出状态码的方法是一种危险的方法。

使用return命令

bash shell会使用return命令以特定的退出状态码退出函数;return命令允许指定一个整数值作为函数的退出状态码。

bash 复制代码
#!/bin/bash

func2() {
  read -p "请输入一个数值:" value
  echo "将输入的数值翻倍。"
  return $[ $value * 2 ]
}

func2
echo "新的值是 $?"
exit

当用此方法获取返回值时,需注意两点:

函数执行一结束就立刻读取返回值:如果在用?变量提取函数返回值之前执行了其它命令,那么函数返回值就会丢失。?变量保存的是最后执行的那个命令的退出状态码。

退出状态码必须在0~255范围内:因为退出状态码必须小于256,因此函数结果也必须是一个小于256的整数值,如果大于256就会产生错误的值。

使用函数输出

bash 复制代码
#!/bin/bash

func3() {
  read -p "请输入一个数值:" value
  echo "将输入的数值翻倍。"
  echo $[ $value * 2 ] # 注意不是return
}

result=$(func3)
echo "新的值是$result"
exit

4、向函数传递参数

函数可以使用标准的位置变量来表示命令行中传给函数的任何参数,如函数名保存在0变量中,函数参数依次保存在1、2等变量中。也可以使用特殊变量**#**来确定传给函数的参数数量。

在shell脚本中调用函数时,必须将参数和函数名放在同一行。

用位置变量获取参数值

bash 复制代码
#!/bin/bash

function func4 {
  if [ $# -eq 0 ] || [ $# -gt 2 ]
  then
    echo -1
  elif [ $# -eq 1 ]
  then
    echo $[ $1 + $1 ]
  else
    echo $[ $1 + $2 ]
  fi
}

echo  -n "传递两个参数:"
value=$(func4 10 15)
echo $value

echo -n "只传递一个参数:"
value=$(func4 20)
echo $value

echo -n "不传递参数:"
value=$(func4)
echo $value

echo -n "传递3个参数:"
value=$(func4 10 20 36)
echo $value

要想在函数中使用脚本的命令行参数,必须在调用函数时手动将其传入。

bash 复制代码
func5() {
  echo $[ $1 * $2 ]
}

if [ $# -eq 2 ]
then
  value=$(func5 $1 $2)
  echo "结果是$value."
else
  echo "错误。"

5、函数中的变量

全局变量

全局变量在shell脚本内任何地方都有效的变量。

在默认情况下,在脚本中定义的任何变量都是全局变量。在函数外定义的变量可在函数内正常访问。

bash 复制代码
#!/bin/bash

function func1 {
  value=$[ $value *2 ]
}

read -p "请输入一个值:" value
func1
echo "新的值是 $value"

局部变量

任何在函数内部使用的变量都可以被声明为局部变量,只需在变量声明之前加上关键字 local 即可:local var

也可在变量赋值语句中使用 local 关键字:local var=\[ value + 7 ]

local关键字保证了变量仅在函数中有效,如果函数外有同名变量,那么shell会保持这两个变量的值互不干扰,意味着可以轻松地将函数变量和脚本变量分离开。

bash 复制代码
#!/bin/bash

function func2 {
  local temp=$[ $value + 10 ]
  result=$[ $temp * 2 ]
}

temp=5
value=8

func2
echo "结果是$result"
if [ $temp -gt $value ]
then
  echo "temp更大"
else
  echo "value更大"
fi

6、向函数传递数组

将数组变量当做单个参数传递的话,是不会起作用的。如果想要将数组变量作为函数参数进行传递,那么函数只会提取数组变量的第一个元素。

bash 复制代码
#!/bin/bash

function func3 {
  echo "参数是:$@"
  thisarray=$1
  echo "接收到的数组是 ${thisarray[*]}"
}

myarray=(1 2 3 4 5 6)
echo "原始数组是:${myarray[*]}"
func3 $myarray

传递数组变量时必须先将数组变量拆解成多个数组元素,然后将这些数组元素作为函数参数传递,最后在函数内部,将所有的参数重新组合成一个新的数组变量。

bash 复制代码
#!/bin/bash

function func5 {
  local newarray
  newarray=(`echo "$@"`)
  echo "新数组是:${newarray[*]}"
}

myarray=(1 2 3 4 5 6)
echo "原始数组是:${myarray[*]}"
func5 ${myarray[*]}
bash 复制代码
#!/bin/bash

function addarray {
  local sum=0
  local newarray
  newarray=(`echo "$@"`)
  for value in ${newarray[*]}
  do
    sum=$[ $sum + $value ]
  done
  echo $sum
}

myarray=(1 2 3 4 5 6)
echo "原始数组是:${myarray[*]}"
arg1=$(echo ${myarray[*]})
result=$(addarray $arg1)
echo "结果是 $result"

7、从函数返回数组

函数先用echo语句按正确顺序输出数组的各个元素,然后脚本再将数组元素重组成一个新的数组变量。

bash 复制代码
#!/bin/bash

function arraydb {
  local origarray
  local newarray
  local element
  local i
  origarray=($(echo "$@")) 
  newarray=($(echo "$@"))# "$@"会将每个参数作为独立的字符串保留
  element=$[ $# - 1 ] # $#表示参数数量
  for (( i = 0; i <= $element; i++ ))
  {
     newarray[$i]=$[ ${origarray[$i]} * 2 ]
  }
  
  echo ${newarray[*]}
}

myarray=(1 2 3 4 5 6)
echo "原始数组是:${myarray[*]}"
arg1=$(echo ${myarray[*]})
result=($(arraydb $arg1))
echo "新数组是:${result[*]}"

8、函数递归

局部函数变量的一个特性是自成体系(self-containment) ,除了获取函数参数,自成体系函数不需要使用任何外部资源。这个特性使函数可以递归地调用,即函数通过调用自己得到结果。

递归函数通常有一个最终可以迭代到的基准值。

递归算法的经典例子就是计算阶乘。

bash 复制代码
#!/bin/bash
function factorial {
  if [ $1 -eq 1 ]
  then
    echo 1
  else
    local temp=$[ $1 - 1 ]
    local result=$(factorial $temp)
    echo $[ $result * $1 ]
  fi
}

read -p "请输入一个数值:" value
result=$(factorial $value)
echo "$value的阶乘是:$result"
exit

9、函数库

bash shell允许创建函数库文件,然后在多个shell脚本中引用此库文件。

可以通过 source 命令(也叫点号操作符)在shell脚本中运行库文件,它会在当前shell的上下文中执行命令,而不是创建新的shell并在其中执行命令。

格式:. ./绝对路径/函数库文件名

bash 复制代码
#!/bin/bash

#创建一个函数库文件

function addem {
  echo $[ $1 + $2 ]
}

function multem {
  echo $[ $1 * $2 ]
}

function divem {
  if [ $2 -ne 0 ]
  then
    echo $[ $1 / $2 ]
  else
    echo -1
  fi
}

在其它shell脚本中运行上面的函数库文件。

bash 复制代码
#!/bin/bash

#用source命令允许库文件
. ./myfuncs.sh # 该函数库文件与此脚本在同一目录下

value1=60
value2=20
result1=$(addem $value1 $value2)
echo "两数相加之和是:$result1"
result2=$(multem $value1 $value2)
echo "两数相乘之积是:$result2"
result3=$(divem $value1 $value2)
echo "两数相除之商是:$result3"

二、在命令行中使用函数

一旦在shell中定义了函数,就可以在整个系统的任意目录中使用,而不用担心该函数是否位于PATH环境变量中。

1、在命令行中创建函数

因为shell会解释用户输入的命令,所以可以在命令行中直接定义一个函数。缺点就是,在退出shell时,函数也会跟着消失

单行方式

使用单行方式定义函数时,需要在每个命令后面加分号,这样shell才能知道哪里是命令的起止。

多行方式

使用多行方式定义函数时,bash shell会使用次提示符来提示输入更多命令,此方法不需要再每条命令后面加分号,只需按下回车键即可。

当输入函数末尾的花括号 } 时,shell就能知道用户已经完成函数的定义了。

2、在.bashrc文件中定义函数

可以将函数定义在每次新shell启动时都会重新读取该函数的地方:.bashrc文件。

不管是交互式shell还是从现有shell启动新的shell,bash shell在每次启动时都会在用户主目录中查找.bashrc文件。

直接定义函数

可以直接在用户主目录的.bashrc文件中定义函数。将函数直接放在末尾即可,如下所示。

此函数在下次启动新的bash shell时生效,然后就能在系统中的任意地方使用此函数。

源引函数文件

只要是在shell脚本中,就可以用source命令(点号操作符)将库文件中的函数添加到.bashrc脚本中,如下所示:

确保库文件的路径正确,下次重新启动bash shell时,就可以使用库中的所有函数。

也可以直接在shell脚本中使用,因为shell会将定义好的函数传给子shell进程,如此一来,这些函数就能够自动用于该shell会话中的任何shell脚本了。

bash 复制代码
#!/bin/bash

value1=20
value2=5

result1=$(addem $value1 $value2)
echo "两数之和是:$result1."
result2=$(multem $value1 $value2)
echo "两数之积是:$result2."
result3=$(divem $value1 $value2)
echo "两数之商是:$result3."

如下所示,执行脚本时报错,但能在交互式命令行执行。

相关推荐
寂柒1 小时前
信号量——基于环形队列的生产消费模型
linux·ubuntu
一袋米扛几楼982 小时前
【密码学】CrypTool2 工具是什么?
服务器·网络·密码学
vin_zheng4 小时前
破解企业安全软件网络拦截实战记录
运维
林姜泽樾5 小时前
Linux入门第十二章,创建用户、用户组、主组附加组等相关知识详解
linux·运维·服务器·centos
xiaokangzhe6 小时前
Linux系统安全
linux·运维·系统安全
feng一样的男子6 小时前
NFS 扩展属性 (xattr) 提示操作不支持解决方案
linux·go
南棱笑笑生6 小时前
20260310在瑞芯微原厂RK3576的Android14查看系统休眠时间
服务器·网络·数据库·rockchip
xiaokangzhe6 小时前
Nginx核心功能
运维·nginx
松果1776 小时前
以本地时钟为源的时间服务器
运维·chrony·时间服务器
XDHCOM6 小时前
ORA-32152报错咋整啊,数据库操作遇到null number问题远程帮忙修复
服务器·数据库·oracle