shell编程之函数与数组

目录

前言

一、函数

1.1 函数定义

1.2 函数返回值

1.3 函数传参

1.4 函数变量的作用范围

1.5 阶乘函数

1.6 递归目录函数

二、数组

2.1 数组基本介绍

2.2 数组定义方法

2.3 数组包括的数据类型

2.4 获取数组长度

2.5 读取某下标赋值

2.6 数组遍历

2.7 数组切片

2.8 数组替换

2.9 删除数组

2.10 追加数组

2.11 向函数传数组参数

三、冒泡排序


前言

函数的作用

  1. 将命令序列按格式写在一起
  2. 可方便重复使用命令序列

使用函数可以避免代码重复
使用函数可以将大的工程分割为若干小的功能模块,代码的可读性更强

一、函数

1.1 函数定义

方式一:


function 函数名 {
    命令序列
}

示列:

#!/bin/bash
function abc {                           #使用function进行函数定义
   read -p "请输入:" a
   a=$[$a*2]
   return $a                             #return表示退出函数并返回一个退出值,脚本中可以用 $? 变量显示该值,我们执行的返回码用于判断命令是否执行成功
}
abc
echo $?                                  #退出状态码必须是0-255,超出时值将为除以256取余


# 调用函数并将其返回值保存在result变量中
abc
result=$?

方式二:

函数名 () {
      命令序列
}

示列:

#!/bin/bash
abc () {                              
    read -p "请输入:" a
    a=$[$a*2]
    echo $a
}
abc                                            #直接使用函数名进行运算,这里加了abc就会输出两次
或者
result=$(abc)
echo $result

1.2 函数返回值

return表示退出函数并返回一个退出值,脚本中可以用 $? 变量显示该值

使用原则:

  1. 函数一结束就取回返回值,因为 $? 变量只返回执行的最后一条命令的退出状态码
  2. 退出状态码必须是0-255,超出时值将为除以256取余 exit 0
  • return和exit的区别在于它们的作用范围:return只作用于函数内部,而exit作用于整个脚本。如果想从函数中返回一个值并继续执行脚本,应该使用return语句;如果想在任何时候结束整个脚本的执行,应该使用exit语句。
  • 在 Shell 脚本中,return 语句用于指定函数的退出状态码(返回码)。约定俗成地,
    返回码为 0 表示成功,非零值表示失败或错误。
  • 返回码的具体含义是由你自己定义的,只要符合约定即可。你可以根据自己的需要定义不同的返回码和其对应的含义。
  • Shell中的 return 命令通常用于在函数内部控制流程,并将状态码或结果传递给调用者。
    它的主要作用是告诉调用者函数的执行状态或结果,并不像其他语言中的 return 语句那样直接返回函数结果。
  • Shell中的 return 命令主要用于控制函数的退出状态码或返回结果,以便在函数调用后进行相应的处理。
    虽然Shell中的 return 不同于其他语言中的返回语句,但在Shell脚本编程中,它仍然具有一定的作用和用途。
  • 在编写 Shell 脚本时,根据函数的执行结果来设置适当的返回码是一种良好的编程习惯,
    它可以帮助调用者根据返回码来判断函数的执行状态,并根据需要采取相应的措施
  • 在 Shell 函数中,可以使用 exit 语句来终止函数的执行并返回一个退出状态码。
    当函数执行到 exit 语句时,整个脚本将立即停止执行,并将指定的退出状态码返回给调用者

1.3 函数传参

在Shell中,调用函数时可以向其传递参数。
在函数体内部,通过 $n 的形式来获取参数的值,
例如,$1表示第一个参数,$2表示第二个参数…即使用位置参数来实现参数传递。

示列:

#!/bin/bash
sum1 () {
   sum=$[$1 + $2]
   echo $sum
}

read -p "请输入第一个参数:" first
read -p "请输入第二个参数:" second
sum1 $first $second:
----------------------------------------------------------------------------
[root@gcc jiaoben1]#chmod +x test5.sh 
[root@gcc jiaoben1]#./test5.sh 
请输入第一个参数:2
请输入第二个参数:3
5



#!/bin/bash
sum1 () {
   sum=$[$1 + $2]
   echo $sum
}

sum1 $1 $2
---------------------------------------------------------------------------
[root@gcc jiaoben1]#./test5.sh  10 20
30

1.4 函数变量的作用范围

函数在shell脚本中仅在当前shell环境中有效
shell脚本中的变量默认全局有效
将变量限定在函数内部使用local命令

[root@gcc jiaoben1]#vim test5.sh
#!/bin/bash
abc () {
   a=5
   b=6
   return $a $b   #在函数内部定义了全局变量,外部的赋值不能改变全局变量
}

