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


726

如果不满足所需的参数计数,我希望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

我究竟做错了什么?


54
您不应该为脚本命名test。这是标准Unix命令的名称,您不想对其进行阴影处理。
巴玛(Barmar)2013年

20
总是使用空格周围'['( '[[')或'('( '((')的if语句在bash。
zoska

5
要在@zoska注释中添加,您需要在[之前加一个空格,因为它是作为命令实现的,请尝试使用'where ['。
Daniel Da Cunha 2014年

1
:更好的例子是在下面的链接给出 stackoverflow.com/questions/4341630/...
拉玛克里斯纳

3
@Barmar肯定会命名test,只要它不在PATH上就可以了吗?
user253751

Answers:


1098

就像任何其他简单命令一样,[ ... ]或者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> ]]

对于纯算术表达式,使用(( ))一些仍可能会更好,但它们仍然可能在[[ ]]与它的算术运算符喜欢-eq-ne-lt-le-gt,或-ge通过将表达为一个字符串参数:

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

如果您还需要将其与其他功能结合使用,那将很有帮助[[ ]]

退出脚本

当传递无效参数时使脚本退出也是合乎逻辑的。这已经提出的意见通过ekangas但有人编辑这个答案与把它-1作为返回值,所以我还不如做是正确的。

-1尽管没有被Bash接受为Bash的参数,但exit没有明确记录,也没有权利用作常见建议。 64也是最正式的值,因为它是在sysexits.hwith中定义的#define EX_USAGE 64 /* command line usage error */。大多数类似的工具ls也会返回2无效的参数。我也曾经返回2过自己的脚本,但是最近我不再真正在意它,只使用1了所有错误。但让我们放在2这里,因为它是最常见的,并且可能不是特定于操作系统的。

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

参考文献


2
OP:请记住,这[只是另一个命令,即try which [
狮子座

5
@Leo命令可以是内置的,也可以不是。在bash中,[是内置[[函数,而是关键字。在某些较旧的shell中,[甚至没有内置。[在大多数系统中,类似命令之类的命令自然可以与外部命令共存,但是内部命令由Shell优先处理,除非您使用command或跳过exec。检查外壳的文档,了解它们如何评估。注意它们之间的差异,以及它们在每个外壳中的行为如何不同。
konsolebox

77

如果要处理数字,最好使用算术表达式

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

在当前情况下,为什么这是个好主意?考虑到效率,可移植性和其他问题,难道不是最好使用最简单,最易理解的语法[ ... ]吗,即,当这项工作很好且不需要花哨的操作时?
最多

@Max算术扩展$(( ))不是花哨的,应该由所有POSIX shell实现。但是,(( ))语法(不带$)不是其中的一部分。如果由于某种原因而受到限制,那么可以肯定地可以使用它[ ],但是请记住,您也不要使用[[ ]]。希望您了解[ ]这些功能存在的陷阱和原因。但这是一个Bash问题,因此我们给出了Bash答案(“根据经验,[[用于字符串和文件。如果要比较数字,请使用ArithmeticExpression”)。
Aleks-Daniel Jakimenko-A。

39

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

我会用:

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

要么:

if [ $# -eq 1 ]; then

9
==实际上是一个未公开的特性,这恰好与GNU工作test。这也恰好与FreeBSD的工作test,但可能无法在正常工作FOO test。在唯一的标准比较=(仅供参考)。
马丁·图尔诺伊

1
它记录在bash man条目上: 使用==和!=运算符时,该运算符右边的字符串被视为一种模式,并根据以下“模式匹配”下所述的规则进行匹配。如果启用了shell选项nocasematch,则执行匹配时将不考虑字母字符的大小写。如果字符串匹配(==)或不匹配(!=)模式,则返回值为0,否则返回1。可以引用模式的任何部分以强制将其匹配为字符串。
jhvaras

2
@jhvaras:这就是Carpetsmoker所说的:它可能在某些实现中有效(实际上,它在Bash中有效),但是不兼容POSIX。例如,它会失败dashdash -c '[ 1 == 1 ]'。POSIX仅指定=,而不指定==
gniourf_gniourf

34

如果只对缺少特定参数的情况感兴趣,那么“ 参数替换”非常有用:

#!/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

难道不是充满了bashisms吗?
德怀特·斯宾塞

@DwightSpencer会重要吗?
konsolebox '16

@Temak如果您有特定问题,我可以,但是链接到的文章比我能更好地解释它。
Pat

13

一个简单的班轮可以使用以下方法完成:

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

分解为:

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

认为要注意:

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

1
如果您在该行之后还有另一行行,那将是错误的,因为exit 1它将仅适用于subshel​​l的上下文,使其与成为同义词( usage; false )。对于选项解析,我不喜欢这种简化方式,但是您可以使用它{ usage && exit 1; }。或者可能只是{ usage; exit 1; }
konsolebox '16

1
@konsolebox(用法&&出口1)适用于ksh,zsh和bash,可回溯至bash 2.0。{...}语法仅适用于bash 4.0+。如果一种方法适合您,然后使用它,请不要误会我的意思,但请记住,并非每个人都使用与您相同的bash实现,我们应该编写代码以posix标准而非bashisms。
德怀特·斯潘塞

我不确定你在说什么。{...}是一种通用语法,即使不是基于所有的Shell sh(即使不是基于POSIX标准的那些较旧的Shell)也可用于大多数(即使不是全部)shell 。
konsolebox

7

查看 bash速查表,它可以提供很多帮助。

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

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

检查长度并进行迭代的示例为:

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

myFunc "$@"

这篇文章对我有所帮助,但对我和我的处境却缺少一些东西。希望这对某人有帮助。


0

为了安全起见,我建议使用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

这里是一个简单的衬里,用于检查是否仅给出了一个参数,否则退出脚本:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit

-1

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

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

我希望这有帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.