检查传递给 Bash 脚本的参数数

Check number of arguments passed to a Bash script

提问人:triple fault 提问时间:9/2/2013 最后编辑:konsoleboxtriple fault 更新时间:3/24/2023 访问量:1125415

问:

我希望我的 Bash 脚本在不满足所需的参数计数时打印错误消息。

我尝试了以下代码:

#!/bin/bash
echo Script name: $0
echo $# arguments 
if [$# -ne 1]; 
    then echo "illegal number of parameters"
fi

由于某种未知原因,我遇到了以下错误:

test: line 4: [2: command not found

我做错了什么?

bash 传递 命令行参数

评论

75赞 Barmar 9/2/2013
你不应该命名你的脚本。这是标准Unix命令的名称,您不想将其隐藏起来。test
28赞 zoska 9/2/2013
在 bash 的 if 语句中,始终使用 '[' ('[[') 或 '(' ('((') 周围的空格。
6赞 Daniel Da Cunha 2/14/2014
要添加到@zoska注释中,您需要在 [ 之前有一个空格,因为它是作为命令实现的,请尝试 'which ['。
1赞 ramkrishna 6/26/2014
下面的链接给出了更好的例子:stackoverflow.com/questions/4341630/...
4赞 user253751 10/18/2014
只要它不在 PATH 上,@Barmar肯定可以命名它吗?test

答:

1494赞 konsolebox 9/2/2013 #1

就像任何其他简单命令一样,或者需要在其参数之间留空格。[ ... ]test

if [ "$#" -ne 1 ]; then
    echo "Illegal number of parameters"
fi

if test "$#" -ne 1; then
    echo "Illegal number of parameters"
fi

建议

在 Bash 中,最好改用,因为它不会对其变量进行单词拆分和路径名扩展,除非它是表达式的一部分,否则引用可能不是必需的。[[ ]]

[[ $# -ne 1 ]]

它还具有其他一些功能,例如未带引号的条件分组、模式匹配(扩展模式匹配)和正则表达式匹配。extglob

以下示例检查参数是否有效。它允许一个或两个参数。

[[ ($# -eq 1 || ($# -eq 2 && $2 == <glob pattern>)) && $1 =~ <regex pattern> ]]

对于纯算术表达式,使用 to some 可能仍然更好,但它们仍然可以与其算术运算符(如 、 、 、 、 )一起使用,或者将表达式作为单个字符串参数:(( ))[[ ]]-eq-ne-lt-le-gt-ge

A=1
[[ 'A + 1' -eq 2 ]] && echo true  ## Prints true.

如果您还需要将其与其他功能结合使用,这应该会有所帮助。[[ ]]

请注意,和 是与 、 、 和 具有相同解析级别的关键字。[[ ]](( ))ifcasewhilefor

此外,正如 Dave 所建议的,错误消息最好发送到 stderr,这样当 stdout 被重定向时,它们就不会被包括在内:

echo "Illegal number of parameters" >&2

退出脚本

当向脚本传递无效参数时,使脚本退出也是合乎逻辑的。ekangas 的评论中已经建议了这一点,但有人编辑了这个答案以将其作为返回值,所以我不妨做对。-1

-1虽然被 Bash 接受为参数,但没有明确记录,也不适合用作常见建议。 也是最正式的值,因为它是在 with 中定义的。大多数工具(如)也会返回无效参数。我也曾经在我的脚本中返回,但最近我不再真正关心,只是在所有错误中使用。但是,让我们放在这里,因为它是最常见的,可能不是特定于操作系统的。exit64sysexits.h#define EX_USAGE 64 /* command line usage error */ls2212

if [[ $# -ne 1 ]]; then
    echo "Illegal number of parameters" >&2
    exit 2
fi

引用

评论

2赞 Leo 5/3/2019
OP:请记住,这只是另一个命令,即 try .[which [
7赞 konsolebox 5/6/2019
@Leo 命令可以内置,也可以不内置。在 bash 中,是内置的,而 是关键字。在一些较旧的 shell 中,甚至不是内置的。在大多数系统中,像这样的命令自然地作为外部命令共存,但内部命令由shell优先处理,除非你绕过或。查看 shell 的文档,了解它们如何评估。注意它们的差异,以及它们在每个外壳中的行为方式如何不同。[[[[[commandexec
1赞 Dave 8/4/2020
最后一点,我建议在退出错误代码之前将错误消息写入 STDERR。这样可以做到:(>&2 echo 'Illegal number of parameters')
2赞 konsolebox 8/4/2020
@Dave 我同意,但子外壳是不必要的。
3赞 konsolebox 3/15/2021
@timgeb 为了保持一致性。如果它不必进行单词拆分和文件名扩展,那么无论其扩展值是否预计不受此类过程的影响,都应将其引用。
109赞 Aleks-Daniel Jakimenko-A. 12/15/2013 #2

如果您正在处理数字,使用算术表达式可能是个好主意。

if (( $# != 1 )); then
    >&2 echo "Illegal number of parameters"
fi

>&2用于将错误消息写入 stderr。

评论

0赞 Max 5/9/2020
在目前的情况下,为什么这可能是一个好主意?考虑到效率、可移植性和其他问题,使用最简单和最普遍理解的语法不是最好吗,即,当这可以很好地完成工作并且不需要花哨的操作时?[ ... ]
3赞 Aleks-Daniel Jakimenko-A. 5/10/2020
@Max算术扩展并不花哨,应该由所有 POSIX shell 实现。但是,语法(没有)不是它的一部分。如果您由于某种原因受到限制,那么您当然可以使用,但请记住,您也不应该使用。我希望您了解这些功能存在的陷阱和原因。但这是一个 Bash 问题,所以我们给出了 Bash 的答案(“根据经验,[[ 用于字符串和文件。如果要比较数字,请使用 ArithmeticExpression“)。$(( ))(( ))$[ ][[ ]][ ]
0赞 Dave 8/4/2020
出现错误时,请始终写入 STDERR。(>&2 echo 'Illegal number of parameters')
1赞 Aleks-Daniel Jakimenko-A. 8/5/2020
@Dave是的。我年轻而愚蠢:)编辑。
47赞 jhvaras 1/7/2014 #3

在 []: !=, =, == ...是字符串比较运算符和 -eq, -gt ...是算术二进制的。

我会使用:

if [ "$#" != "1" ]; then

艺术

if [ $# -eq 1 ]; then

评论

11赞 Martin Tournoij 7/22/2014
==实际上是一个未记录的功能,它恰好适用于 GNU 。它也恰好适用于 FreeBSD ,但可能不适用于 foo唯一的标准比较是(仅供参考)。testtesttest=
2赞 jhvaras 11/18/2014
它记录在 bash man 条目中:当使用 == 和 != 运算符时,运算符右侧的字符串被视为一种模式,并根据下面模式匹配下描述的规则进行匹配。如果启用了 shell 选项 nocasematch,则在不考虑字母字符大小写的情况下执行匹配。如果字符串与模式匹配 (==) 或不匹配 (!=),则返回值为 0,否则返回值为 1。可以引用模式的任何部分以强制将其匹配为字符串。
2赞 gniourf_gniourf 1/5/2017
@jhvaras:这正是 Carpetsmoker 所说的:它可能在某些实现中有效(事实上,它在 Bash 中有效),但它不符合 POSIX。例如,它将失败,并显示 : 。POSIX 仅指定 ,而不指定 。dashdash -c '[ 1 == 1 ]'===
16赞 Dwight Spencer 8/12/2014 #4

一个简单的 一个有效的衬里 可以使用:

[ "$#" -ne 1 ] && ( usage && exit 1 ) || main

这细分为:

  1. 测试 bash 变量的大小参数 $# 不等于 1(我们的子命令数)
  2. 如果为 true,则调用 usage() 函数并以状态 1 退出
  3. 否则调用 main() 函数

注意事项:

  • usage() 可以只是简单的 echo “$0: params”
  • main 可以是一个长脚本

评论

1赞 konsolebox 5/17/2016
如果该行之后有另一组行,那将是错误的,因为它仅适用于子 shell 的上下文,使其与 .在选项解析方面,我不喜欢这种简化方式,但你可以改用它。或者可能只是.exit 1( usage; false ){ usage && exit 1; }{ usage; exit 1; }
1赞 Dwight Spencer 5/18/2016
@konsolebox (usage & exit 1 ) 适用于 ksh、zsh 和 bash 回到 bash 2.0。{...} 语法仅是 bash 的 4.0+ 版本。不要误会我的意思,如果一种方法对你很好,那就使用它,但请记住,并不是每个人都使用与你相同的 bash 实现,我们应该按照 posix 标准而不是 bashisms 进行编码。
0赞 konsolebox 5/20/2016
我不确定你在说什么。 是一种通用语法,并且可用于大多数(如果不是全部)基于 的 shell,即使是那些不遵循 POSIX 标准的旧 shell。{...}sh
37赞 Pat 10/16/2014 #5

如果你只对缺少特定参数的保释感兴趣,那么参数替换是很好的选择:

#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#  Script exits here if command-line parameter absent,
#+ with following error message.
#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT

评论

0赞 Dwight Spencer 4/29/2016
那不是充满了抨击吗?
1赞 konsolebox 5/17/2016
@DwightSpencer 这有关系吗?
0赞 Pat 11/9/2016
如果您有具体问题,我可以@Temak,但链接到的文章比我解释得更好。
2赞 Trinidad 12/25/2021
在这一点上,这只是互联网幽默,当有人专门问一个关于软件的问题(在这种情况下是bash),然后人们抱怨回答问题的回复,但使用使生活更轻松但独有的功能是该软件独有的。再见,我要回到 Google 表格论坛抱怨他们的回复不适用于我的意大利语本地化版本的 Office 95。
-1赞 Fabricio 11/19/2015 #6

您应该在测试条件之间添加空格:

if [ $# -ne 1 ]; 
    then echo "illegal number of parameters"
fi

我希望这会有所帮助。

0赞 IsaacE 11/12/2017 #7

如果你想安全起见,我建议使用 getopts。

下面是一个小例子:

    while getopts "x:c" opt; do
      case $opt in
        c)
          echo "-$opt was triggered, deploy to ci account" >&2
          DEPLOY_CI_ACCT="true"
          ;;
            x)
              echo "-$opt was triggered, Parameter: $OPTARG" >&2 
              CMD_TO_EXEC=${OPTARG}
              ;;
            \?)
              echo "Invalid option: -$OPTARG" >&2 
              Usage
              exit 1
              ;;
            :)
              echo "Option -$OPTARG requires an argument." >&2 
              Usage
              exit 1
              ;;
          esac
        done

在此处查看更多详细信息,例如 http://wiki.bash-hackers.org/howto/getopts_tutorial

评论

0赞 konsolebox 2/23/2022
Getopt[s] 只是为了允许相邻的空头选项而使事情变得复杂。学习进行手动解析。
12赞 jenkizenki 3/7/2019 #8

看看这个 bash 备忘单,它可以有很大帮助。

若要检查传入的参数的长度,请使用"$#"

若要使用传入的参数数组,请使用"$@"

检查长度和迭代的示例如下:

myFunc() {
  if [[ "$#" -gt 0 ]]; then
    for arg in "$@"; do
      echo $arg
    done
  fi
}

myFunc "$@"

这篇文章帮助了我,但对我和我的情况来说缺少一些东西。希望这对某人有所帮助。

评论

1赞 lallolu 9/7/2021
谢谢。你是救命恩人。我的场景是,我在脚本中创建了函数,脚本采用一个参数,该参数在脚本中调用的最后一个函数中使用。再次感谢。
3赞 panticz 4/12/2019 #9

这里有一个简单的行,用于检查是否只给出一个参数,否则退出脚本:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit
1赞 ngenetzky 7/4/2020 #10

这里有很多很好的信息,但我想添加一个我认为有用的简单片段。

它与上面的一些有何不同?

  • 将用法打印到 stderr,这比打印到 stdout 更合适
  • 返回另一个答案中提到的退出代码
  • 不会把它变成一个班轮......
_usage(){
    _echoerr "Usage: $0 <args>"
}

_echoerr(){
    echo "$*" >&2
}

if [ "$#" -eq 0 ]; then # NOTE: May need to customize this conditional
    _usage
    exit 2
fi
main "$@"

1赞 Rukmangathan 3/24/2023 #11
#!/bin/bash

Help() {
  echo "$0 --opt1|-opt1 <opt1 value> --opt2|-opt2 <opt2 value>"
}

OPTIONS=($@)
TOTAL_OPTIONS=$#
INT=0

if [ $TOTAL_OPTIONS -gt 4 ]
then
        echo "Invalid number of arguments"
        Help
        exit 1
fi

while [ $TOTAL_OPTIONS -gt $INT ]
do
        case ${OPTIONS[$INT]} in

                --opt1 | -opt1)
                        INT=`expr $INT + 1`
                        opt1_value=${OPTIONS[$INT]}
                        echo "OPT1 = $opt1_value"
                        ;;

                --opt2 | -opt2)
                        INT=`expr $INT + 1`
                        opt2_value=${OPTIONS[$INT]}
                        echo "OPT2 = $opt2_value"
                        ;;

                --help | -help | -h)
                        Help
                        exit 0
                        ;;

                *)
                        echo "Invalid Option - ${OPTIONS[$INT]}"
                        exit 1
                        ;;

        esac
        INT=`expr $INT + 1`
done

这就是我的使用方式,它的工作没有任何问题

[root@localhost ~]# ./cla.sh -opt1 test --opt2 test2
OPT1 = test
OPT2 = test2