a=8
b=12
c=9
abc
echo "a等于$a"
echo "b等于$b"
echo "c等于$c"

#!/bin/bash
abc () {
   local a=5     #用了local就是函数内部的变量,外部重新赋值会替换这个变量
   local b=6
   return $a $b
}

a=8
b=12
c=9
abc
echo "a等于$a"
echo "b等于$b"
echo "c等于$c"

1.5 阶乘函数

阶乘(factorial)是基斯顿·卡曼(Christian Kramp, 1760 – 1826)于1808年发明的运算符号。

阶乘,也是数学里的一种术语。

阶乘指从1乘以2乘以3乘以4一直乘到所要求的数。  6 1*2*3*4*5*6  8 12345678

例如所要求的数是4,则阶乘式是1×2×3×4,得到的积是24,24就是4的阶乘。 例如所要求的数是6,
则阶乘式是1×2×3×……×6,得到的积是720,720就是6的阶乘。例如所要求的数是n,则阶乘式是1×2×3×……×n,
设得到的积是x,x就是n的阶乘。

在表达阶乘时,就使用“!”来表示。如h阶乘,就表示为h!

示列:

#!/bin/bash
fact () {
   if [ $1 -eq 1 ]
   then
       echo 1
   else
       local temp=$[$1 - 1]   
       local result=$(fact $temp)  
       echo "$[$1*$result]"
   fi
}

read -p "请输入阶乘数:" n
result=`fact $n`
echo "$result"

调用fact函数,并将5作为输入参数传递进去。
在fact函数内部,判断输入参数是否等于1。由于输入参数为5,所以不等于1。
将输入参数减1,得到4。然后递归调用fact函数,并将4作为输入参数传递进去。
在新的fact函数内部,同样判断输入参数是否等于1。由于输入参数为4,所以不等于1。
将输入参数减1,得到3。然后递归调用fact函数,并将3作为输入参数传递进去。
在新的fact函数内部,同样判断输入参数是否等于1。由于输入参数为3,所以不等于1。
将输入参数减1,得到2。然后递归调用fact函数,并将2作为输入参数传递进去。
在新的fact函数内部,同样判断输入参数是否等于1。由于输入参数为2,所以不等于1。
将输入参数减1,得到1。然后递归调用fact函数,并将1作为输入参数传递进去。
在新的fact函数内部,判断输入参数是否等于1。由于输入参数为1,所以等于1。
直接返回1。
回到第9步的fact函数中,将输入参数1乘以结果1,得到1。
回到第7步的fact函数中,将输入参数3乘以结果1,得到3。
回到第5步的fact函数中,将输入参数4乘以结果3,得到12。
回到第3步的fact函数中,将输入参数5乘以结果12,得到60。
将60作为返回值返回给主程序,存储在result变量中。
打印result变量的值,即60。

1.6 递归目录函数

遍历目标目录下的所有目录及子目录

#!/bin/bash
function list_files {
    for f in `ls $1`
    do
      if [ -d "$1/$f" ]
      then
          echo "$f"                  #如果是目录就输出这个目录,$2为空格是用于区分父子目录
          list_files "$1/$f"
      else
          echo "$f"                   #不是目录则输出$f结果
      fi
     done
}
list_files $1

[ -d "$1/$f" ]中,斜杠(/)用于将参数$1和变量$f连接起来,以构造一个路径,用于检查是否存在一个指定的目录。
例如,如果参数$1包含/home/user,变量$f包含mydir,则路径/home/user/mydir将被构造出来,
并用于检查是否存在一个名为mydir的目录。

二、数组

