如何编写一个有或没有参数的脚本?


13

我有一个bash脚本,它像这样:

#!/bin/bash

if [ $1 = "--test" ] || [ $1 = "-t" ]; then
    echo "Testing..."
    testing="y"

else
    testing="n"
    echo "Not testing."

fi

因此,我想做的不仅是使用./script --test或来运行它./script -t,而且还不使用任何参数(只是./script)来运行它,而且如果我使用当前代码来执行该输出,则输出看起来就是:

./script: line 3: [: =: unary operator expected
./script: line 3: [: =: unary operator expected
Not testing.

那么,我该如何编程以使其完全不带任何参数地运行else而不会抛出错误呢?我究竟做错了什么?


1
您需要在支票两边加双括号。[[]]
Terrance


@Terrance 不需要它们,尽管如果以bash为目标,则应使用它们。
Ruslan

Answers:


1

在shell脚本中使用长选项的“正确方法”是通过getopt GNU Utility。还有一个内置的bash getopts,但是它只允许使用简短的选项,例如-tgetopt可以在此处找到一些用法示例。

这是一个脚本,演示了我将如何处理您的问题。大多数步骤的说明已添加为脚本本身中的注释。

#!/bin/bash

# GNU getopt allows long options. Letters in -o correspond to
# comma-separated list given in --long.

opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened

set -- $opts # some would prefer using eval set -- "$opts"
# if theres -- as first argument, the script is called without
# option flags
if [ "$1" = "--"  ]; then
    echo "Not testing"
    testing="n"
    # Here we exit, and avoid ever getting to argument parsing loop
    # A more practical case would be to call a function here
    # that performs for no options case
    exit 1
fi

# Although this question asks only for one 
# option flag, the proper use of getopt is with while loop
# That's why it's done so - to show proper form.
while true; do
    case "$1" in
        # spaces are important in the case definition
        -t | --test ) testing="y"; echo "Testing" ;;
    esac
    # Loop will terminate if there's no more  
    # positional parameters to shift
    shift  || break
done

echo "Out of loop"

通过一些简化和删除的注释,可以将其简化为:

#!/bin/bash
opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened
set -- $opts    
case "$1" in
    -t | --test ) testing="y"; echo "Testing";;
    --) testing="n"; echo "Not testing";;
esac

16

几种方法;最明显的两个是:

  • $1在双引号:if [ "$1" = "--test" ]
  • 使用$#检查参数数量

更好的是,使用getopts。


2
+1以使用getopts。那是最好的方法。
塞斯

3
另一个常见的习惯用法是[ "x$1" = "x--test" ]再次捍卫命令行参数,这些参数是该[命令的有效运算符。(这是为什么[[ ]]会首选它的另一个原因:它是shell语法的一部分,而不仅仅是内置命令。)
Peter Cordes

7

您需要在if条件中引用变量。更换:

if [ $1 = "--test" ] || [ $1 = "-t" ]; then

与:

if [ "$1" = '--test' ] || [ "$1" = '-t' ]; then  

然后它将起作用:

➜〜
. /test.sh 不进行测试。

始终总是用双引号括住您的变量!


单引号是必不可少的还是可以使用双引号?

确切的答案取决于您使用的是哪个shell,但是根据您的问题,我假设您正在使用Bourne-shell后代。单引号和双引号之间的主要区别是变量扩展发生在双引号内,而不是单引号内:$ FOO = bar $ echo“ $ FOO” bar $ echo'$ FOO'$ FOO(hmm .... can' t格式代码中的注释...)
JayEye

@ParanoidPanda您不必使用单引号,但是,在字符串文字上使用它们是一个好习惯。这样,如果您的字符串数据中包含bash想要扩展的符号,您以后就不会感到惊讶。
塞斯

@ParanoidPanda @JayEye说的是:用单引号不会将变量扩展foo=bar echo '$foo'打印$foo到输出,而是foo=bar echo "$foo"打印bar
EKons,2016年

4

您正在使用bash。Bash有一个很好的选择[[[。使用[[,您不必担心是否要报价,并且操作符比得多[,包括对以下内容的适当支持||

if [[ $1 = "--test" || $1 = "-t" ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

或者,您可以使用正则表达式:

if [[ $1 =~ ^(--test|-t) ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

当然,应该始终进行正确的报价,但是[在使用bash时没有理由坚持使用。


3

测试参数是否存在(通常,并相应地执行操作),这应该起作用:

#!/bin/bash

check=$1

if [ -z "$check" ]; then
  echo "no arguments"
else echo "there was an argument"
fi

为什么不[ -z "$1" ]呢?
EKons,2016年

@ΈρικΚωωσταντόπουλος在这种情况下,可以肯定的是,通常在脚本中,我会一次调用该变量,并在过程中对其进行引用,通常会多次引用该变量。决定将协议保留在样本中。
雅各布·弗利姆

在此示例中,您似乎$check在任何shifts 之前使用了一次,因此最好$1改用。
EKons,2016年

@ΈρικΚωωσταντόπουλος当然,如上所述,它不是示例,可以在脚本中使用。在脚本中,一次又一次地重复执行此操作是不好的做法。
雅各布·弗利姆

我理解您的意思,但是更好的做法是#!在提供示例时不要使用shebang (您应该在代码外部描述shell)。
EKons,2016年
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.