如何编写一个Bash脚本,以接受可选的输入参数?

752

我希望我的脚本能够接受一个可选的输入参数,

例如,当前我的脚本是这样的:

#!/bin/bash
somecommand foo

但我希望它说:

#!/bin/bash
somecommand  [ if $1 exists, $1, else, foo ]

3
请参见http://unix.stackexchange.com/questions/122845/using-a-b-for-variable-assignment-in-scripts。 - Vadzim
2
...和https://dev59.com/knI-5IYBdhLWcg3wHUZB - Vadzim
我想说的是,这个主题不是关于可选参数,而是一个带默认值的位置参数。这个术语会让人非常困惑。“可选参数”意味着无论命令行中是否存在这些参数都可以。 - Joonho Park
10个回答

1066
您可以使用默认值语法:
somecommand ${1:-foo}

上述内容,如Bash参考手册-3.5.3 Shell参数扩展所述: 如果参数未设置或为空,则替换为单词的扩展。否则,将替换参数值。 如果您只想在参数未设置时替换默认值(但不是null,例如不是空字符串),请改用此语法:
somecommand ${1-foo}

来自Bash参考手册-3.5.3 Shell参数扩展:

省略冒号只会检测未设置的参数。换句话说,如果包含了冒号,该运算符测试参数的存在性和其值不为空;如果省略了冒号,则该运算符仅测试参数的存在性。


77
请注意上述命令 "如果$1未设置或为空字符串,则返回foo" 和 ${1-foo} 之间的语义差异,"${1-foo}" 的意思是 "如果 $1 未设置,则返回 foo"。 - l0b0
17
你能解释一下为什么这个方法有效吗?特别是,':'和'-'的作用/目的是什么? - jwien001
11
在上面提交的答案中,使用了替换操作符来返回默认值,如果变量未定义。具体来说,逻辑是“如果$1存在且不为空,则返回其值;否则,返回foo。” 冒号是可选的。如果省略,将“存在且不为空”更改为仅“存在”。减号指定返回foo而不将$1设置为'foo'。替换操作符是扩展操作符的一个子类。请参阅罗宾斯和比比的《经典Shell脚本编程》[O'Reilly](http://shop.oreilly.com/product/9780596005955.do)第6.1.2.1节。 - Jubbles
6
@Jubbles,如果你不想为了一个简单的参考购买整本书......可以参考这个链接:http://www.tldp.org/LDP/abs/html/parameter-substitution.html。该网页详细介绍了参数替换的相关知识。 - Ohad Schneider
3
如果这个回答能够展示如何将默认设置为运行命令的结果,就会更好,就像@hagen所做的那样(尽管那个回答不太优雅)。 - sautedman
显示剩余4条评论

539

您可以这样为变量设置默认值:

somecommand.sh

#!/usr/bin/env bash

ARG1=${1:-foo}
ARG2=${2:-'bar is'}
ARG3=${3:-1}
ARG4=${4:-$(date)}

echo "$ARG1"
echo "$ARG2"
echo "$ARG3"
echo "$ARG4"

以下是一些示例,演示如何运作:

$ ./somecommand.sh
foo
bar is
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh ez
ez
bar is
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh able was i
able
was
i
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh "able was i"
able was i
bar is
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh "able was i" super
able was i
super
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh "" "super duper"
foo
super duper
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh "" "super duper" hi you
foo
super duper
hi
you

8
好的,我会尽力进行翻译。 "啊,好的。"这个横杠让我困惑了(它是否是否定的意思?)。 - khatchad
16
不是的-那只是bash赋值的一种奇怪方式。我会添加一些更多的例子以便更加清晰地解释这个问题...谢谢! - Brad Parks
看起来这是bash参数的限制。使用Python术语,这不是可选的,而是位置性的。它只是一个带有默认值的位置参数。在bash中真的没有可选参数吗? - Joonho Park
你可以使用getopts来获取你想要的内容 - 这里有两个stackoverflow答案,详细介绍了dfeault valuesinside a function - Brad Parks
请注意,为了更加混淆(在JSON/Python字典中),任何地方都不允许有空格,例如在冒号后面和/或破折号之前/之后...这里的最小惊奇原则在哪里? :) - mirekphd
好观点 @mirekphd - 我添加了一个示例,展示如何在默认值中放置空格。如果您想要字面值,可以使用单引号,如果您想要插值变量,则可以使用双引号。 - Brad Parks

