Linux——shell脚本的传参方式

前言

对于逻辑较为复杂的shell脚本来说,脚本的执行逻辑需要结合我们传入的参数来解析运行。本文将对shell脚本中传参方式进行介绍,内容不多,希望对各位读者有所帮助。

方式一:直接在脚本后面输入参数值,多个参数间用空格隔开

这种方式需要和$@$n这些特殊变量相结合来使用,具体做法可以参考我的这篇文章:linux——shell脚本中关于$和特殊变量的使用细节

下面我们用一个小案例来进行演示,我们希望脚本能将我们传入的所有变量都进行打印输出:

#!/bin/bash

echo '------------'
for var in "$@"
do
    echo "this param value is $var"
done

执行脚本,脚本中传入了三个参数

 sh test.sh aa bb cc

结果如下,可以看到我们的入参都被完整打印出来了:

------------
this param value is aa
this param value is bb
this param value is cc
------------`

方式二:使用getopts命令来获取参数

getopts是一个解析脚本选项参数的工具,也是shell 内置的命令,一般用在循环中。每当执行循环是,getopts都会检查下一个命令选项,如果这些选项出现在option中,则表示是合法选项,否则不是合法选项。并将这些合法选项保存在VARIABLE这个变量中。

语法:getopts optstring argName 【optstring : [option[:]] [DESCPRITION]】
(1)optstring:表示为某个脚本可以使用的选项,比如"abc",说明一共支持abc三个选项
(2)若optstring前面存在单个冒号:,则表示是静默模式,忽略一般错误信息
(3)::如果某个选项(option)后面出现了单个冒号(":"),则表示这个选项后面必须要加上参数。选项和参数之间可以不用空格分隔,当然了建议脚本输入的时候还是有空格分隔会好一点。
(4)argName:表示将某个选项保存在变量argName中

getopts的内置变量
变量名 含义
OPTARG 就是将选项后面的参数(或者描述信息DESCPRITION)保存在这个变量当中。
OPTIND 这个表示命令行的下一个选项或参数的索引(文件名不算选项或参数)
小案例一:getops初使用和静默模式的应用

语法说的有点多,我们不妨先用一个例子来演示一下,以下脚本的主要逻辑是,通过getops语法来获取用户传入的所有参数,且我们预定的参数需要在abc三者之间存在,对于匹配到的选项,我们会对其参数进行打印,顺便打印一下下一个参数的位置。

#!/bin/bash
echo '------------'
while getopts "ab:c:" optname
     do
        case "$optname" in
            "a")
            echo "current opt idx is $OPTIND"
            echo "opt a is catched"
            ;;
            "b")
            echo "current opt idx is $OPTIND"
            echo "opt b is catched.param is $OPTARG"
            ;;
            "c")
            echo "current opt idx is $OPTIND"
            echo "opt c is catched.param is $OPTARG"
            ;;
            "*")
            echo "current opt idx is $OPTIND"
            echo "unknow opt name"
            ;;
        esac
    done
echo '------------'
执行脚本sh test.sh -asdf,结果如下:

可以看到,第一个参数a可以成功被识别出来,后续的sdf虽然连在一起,但还是被识别成sdf这几个不同的选项,又因为这几个选项不再我们定义的脚本范围内,所以直接报了错误出来。

------------
current opt idx is 1
opt a is catched
test.sh: illegal option -- s
test.sh: illegal option -- d
test.sh: illegal option -- f
------------

我们把原来的ab:c:改为:ab:c:,再执行一次脚本sh test.sh -asdf,结果如下。我们可以发现由于我们在optstring前面加上了冒号,所以因参数不合法的错误就不会再展示出来了。

------------
current opt idx is 1
opt a is catched
------------
案例二:选项和参数的正确传入

我们这里还是用上个案例的脚本内容

#!/bin/bash
echo '------------'
while getopts "ab:c:" optname
     do
        case "$optname" in
            "a")
            echo "current opt idx is $OPTIND"
            echo "opt a is catched"
            ;;
            "b")
            echo "current opt idx is $OPTIND"
            echo "opt b is catched.param is $OPTARG"
            ;;
            "c")
            echo "current opt idx is $OPTIND"
            echo "opt c is catched.param is $OPTARG"
            ;;
            "*")
            echo "current opt idx is $OPTIND"
            echo "unknow opt name"
            ;;
        esac
    done
echo '------------'

执行脚本sh test.sh a -b 1 -c 2,执行结果如下。可以看到,若首个选项前面没有加-,后面的选项无法被正常解析

------------
------------

执行脚本sh test.sh -a -b 1 -c 2,执行结果如下。可以看到,现在参数可以被正常打印出来了

------------
current opt idx is 2
opt a is catched
current opt idx is 4
opt b is catched.param is 1
current opt idx is 6
opt c is catched.param is 2
------------

执行脚本sh test.sh -a1 -b2 -c3,结果如下。可以看到,选项和参数之前没有空格,也是可以正常被解析出来的。若选项为无需参数的选项,则会自动把后面跟着的参数识别为选项进行校验。

------------
current opt idx is 1
opt a is catched
test.sh: illegal option -- 1
current opt idx is 3
opt b is catched.param is 2
current opt idx is 4
opt c is catched.param is 3
------------

方式三:使用getopt进行传参

getopt和方式二提到的getopts关键字咋一看挺像的,但两个关键字还是有区别的。

  • 区别1:getopts是bash内置的命令,而getopt是一个外部的命令,不同的linux版本的通常会自带
  • 区分2:getopts不支持长命令(例如:--list这种语法),而getopt支持,且后者支持更加复杂的语法。
简写 全写 说明
-a --alternative 允许长选项以 - 开始
-h --help 帮助,用法指南
-l --longoptions <长选项> 要识别的长选项
-n --name <程序名> 将错误报告给的程序名
-o --options <选项字符串> 要识别的短选项
-q --quiet 禁止 getopt(3) 的错误报告
-Q --quiet-output 无正常输出
-s --shell <shell> 设置 shell 引用规则
小案例

getopt的用法稍微要比getopts复杂一些,我们做一个小案例,脚本中定义六个选项,其中a b c为短选项,test1 test2 test3为长选项,且a,test1不需要参数,b,test2需要参数,c,test3有一个可选参数。
我们在脚本中把输入的参数进行打印输出。

echo "origin param is $@"
# -o或者-option 后面跟着的是短选项; --long后面跟着的是长选项,选项名之间使用逗号隔开
# 选项后面不加":"表示不需要参数,选项后面有一个":"表示必须有参数,选项后面有2个":"表示后面跟着一个可选选项
# -n 选项后面跟着解析错误时提示的脚本名称
preExecute=`getopt -o ab:c:: --long test1,test2:,test3:: -n "$0" -- "$@"`
if [ $? != 0 ]; then
    echo "脚本解析失败..."
    exit 1
fi

echo  "preExecute=$preExecute"

# set命令将规范化后的命令参数分配到位置参数 $1 $2上面
eval set -- "${preExecute}"
echo "formatetd parameters=[$@]"

while true
do
    case "$1" in
        -a | --test1)
        echo "option is a or test1"
        shift
        ;;
        -b | --test2)
        echo "option is b or test2,and param is $2"
        shift 2
        ;;
        -c | --test3)
            case "$2" in
            "")
                echo "option c or test3, and not param"
                shift 1
                ;;
            *)
                echo "option c or test3, param is $2"
                shift 2
                ;;
            esac
            ;;
        --)
            echo "shift"
            shift
            break;
            ;;
        *)
            echo "Internal error!!!"
            exit 1
            ;;
    esac
done
echo "====================="

这里再解释一下脚本的一些比较特别的语法。

  • set 作用
    要在脚本中使用getopt。首先,要用getopt命令生成格式化后的版本来替换已有的命令行选项和参数。需要用到set命令。set命令能够处理shell中的各种变量。set命令的双破折线(--)选项,效果是将命令行参数替换成set命令的参数值。$@符号是代表输入参数字符串。

  • shift作用
    shift的特性是将参数向前挪移一步,比如$1:a $2:b $3:c,此时shift 1,则$1:b $2:c

脚本执行结果如下,可以看到脚本的参数都可以正常的打印出来

[root@10-60-159-92 testssh]# sh test.sh -a -b 'hello' -cword --test1 --test2=hello --test3=word
=====================
origin param is -a -b hello -cword --test1 --test2=hello --test3=word
preExecute= -a -b 'hello' -c 'word' --test1 --test2 'hello' --test3 'word' --
formatetd parameters=[-a -b hello -c word --test1 --test2 hello --test3 word --]
option is a or test1
option is b or test2,and param is hello
option c or test3, param is word
option is a or test1
option is b or test2,and param is hello
option c or test3, param is word
shift
=====================

个人感觉,如果脚本不需要比较复杂的逻辑的话,建议使用getopt命令就行了~

总结——三种方式的优缺点

个人觉得三种传参的方式各有优点
(1)方式一简单易用,但灵活性不足,但个人觉得使用方式一的前提是脚本的执行人员本身很熟悉脚本的内容,不然很容易出现传参错误、遗漏的问题。同时对于参数传递的顺序不可以轻易的变化,否则也容易有问题。
(2)方式二执行时需要手动指定参数。虽然繁琐了一点但入参清晰,脚本的维护性会更好,而且语法也比较好学,个人比较推荐使用这种方式来传参
(3)方式三功能最强大,但语法相对麻烦一点,如果是复杂的脚本逻辑,推荐使用方式三来解决,如果不复杂,方式二可能会是更好的选择。

参考文章

Shell系统学习之向Shell脚本传递参数:https://blog.csdn.net/MashiMaroJ/article/details/125583142
执行shell脚本传递参数的方式:https://www.jianshu.com/p/cee95826fab1

posted @ 2023-06-24 16:17  moutory  阅读(2733)  评论(0编辑  收藏  举报  来源