6. 函数和数组

6.1 函数

6.1.1 什么是函数

所谓函数,与其他语言中的函数没有太大的本质区别。它们都完成特定功能的代码块,这个代码块是可以重复使用的,并是一组命令或者语句组成。

在 Shell 中,函数本质上就是将函数名称与要实现的特殊功能的代码进行引用的一种方式。

Shell 中函数具有如下的优势:

  1. 把相同的程序代码定义函数,这样就可以重复利用,从而提长开发效率
  2. 增加了程序代码段的可读性和提升管理效率
  3. 可以实现程序功能的模块化,使得程序具备通用性

6.1.2 函数的语法

在 Shell 中函数具有以下语法结构:

# 完整结构
function 函数名称() {
	指令或语句
	return [返回值]
}

# 简化结构
function 函数名称 {
	指令或语句
	return [返回值]
}

# 更加简化的结构
函数名称() {
	指令或语句
	return [返回值]
}

6.1.3 函数的调用

在 Shell 中调用函数之前,该函数必须存在。调用函数的语法如下:

函数名称 [参数1 参数2 ....]

对于函数的调用,我们也可以使用 $# 来获取参数的个数,也可以使用 $1$2······来获取是第几个参数,还可以使用 $@ 或者 $* 来获取所有的参数列表。

例如:

[root@openEuler ~]# cat fun1.sh
#!/bin/bash

# 定义一个名称叫 fun 的函数
fun() {
        echo "the function has $# parameters"
        echo "all parameters $@"
        echo "first parameter $1"
        echo "second parameter $2"
}

# 调用定义好的 fun 函数
fun hello world


[root@openEuler ~]# bash fun1.sh 
the function has 2 parameters
all parameters hello world
first parameter hello
second parameter world
[root@openEuler ~]# 

注意:在 Shell 函数是先定义再使用,如果是先使用再定义,则会报如下的错误:

[root@openEuler ~]# cat fun1.sh
#!/bin/bash

#先调用 fun 函数
fun hello world

# 再定义 fun 函数
fun() {
        echo "the function has $# parameters"
        echo "all parameters $@"
        echo "first parameter $1"
        echo "second parameter $2"
}

[root@openEuler ~]# bash fun1.sh 
fun1.sh: line 3: fun: command not found

如果函数只声明了,但是没有调用这个函数,那么该函数不会执行。但是我们可以使用 . 或者 source 来先执行这个脚本,让函数变放到当前的 shell 环境中,此时我们在 shell 环境中就可以执行这个函数了。 如:

[root@openEuler ~]# cat fun1.sh
#!/bin/bash

#只声明 fun 函数,但是没调用函数
fun() {
        echo "the function has $# parameters"
        echo "all parameters $@"
        echo "first parameter $1"
        echo "second parameter $2"
}

#将脚本中的函数放到当前shell 环境中
[root@openEuler ~]# source ./fun1.sh 

[root@openEuler ~]# fun 1 2
the function has 2 parameters
all parameters 1 2
first parameter 1
second parameter 2

6.1.4 函数的返回值

在 Shell 中函数也是可以有返回值,但是它和别的语言中的函数返回值不太相同。

1、在函数中如果使用 return 语句来退出函数,并返回函数的值的话,那么在函数外就需要使用 echo $? 来获取返回的值

注意:在 shell 中使用 return 返回的只能是整数值,而且范围不能超过 0-255,如果超过了则需要与 256 取模

2、如果在函数中使用的是 echo 输出,那么在函数外部就可以使用 变量=$(函数名称)来获取返回的值

示例1:计算两个参数的和

[root@openEuler ~]# cat fun2.sh 
#!/bin/bash
sum1() {
	sum=$[$1 + $2]
	echo $sum
}
read -p "Please enter first number: " first
read -p "Please enter second number: " second

sum1 first second
#或者
res=$(sum1 first second)

echo $res

[root@openEuler ~]# bash fun2.sh 
Please enter first number: 6     
Please enter second number: 4
10

示例2:获取字符串的长度