66
if [ ! -z $1 ] 
then 
    : # $1 was given
else
    : # $1 was not given
fi

2
从技术上讲,如果你传入一个空字符串''作为参数,那可能会被计算在内,但你的检查会忽略它。在这种情况下,$#将会显示有多少个参数被给出。 - vmpstr
30
-n! -z是相同的意思。 - l0b0
1
我使用“-n”和“!-z”得到了不同的结果,因此我认为这里不是这种情况。 - Eliezer
6
由于您未引用变量,[ -n $1 ]将始终为真。如果您使用的是bash,则[[ -n $1 ]]会按预期执行,否则您必须引用 [ -n "$1" ] - glenn jackman
3
对于像我这样的人:在代码中忘记冒号是不必要的,可以用你真正的指令替换它!请注意,这句话是翻译内容,不包括解释或其他任何信息。 - Anh-Thi DINH

32

你可以用 $# 来检查参数数量。

#!/bin/bash
if [ $# -ge 1 ]
then
    $1
else
    foo
fi

12

请不要忘记,如果它是变量 $1 .. $n,您需要将其写入常规变量以使用替换。

#!/bin/bash
NOW=$1
echo  ${NOW:-$(date +"%Y-%m-%d")}

2
Brad在上面的回答证明了参数变量也可以在没有中间变量的情况下被替换。 - Vadzim
2
+1 表示记录如何使用像 date 这样的命令作为默认值而不是固定值。这也是可能的:DAY=${1:-$(date +%F -d "yesterday")} - Garren S

9
这允许为可选的第一个参数设置默认值,并保留多个参数。
 > cat mosh.sh
   set -- ${1:-xyz} ${@:2:$#} ; echo $*    
 > mosh.sh
   xyz
 > mosh.sh  1 2 3
   1 2 3 

5

对于可选的多个参数,类比于可以接受一个或多个文件或默认列出当前目录中所有内容的ls命令:

if [ $# -ge 1 ]
then
    files="$@"
else
    files=*
fi
for f in $files
do
    echo "found $f"
done

遗憾的是,对于路径中带有空格的文件,它不能正常工作。我还没有想出如何让它正常工作。


4

我们可以使用变量替换来将一个固定值或命令(例如date)替换为一个参数。迄今为止,答案都集中在固定值上,但这是我用来使日期成为可选参数的方法:

~$ sh co.sh
2017-01-05

~$ sh co.sh 2017-01-04
2017-01-04

~$ cat co.sh

DAY=${1:-$(date +%F -d "yesterday")}
echo $DAY

1
while getopts a: flag
do
    case "$flag" in
        a) arg1=${OPTARG};;
    esac
done

echo "Print optional argument: $arg1"

if [[ -z "$arg1" ]]; then
    ARG=DEFAULT_VAL
else
    ARG=$arg1
fi  

#Run using below command (eg: file name : runscript.sh)
bash runscript.sh -a argument_val &  

-1

当你使用 ${1: } 时,你可以捕获传递给函数的第一个参数(1),或者(:)你可以捕获一个空格作为默认值。

例如,为了能够使用 Laravel artisan,我将以下内容放入我的 .bash_aliases 文件中:

artisan() {
    docker exec -it **container_name** php artisan ${1: } ${2: } ${3: } ${4: } ${5: } ${6: } ${7: }
}  

现在,我只需要在命令行中输入:

  • artisan -- 所有参数(${1: } ${2: } ${3: } ${4: } ${5: } ${6: } ${7: })都将是空格
  • artisan cache:clear -- 第一个参数(${1: })将是cache:clear,其他所有参数都将是空格

因此,在这种情况下,我可以选择传递7个参数。

希望这能帮助到某些人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接