传递给源脚本的参数是错误的

传递给源脚本的参数是错误的

AIX 上的一个脚本,用于检查传递的参数数量并在参数不正确时发出警告:-

if [ "$#" -ge 1 ]; then
  ...
else
  echo 'Usage: myscript <a> [b] [c]'
fi

该脚本设置了一些环境变量,以便获取它。我的意思是我在命令行上输入以下内容。没有第二个脚本,我不是从另一个脚本中获取脚本。

. ./myscript.sh

如果我不向它传递任何参数,我希望它打印出使用语句,但它会使用传递给所获取的上一个命令的参数来运行(甚至不一定是这个脚本!)。

为了仔细检查这一点,我在脚本的开头添加了这一行,以证明情况确实如此。

echo $# $@

如果我不传递任何参数,它会打印出先前来源脚本中的参数数量和列表。

当我向它传递零个参数并获取它时,如何让它说$#为零?

最小的可能重新创建是一个脚本,因此:-

myscript.sh

echo $# $@

然后运行它:-

. ./myscript.sh a b c

打印出

3 a b c

然后在没有任何参数的情况下再次运行它:-

. ./myscript.sh

打印出相同的结果

3 a b c

当你期望它打印出来时

0

如果您不获取脚本并直接运行它:-

./myscript.sh

然后它按预期工作并打印出来

0

我希望这能澄清当前的问题。

由于我似乎得到了很多答案来解释为什么上述方法不起作用,并且没有答案告诉我应该做什么,所以我想我会再尝试一次尝试解释我想要做什么。

要求

我需要编写一个脚本,该脚本至少需要一个参数,如果未使用所述参数调用它,则会发出抱怨(请参阅上面问题中的第一段代码),并且该脚本需要设置一些环境变量。由于第二个要求,我假设我需要获取脚本,以便在脚本完成时仍然设置环境变量。如何编写一个脚本来判断何时没有传递参数,并且还可以设置环境变量?

答案1

. path/to/script at least one argument

正如其他人所说,根据 shell 的不同,这些额外的参数要么被忽略(Bourne、Almquist),要么替换当前的位置参数集($@$1$2...)以解释该脚本(大多数其他 shell)。 POSIX 不指定将额外参数传递给 时的行为.。在这种情况下,对于set a b脚本中对位置参数(如 )所做的任何更改是否会在完成后保留,shell 之间也存在一些差异.

在:

. path/to/script

但是,行为是指定的。需要位置参数不是受到影响,所以对于脚本的解释将保持与之前相同。

如果您希望在解释该脚本期间位置参数为空列表,则需要事先重置它,或使用函数:

set -- # or shift "$#"
. path/to/script

(尽管位置参数丢失)。或者使用一个函数:

dot() { file=$1; shift; . "$file"; }
dot path/to/myscript

执行命令时.,位置参数将是函数的位置参数。在 后面shift,它将是传递给函数的参数减去第一个参数,因此在上面是一个空列表;但这意味着使用该函数,您可以将任意位置参数列表传递给源脚本,如下所示:

dot path/to/myscript extra args

这些将在源脚本中extra args提供。$1$2

如果myscript调用set修改位置参数列表,则只会影响函数的位置参数,并且这些更改在函数返回后不会持续。

答案2

Bash 的帮助文本.

.: . filename [arguments]
    Execute commands from a file in the current shell.

    Read and execute commands from FILENAME in the current shell.  The
    entries in $PATH are used to find the directory containing FILENAME.
    If any ARGUMENTS are supplied, they become the positional parameters
    when FILENAME is executed.

注意这句话:“如果提供任何参数”。如果没有参数,则保留当前位置参数。

$ cat /tmp/sourceme.sh     
printf "%d: " "$#"
printf "<%s> " "$@"
printf "\n"
$ bash -c 'set -- aa bb; . /tmp/sourceme.sh'
2: <aa> <bb> 
$ bash -c 'set -- aa bb; . /tmp/sourceme.sh foo bar'
2: <foo> <bar> 

我测试的所有其他 shell(zsh、ksh)的行为类似。除了dash, 其中总是保留原始位置参数。

$ dash -c 'set -- aa bb; . /tmp/sourceme.sh foo bar'
2: <aa> <bb> 

我在中看不到任何有关重置位置参数的提及POSIX 定义.因此,似乎.从一开始就给出论点是不标准的。


您可以使用函数来代替源脚本,因为它们可以接受标准 shell 中的参数。在这种情况下,您将获取一个定义该函数的文件。

define_fun.sh例如,带有函数定义的文件 ( ):

#!/bin/sh
f() {
    echo "args: $# $@"     # just a test
    if [ "$#" = 0 ]; then 
        echo please give at least one argument
        return 1
    fi;
    ENVVAR=$1          # this should be visible to the whole shell  
    export ENVVAR      # and any commands called later
} 

然后使用它:

$ . define_fun.sh      # source the file to load the func in the current shell

$ f                    # actually run it: this'll complain
$ f 123 456            # this should set ENVVAR
$ echo $ENVVAR         # and this will print '123'

您可以将函数定义放在 shell 的初始化文件之一中(~/.profile?取决于 shell。)

答案3

将参数传递给dot内置命令是 Korn Shell 扩展,不允许在其他 shell 中使用。

由于 POSIX 标准没有提及 dot 命令的 $# 和 $@,因此当前 shell 的参数对 dot 命令是可见的,因此如果您将 shell 调用为:

myshell -s a b c

然后打电话

. ./myscript.sh

预计将打印

3 a b c

相关内容