Shell编程中关于向函数传递数组参数的解读

435 阅读3分钟

最近在学习《Linux命令行和shell脚本编程大全》(第四版)这本书,对于自己遇到的问题以及通过搜索引擎和书籍中的解决方案进行一个案例的剖析,希望对于像我这样的初学者,有一个帮助。

1、 背景

看过17.4《数组与变量》一节,其实很容易感到混乱,对于参数传递数组,会有不同的写法,例如:

 ###1
 echo "The parameters are: $@"
 ###2
 thisarray=$1
 ###3
 newarray=(`echo "$@"`)
 ###4
 myarray=(1 2 3 4 5)
 testit ${myarray[*]}
 ###5
 myarray=(1 2 3 4 5)
 arg1=$(echo ${myarray[*]})
 result=$(addarray $arg1)
 ###6
 origarray=($(echo "$@"))

这一节当中出现了如此多的对于参数的处理方式,让像我一样的初学者看到这里感到很迷惑,究竟应该怎么写?哪个是对的?为什么用echo,是什么意思?。。。大大的脑袋里有很多的问号。

2、 数组的基本引用方式

那么先让我们抛弃上面的不同的写法,回归到最本质的东西:给定一个数组arr=(1 2 3 4 5 6) ,如何对该数组进行变量的传递呢?

格式如下:

 ${arr[*]}

如何理解这个语法呢?结合python对于数组的切片处理来设想,arr[*] 中的*表示所有,即对数组arr进行所有元素的切片,而最后的结果其实是可以理解成将数组“剥去了外壳”,如:1 2 3 4 5 6。而如果明白了这个,接下来就容易理解了。

3、 传参数组的原理解析

以下通过例子来说明传参数组遇到的问题以及原因:

第一、关于$1 的问题

 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# cat fun_array.sh 
 #!/bin/bash
 function pro_arr {
     echo "The parameter array is :$1"
 }
 arr=(1 2 4 6 8 34 54)
 pro_arr ${arr[*]}    ####位点1
 # 结果
 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# sh fun_array.sh 
 The parameter array is :1

我们看脚本中位点1 ,我们对函数pro_arr 传参了 ${arr[*]} ,即传参了1 2 4 6 8 34 54 ,根据IFS 默认的分隔符空格,所以,这里的 $1 表示第一个参数,但最后的结果仅提取了列表的第一个元素 1 。如果想要传递整个数组,可以用""包裹的方式来传参,如下:

 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# cat fun_array.sh 
 #!/bin/bash
 function pro_arr {
     echo "The parameter array is :$1"
 }
 arr=(1 2 4 6 8 34 54)
 pro_arr "${arr[*]}"    ####位点2
 # 结果
 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# sh fun_array.sh 
 The parameter array is :1 2 4 6 8 34 54

这里的位点2 将传参的数组用""包裹了起来,表示将整个参数当成一个字符串,这样内部的分隔符IFS无法对字符串内的空格起作用了,达到了传递整个数组的目的。

第二、关于$@的问题

$@表示获取脚本的所有参数,而以下的例子:

 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# cat fun_array.sh 
 #!/bin/bash
 function pro_arr {
     echo "The parameter array is :$@"
 }
 arr=(1 2 4 6 8 34 54)
 pro_arr ${arr[*]}
 # 结果
 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# sh fun_array.sh 
 The parameter array is :1 2 4 6 8 34 54

$@ 其实是取脚本所传递的所有参数,而这里由于只向函数传递了1个参数并且该参数是数组,因此在这种特定情况下也可以取传递的数组参数。

第三、为何在有newarray=(`echo "$@"`)arg1=$(echo ${myarray[*]}) 差异

讲清楚这里的区别,其实我们还是要先回归到基础知识点上,关于命令输出赋给变量的两种方式:反引号(`)和 $()格式

当有命令的输出的时候,需要用反引号(`)或者$() 的格式来赋值,如下:

 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# cat param.sh 
 #!/bin/bash
 arg1=`date`
 echo "arg1's value is $arg1"
 arg2=$(echo "$#")
 arg3=$1
 echo "The script has $arg2 parameters and the first parameter is $arg3."
 #  结果
 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# sh param.sh "hello world"
 arg1's value is Tue Aug 27 10:25:28 AM CST 2024
 The script has 1 parameters and the first parameter is hello world.

所以题目中的newarrayarg1 中使用了反引号或者$() 来将命令输出赋值给变量。但是二者又有不同,如果是单单赋值,那么newarray=(`echo "$@"`) 是不需要反引号外的括号的,而这个括号其实是将数组写成(n1 n2 n3 n4 n5 ...)的形式,而后者arg1=$(echo ${myarray[*]}) 是将数组写成n1 n2 n3 n4 n5 ...的形式,如下:

对函数传参数 $arg2形式:

 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# cat fun_array.sh 
 #!/bin/bash
 function pro_arr {
     echo "The parameter array is :$@"
     r_arr="$@"
     echo "The array is:${r_arr[*]}"
     new_arr=("$@")
     echo "The array is:${new_arr[*]}"
 }
 arr=(1 2 4 6 8 34 54)
 arg2=$(echo ${arr[*]})
 echo "$arg2 is $arg2."
 pro_arr $arg2

对函数传参数字符串形式:

 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# cat fun_array.sh 
 #!/bin/bash
 function pro_arr {
     echo "The parameter array is :$@"
     r_arr="$@"
     echo "The array is:${r_arr[*]}"
     new_arr=("$@")
     echo "The array is:${new_arr[*]}"
 }
 arr=(1 2 4 6 8 34 54)
 arg2=$(echo ${arr[*]})
 echo "$arg2 is $arg2."
 # pro_arr $arg2
 pro_arr "${arr[*]}"

而不论传参字符串还是数组参数,那么结果都是一样的:

 [root@iZuf6gxtsgxni1r88kx9rtZ linux_cmd]# sh fun_array.sh 
 $arg2 is 1 2 4 6 8 34 54.
 The parameter array is :1 2 4 6 8 34 54
 The array is:1 2 4 6 8 34 54
 The array is:1 2 4 6 8 34 54

所以,能够得出结论,在引用形如数组的参数时,其外层的() 的作用是无效的,至少在我的linux版本中是这样的。

综上所述,我的建议,对于书中的内容应该删繁就简一些来对待,比如:

 newarray=(`echo "$@"`)
 # 调整为
 newarray=`echo "$@"`
 ##——————————————————————————————
 arg1=$(echo ${myarray[*]})
 testit $arg1
 # 调整为
 testit "${myarray[*]}"

有的时候简单即是最佳,当然书也不代表100%正确。