[root@openEuler ~]# cat fun3.sh 
#!/bin/bash
length() {
        str=$1
        len=0
        if [ "$1" != "" ]; then
                len=${#str}
        fi      
        return $len
}       
length "abc123"
echo "the string's length is $?"

[root@openEuler ~]# bash fun3.sh 
the string's length is 6

6.1.5 函数的案例

示例1:将一个点分十进制格式的IP地址转换成点分二进制格式。比如
255.255.255.255 --> 11111111.11111111.11111111.11111111

[root@openEuler ~]# cat fun4.sh 
#!/bin/bash

decimal_to_bin() {
	NUM=$1
	for i in {1..8}
	do
		SUM=$[NUM % 2]$SUM
		let NUM/=2
		#((NUM=NUM/2))
	done
	echo $SUM
}

split_ip() {
	IP=$1
	for i in {1..4}
	do
		num=${IP%%.*}    # IP=192.168.72.150     -> num=192
		#echo "num=$num"
		IP=${IP#*.} # IP=168.72.150    delete 192
		#echo "ip=$IP"
		BIN=$(decimal_to_bin num)
		#decimal_to_bin $num
		echo -n $BIN
	done
}

read -p "Please enter a valid IP address: " INIP

res=$(split_ip $INIP)
echo ${res%.*}

[root@openEuler ~]# bash fun4.sh 
Please enter a valid IP address: 192.168.72.112
11000000.10101000.01001000.01110000

示例2:写一个脚本,判断给定的 IP 地址范围[192.168.72.130 ~ 192.168.72.140]是否在线。

[root@openEuler ~]# cat fun5.sh
#!/bin/bash

online() {
        for i in {148..152}
        do      
                if ping -c1 192.168.72.$i &>/dev/null
                then    
                        echo "192.168.72.$i is up"
                else    
                        echo "192.168.72.$i is unknow"
                fi      
        done    
}       

online

[root@openEuler ~]# bash fun5.sh
192.168.72.148 is unknow
192.168.72.149 is unknow
192.168.72.150 is up
192.168.72.151 is up
192.168.72.152 is unknow

6.1.6 函数变量的作用域

示例1:函数的变量是全局变量

[root@openEuler ~]# cat fun6.sh 
#!/bin/bash

str="hello shell"
func() {
	str="openlab"
	echo $str
	str2="hello"
}
echo "$str"
func
echo "$str"
echo "$str2"

[root@openEuler ~]# bash fun6.sh 
hello shell
openlab
openlab
hello

从上面的结果中可以发现,在函数中定义变量是全局变量

注意:在 $() 这种方式来执行命令时,变量值还是需要使用 $变量名 的方式来获取,当使用 $(()) 来做算术运算时,可以省略$而直接使用变量名。

 #!/bin/bash
 
 a=1
 b=2
 c=$[ $a + $b ] #`$a+$b`
 
 echo `expr $a + $b`
 
 echo $(expr $a + $b)
 
 echo $c
 
 d=$((a+b))
 echo $d

示例2:函数的变量如果希望是局部的,那么我们需要显式的使用 local 来声明

[root@openEuler ~]# cat fun7.sh 
#!/bin/bash

s="ni hao"
func() {
	local s="ni ye hao"
	echo $s
	local s1="da jia wang shang hao"
}
echo "$s"
func
echo "$s"
echo "${s1}"
[root@openEuler ~]# bash fun7.sh 
ni hao
ni ye hao
ni hao

[root@openEuler ~]# 

当使用 local 来声明变量时,它就是一个局部变量,离开函数后,这个变量就消失了。

6.1.7 递归函数

示例1:根据用户输入的数值计算该数的阶乘

[root@openEuler ~]# cat recursion.sh 
#!/bin/bash

recursion() {
	if [ $1 -eq 1 ] ; then
		result=1
	elif [ $1 -gt 1 ] ; then
		local n=$[ $1 - 1 ]
		recursion $n
		let "result=$1 * $?"
	else
		echo "The number is invalid"
	fi
	return $result
}

recursion $1
echo $?

[root@openEuler ~]# bash recursion.sh 5
120

由于在函数中使用的是 return 来返回的值,所以它能返回的最大值为 255,超过这个值后会与 256 取模后再返回。可以考虑使用 echo 的来返回值。

示例2:使用函数递归/var/log目录,如果是文件直接输入文件名,如果是目录则输出目录名称并输出目录下所有子目录和文件名

[root@openEuler ~]# cat fact.sh 
#!/bin/bash

list_file() {
	for f in $(ls $1)
	do
		if [ -d "$1/$f" ]; then #/var/log/anaconda
			echo "$2 directory $f"
			list_file "$1/$f" "├── $2"
		else
			echo "$2 file $f"
		fi
	done
}
list_file $1 "├── "


[root@openEuler ~]# bash fact.sh /var/log
├──  directory anaconda
├── ├──  file anaconda.log
├── ├──  file dbus.log
├── ├──  file dnf.librepo.log
├── ├──  file hawkey.log
├── ├──  file journal.log
├── ├──  file lorax-packages.log
├── ├──  file lvm.log
├── ├──  file packaging.log
├── ├──  file program.log
├── ├──  file storage.log
├── ├──  file syslog
├── ├──  file X.log
├──  directory audit
├── ├──  file audit.log
├──  file boot.log
├──  file boot.log-20240324
........

6.1.8 函数库文件

示例1:创建库文件

[root@openEuler ~]# cat function_library.sh 
#!/bin/bash

addition() {
	echo $[$1 + $2]
}

substraction() {
	echo $[$1 - $2]
}

multipication() {
	echo $[$1 * $2]
}

division() {
	if [ $2 -eq 0 ]; then
		echo "Division cannot be 0"
	else
		echo $[$1 / $2]
	fi
}

factorial() {
	if [ $1 -eq 1 ]; then
		echo 1
	elif [ $1 -gt 1 ]; then
		local tmp=$[$1-1]
		local res=$(factorial $tmp)
		echo $[$1 * res]
	else
		echo "input value invalid"
	fi
}

2)在脚本中加载函数库文件并使用

[root@openEuler ~]# cat fl_test.sh 
#!/bin/bash

# load function_library.sh
source /root/function_library.sh

v1=10
v2=5

r1=$(addition $v1 $v2)
r2=$(substraction $v1 $v2)

r3=$(factorial $v2)


echo $r1
echo $r2
echo $r3


[root@openEuler ~]# bash fl_test.sh 
15
5
120

6.2 数组

所谓数组,就是指将具有相同类型的若干变量按照一定的顺序组织在一起的一种数据类型。

6.2.1 定义数组语法

第一种语法:用小括号将变量值括起来赋值给数组变量,每个变量之间使用空格分隔。

array=(value1 value2 value3 ... valuen)

第二种语法:使用小括号将变量值括起来,并且采用键值对的形式赋值。

array=([0]=one [1]=two .... [n-1]=valuen)

第三种语法:分别定义数组变量的值

array[0]=one; array[1]=tow; array[n]=valuen

第四种语法:使用动态的定义变量,并使用命令的输出结果作为数组的内容

array=(命令)

第五种语法:通过 declare 语句来定义数组

declare -a array

6.2.2 数组操作

6.2.2.1 获取所有元素
# 声明数组,数组的名称为 array,它里面有 6 个元素,分别是 1,2,3,4,5,6
[root@openEuler ~]# array=(1 2 3 4 5 6)
# 获取数组所有无素,需要注意使用大括号将整个数组包起来
[root@openEuler ~]# echo $array[*]
1[*]
[root@openEuler ~]# echo ${array[*]}
1 2 3 4 5 6
[root@openEuler ~]# echo ${array[@]}
1 2 3 4 5 6
6.2.2.2 获取元素下标
# 定义数组,数组的名称为 array,它的第一个元素是 hello,第二个元素是 shell,第三个元素是 python
[root@openEuler ~]# array=([0]="hello" [1]="shell" [2]="python")
# 通过 !数组名称[*|@] 来获取元素对应的下标
[root@openEuler ~]# echo ${!array[@]}
0 1 2
[root@openEuler ~]# echo ${!array[*]}
0 1 2
6.2.2.3 获取数组长度
# 定义数组,使用的是第三种语法
[root@openEuler ~]# array[0]="zhangsan"; array[1]="lisi"; array[2]="wangwu"
# 获取数组中第一个元素的长度
[root@openEuler ~]# echo ${#array}
8
# 获取数组元素个数(数组长度)
[root@openEuler ~]# echo ${#array[*]}
3
[root@openEuler ~]# echo ${#array[@]}
3
6.2.2.4 获取数组元素
#使用第四种语法:数组名称=(命令) 来创建数组,然后通过下标获取对应的元素
[root@openEuler ~]# array=(`seq 5`)
#获取数组中所有元素
[root@openEuler ~]# echo ${array[*]}
1 2 3 4 5
#获取数组中下标为 3 所对应的元素,如果存在则返回该下标对应的元素,如果不存在则返回空
[root@openEuler ~]# echo ${array[3]}
4
[root@openEuler ~]# echo ${array[6]}

[root@openEuler ~]# echo $?
0
6.2.2.5 数组添加元素
# 使用第五种语法来定义数组,数组的名称为 array
[root@openEuler ~]# declare -a array
# 给数组下标为0的位置添加元素
[root@openEuler ~]# array[0]=50
# 给数组下标为1的位置添加元素
[root@openEuler ~]# array[1]=35
# 给数组下标为2的位置添加元素
[root@openEuler ~]# array[20]=28
# 获取数组中所有元素
[root@openEuler ~]# echo ${array[*]}
50 35 28

# 使用 -A 的方式来定义数组
[root@openEuler ~]# declare -A arr
[root@openEuler ~]# arr[one]=15
[root@openEuler ~]# arr[two]=20
[root@openEuler ~]# arr[thr]=30
[root@openEuler ~]# echo ${arr[@]}
30 20 15

# 获取下标对应的元素
[root@openEuler ~]# echo ${array[1]}
35
[root@openEuler ~]# echo ${arr[two]}
20
6.2.2.6 删除数组元素
# 定义数组
[root@openEuler ~]# array=(10 20 30 40 50)
# 获取数组中所有元素
[root@openEuler ~]# echo ${array[@]}
10 20 30 40 50
# 删除数组中下标为2的元素
[root@openEuler ~]# unset array[2]
# 验证是否已经删除
[root@openEuler ~]# echo ${array[*]}
10 20 40 50
[root@openEuler ~]# echo ${#array[*]}
4
6.2.2.7 删除数组
# 定义数组
[root@openEuler ~]# array=(10 20 30 40 50)
# 获取数组长度
[root@openEuler ~]# echo ${#array[*]}
5
# 删除数组
[root@openEuler ~]# unset array
# 验证数组是否删除
[root@openEuler ~]# echo ${#array[@]}
0
6.2.2.8 遍历数组元素
[root@openEuler ~]# cat array.sh
#!/bin/bash

files=(`ls $1`)
for((i=0;i<${#files[*]};i++)); do
        echo ${files[$i]}
done

[root@openEuler ~]# bash array.sh 
array.sh
fact.sh
fl_test.sh
fun1.sh
fun2.sh
fun3.sh
fun4.sh
fun5.sh
fun6.sh
fun7.sh
function_library.sh
ips
myfile
mytest.sh
recursion2.sh
recursion.sh

6.2.3 数组案例

示例1:从标准输入读取 n 次字符串,每次输入的字符串保存在数组 array 中。

[root@openEuler ~]# cat array1.sh 
#!/bin/bash
i=0
n=5
while [ "$i" -lt $n ]; do
	echo "Please input string... `expr $i + 1`"
	read array[$i]
	b=${array[$i]}
	echo "$b"
	i=`expr $i + 1`
done
[root@openEuler ~]# bash array1.sh 
Please input string... 1
1
1
Please input string... 2
2
2
Please input string... 3
3
3
Please input string... 4
4
4
Please input string... 5
5
5

示例2:将字符串中的字母逐个放入数组,并输出到标准输出

#!/bin/bash

strs="helloworldshell"
declare -a array

for ((i=0;i<${#strs};i++)); do
        array[$i]=${strs:$i:1}
done

for i in ${array[@]}; do
        echo ${i}
done

示例3:把1~3这3个数字存到数组中,分别乘以8后再依次输出

#!/usr/bin/bash

array=(`seq 3`)

for ((i=0; i<${#array[@]}; i++)); do
        echo $[${array[$i]}*8]
done

示例4:输出如下内容中字母数不大于6的单词。

cat is favorite to eat fish

#!/bin/bash

array=(cat is favorite to eat fish)

for i in ${array[*]}; do
        if [ ${#i} -le 6 ]; then
                echo $i
        fi      
done
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

acro_09

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值