2.1 数组基本介绍

  • arr=(30  20  10  60) #数组中的元素值其中:30为索引0、20为索引1、10为索引2、60为索引3、以此类推,不过注意索引是从0开始的,不要弄混肴
  • 如果数组中有三个参数:1 2 3
    • 运行数组脚本后
      "$*" 为 “1 2 3” (作为一个整体使用)
      "$@" 为“1” “2” “3” (分别作为单个的个体使用)
      "$#" 为3 (表示参数的数量,也可以叫做长度

 echo "使用 \"${arr[*]}\" 进行展开:"

for item in "${arr[*]}"; do
    echo $item
done

echo "使用 \"${arr[@]}\" 进行展开:"

for item in ${arr[@]}; do
    echo $item
done

2.2 数组定义方法

方法一:

数组名=(value0 valuel value2 …)

示列:

[root@loaclhost shuzu1]#arr=(1 2 3 4 5)

[root@loaclhost shuzu1]#echo ${arr[*]}
1 2 3 4 5

方法二:

数组名=( [0]=value [1]=value [2]=value …)

示列:

[root@loaclhost shuzu1]#arr=([0]=1 [1]=2 [2]=3)

[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3

方法三:

列表名=“value0 valuel value2 …”
数组名=($列表名)

示列:

[root@loaclhost shuzu1]#list="1 2 3 4"
[root@loaclhost shuzu1]#arr2=($list)

[root@loaclhost shuzu1]#echo ${arr2[*]}
1 2 3 4

方法四:

数组名[0]=“value”
数组名[1]=“value”
数组名[2]=“value”

示列:

root@loaclhost shuzu1]#arr3[0]="1"
[root@loaclhost shuzu1]#arr3[1]="2"
[root@loaclhost shuzu1]#arr3[2]="3"

[root@loaclhost shuzu1]#echo ${arr3[*]}
1 2 3

2.3 数组包括的数据类型

  • 数值类型
  • 字符类型
  • 使用" "或‘ ’定义

2.4 获取数组长度

[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${#arr1[*]}      #也就是数组中总共多少个元素
5

2.5 读取某下标赋值

[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)

[root@loaclhost shuzu1]#echo ${arr1[0]}      #获取索引为0的元素,即第一个元素
1
[root@loaclhost shuzu1]#echo ${arr1[3]}      #获取索引为3的元素,即第四个元素
4

2.6 数组遍历

[root@loaclhost shuzu1]#vim a.sh
#!/bin/bash
arr5=(1 2 3 4 5)
for i in ${arr5[*]}   或  for i in ${arr5[@]}     #使用*或者@表示都可以
do
  echo $i
done

[root@loaclhost shuzu1]#chmod +x a.sh 
[root@loaclhost shuzu1]#./a.sh           #将数组中的元素列出来就叫数组遍历
1
2
3
4
5

2.7 数组切片

取数组中的某一段的元素的值

格式:

${数组名[@或*]}:起始位置(起始索引):长度

[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]}         #输出整个数组
1 2 3 4 5
[root@loaclhost shuzu1]#
[root@loaclhost shuzu1]#echo ${arr1[*]:0:2}      #这里是从0索引开始获得往后两位元素的值
1 2
[root@loaclhost shuzu1]#echo ${arr1[*]:2:2}      #获取从索引2开始往后的两位元素的值
3 4

2.8 数组替换

临时替换或者永久替换

格式:

$(数组名[@或*]/查找字符/替换字符}

[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]/4/66}     #将数组arr1中的元素4替换为66,这只是临时替换,并不是永久替换
1 2 3 66 5
[root@loaclhost shuzu1]#echo ${arr1[*]}          #原来的数组中的元素的值并不会改变
1 2 3 4 5 

[root@loaclhost shuzu1]#arr1=(${arr1[*]/4/66})    #如果想要永久替换的话,可通过重新赋值实现
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 66 5

[root@test1 opt]# arr1[3]=44
[root@test1 opt]# echo ${arr1[*]}
1 2 3 44 5 6

2.9 删除数组

使用unset删除数组

[root@loaclhost shuzu1]#unset arr1
[root@loaclhost shuzu1]#echo ${arr1[*]}

删除数组中的某个索引对应的元素的值

[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5
[root@loaclhost shuzu1]#unset arr1[2]       #删除索引2的对应的元素值
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 4 5

2.10 追加数组

方法一:

[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5                            #这是原始的数组中的各元素的值,一直到索引4结束
--------------------------------------------------------------------------------
[root@loaclhost shuzu1]#arr1[5]=6          #此时追加数组索引5对应的元素值为6
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5 6                          #发现数组的索引5位置上的元素已经追加成功

方法二:

[root@loaclhost shuzu1]#arr1[${#arr1[*]}]=7      
#当数组的元素非常多的时候,可以直接使用数组的长度作为自己将要追加的索引的值,这样就可以直接追加元素了。
因为原始数组的索引是从0开始的,所以用长度减去1就是原始数组的最后的以为索引值了,
那么自己将要添加的值应该是原始索引值的后一位,那显然就等于数组的长度值了。
[root@loaclhost shuzu1]#echo ${arr1[*]} 
1 2 3 4 5 6 7

方法三:

[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5
---------------------------------------------------------
[root@loaclhost shuzu1]#arr1=("${arr1[@]}" 6 7)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5 6 7

双引号不能省略,否则,当数组arr1中存在包含空格的元素时会按空格将元素拆分成多个。
不能将“@“替换为“*”,如果替换为"*",
不加双引号时与"@"的表现一致,加双引号时,会将数组arr1中的所有元素作为一个元素添加到数组中。
可以简单的理解为:用*号是作为一个整体,而用@还是单个的个体。

$*、$@不加双引号时表现一致;加双引号时,$*会将所有参数作为一个整体。

[root@loaclhost shuzu1]#abc=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${abc[*]}
1 2 3 4 5
[root@loaclhost shuzu1]#abc=(${abc[@]} 6 7 8) #用@可以不加引号
[root@loaclhost shuzu1]#echo ${abc[*]}
1 2 3 4 5 6 7 8
[root@loaclhost shuzu1]#for i in ${abc[*]}
> do
> echo $i
> done
1
2
3
4
5
6
7
8

方法四:

[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5
-----------------------------------------------
[root@loaclhost shuzu1]#arr1+=(11 22)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5 11 22


待添加元素必须用“()"包围起来,并且多个元素用空格分隔

2.11 向函数传数组参数

1、如果将数组变量作为函数参数,函数只会取数组变量的第一个值。如下:
 

[root@loaclhost shuzu1]#vim b.sh 
#!/bin/bash
test1 () {
   echo "接受到的参数列表:$@"
   abc2=$1                 
   echo "新数组的值为:${abc2[*]}" 
}

abc=(3 2 1 4 5)
echo "原始数组的值为:${abc[*]}"
test1 $abc                          #将数组变量作为函数的参数,只会取数组变量的第一个值
------------------------------------------------------------------------------------
[root@loaclhost shuzu1]#chmod +x b.sh 
[root@loaclhost shuzu1]#./b.sh 
原始数组的值为:3 2 1 4 5
接受到的参数列表:3
新数组的值为:3

2、解决这个问题则需要将数组变量的值分解成单个的值,
然后将这些值作为函数参数使用。在函数内部,再将所有的参数重新组合成一个新的数组变量。如下:

$*、$@不加双引号时表现一致;加双引号时,$*会将所有参数作为一个整体。
[root@loaclhost shuzu1]#vim c.sh
#!/bin/bash
test2 () {
  abc1=($(echo $@))                       或者表示为   abc1=(`echo $@`)

  echo "新数组的值为:${abc1[*]}"
}

abc=(`seq 1 10`)
test2 ${abc[*]}                           #将数组的值分解为单个的值
-------------------------------------------------
[root@loaclhost shuzu1]#chmod +x c.sh 
[root@loaclhost shuzu1]#./c.sh 
新数组的值为:1 2 3 4 5 6 7 8 9 10

3、从函数返回数组(调用新数组的元素进行函数运算)

#!/bin/bash
test2 () {
  abc1=(`echo $@`)
  sum=0
  for i in ${abc1[*]}
  do
    sum=$[$sum + $i]
  done
  echo "$sum"
}

abc=(3 2 1 4 5)
test2 ${abc[*]}
--------------------------------------------------------------
[root@loaclhost shuzu1]#chmod +x c.sh 
[root@loaclhost shuzu1]#./c.sh 
15

三、冒泡排序

类似气泡上涌的动作,会将数据在数组中从小到大或者从大到小不断的向前移动。

基本思想:
冒泡排序的基本思想是对比相邻的两个元素值,
如果满足条件就交换元素值,把较小的元素移动到数组前面,
把大的元素移动到数组后面(也就是交换两个元素的位置) ,
这样较小的元素就像气泡一样从底部上升到顶部

算法思路:
冒泡算法由双层循环实现,其中外部循环用于控制排序轮数,
一般为要排序的数组长度减1次,因为最后一次循环只剩下一个数组元素,不需要对比,
同时数组已经完成排序了。而内部循环主要用于对比数组中每个相邻元素的大小,
以确定是否交换位置,对比和交换次数随排序轮数而减少。

在实际应用中,冒泡排序适用于对小规模数据进行排序

示列:


#!/bin/bash
abc=(20 10 60 40 50 30)          #定义一个数组
echo "原数组的排列顺序为:${abc[*]}"
length=${#abc[*]}                #定义原数组的长度为length变量
for ((i=1;i<$length;i++))        #定义排序轮次
do
    echo $i
  for ((k=0;k<$length-i;k++))    #确定第一个元素的索引位置
  do
     first=${abc[$k]}            #定义第一个元素的值
     j=$[$k+1]                   #定义第二个元素的索引号
     second=${abc[$j]}           #定义第二个元素的值
     if [ $first -gt $second ]   #第一个元素和第二个元素比较,如果第一个元素比第二个元素大则互换
     then             
         temp=$first             #把第一个元素的值保存在临时变量temp中
         abc[$k]=$second         #把第二个元素的值赋给第一个元素
         abc[$j]=$temp           #把原第一个元素的值,赋给第二个元素
     fi
  done
done

echo "排序后数组的排列顺序为${abc[*]}"       #输出排序后的数组
---------------------------------------------------------------------------------------
[root@loaclhost shuzu1]#./g.sh 
原数组的排列顺序为:20 10 60 40 50 30
排序后数组的排列顺序为10 20 30 40 50 60

详细动画讲解可看此链接视频:冒泡排序法_哔哩哔哩_bilibili

  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值