文章目录
6.1 函数
6.1.1 什么是函数
所谓函数,与其他语言中的函数没有太大的本质区别。它们都完成特定功能的代码块,这个代码块是可以重复使用的,并是一组命令或者语句组成。
在 Shell 中,函数本质上就是将函数名称与要实现的特殊功能的代码进行引用的一种方式。
Shell 中函数具有如下的优势:
- 把相同的程序代码定义函数,这样就可以重复利用,从而提长开发效率
- 增加了程序代码段的可读性和提升管理效率
- 可以实现程序功能的模块化,使得程序具备通用性
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