嵌套 shell 脚本不适用于列表变量

嵌套 shell 脚本不适用于列表变量

我创建了以下两个最小的 shell 脚本...

测试1

#!/usr/bin/env bash

letterList=$1

for letter in $letterList
do
    echo $letter
done

和第二个

测试2

#!/usr/bin/env bash

letterList=$1

test1 $letterList

当我执行时,test1 'A B C'我得到所需的输出,A然后BC

但是当我执行时test2 'A B C'我期望test1 'A B C'发生,但实际输出只是A.

因此,看起来好像嵌套变量列表没有被读取——换句话说,正在执行的是test1 A

发生了什么事,我怎样才能达到预期的结果?

PS:如果相关的话,我正在使用 OS X。

答案1

您的代码中没有列表变量。你有一根绳子。

对的调用test1使用未加引号的扩展$letterList。这将使 shell 将其值拆分为空格、制表符和换行符(默认情况下)上的多个单词,并且每个单词都将进行文件名通配符(如果它们包含文件名通配符)。结果单词test1作为单独的参数传递。

在中test1,您只需将这些参数中的第一个放入letterList.

如果您想将字符串$letterList按原样传递给test1from test2,则需要在调用中双引号其扩展:

test1 "$letterList"

顺便说一下,你依靠$letterList在 shell 中执行循环中值的拆分test1(导致问题的机制相同),但您永远不会保护代码免于意外执行文件名通配(使用 例如 测试您的脚本test2 "A B C *"以了解我的意思)。


如果你想通过列表而不是单个字符串,然后test2使用列表而不是字符串进行调用:

test2 A B C "Hello World" "* *"

然后,在 中test2,将参数传递给test1

#!/bin/sh

test1 "$@"

在这里,"$@"将扩展到位置参数(脚本的参数)列表,每个单独的参数都被引用。这意味着像这样的参数Hello World在调用中不会被拆分为多个参数test1,并且字符串* *也不会被拆分为两个,并且不会发生文件名匹配。

在 中test1,迭代参数:

#!/bin/sh

for arg in "$@"; do
    printf '%s\n' "$arg"
done

或者

#!/bin/sh

for arg do
    printf '%s\n' "$arg"
done

...或者,如果您只想打印参数,只需

#!/bin/sh

printf '%s\n' "$@"

/bin/sh自始至终都将其用作 shell,因为这里没有任何特定于bash.

答案2

./test2 'a b c'运行时我们得到:

$1=a b c

所以最后一行变成:

test1 a b c

test1运行时我们得到:

$1=a
$2=b
$3=c

也就是说,给的参数test1没有用引号引起来,因此它们变成了三个参数而不是一个。

要传递test1单个参数,只需将变量括在双引号中即可。也就是说,将最后一行替换test2为:

./test1 "$letterlist"

请注意,我还添加了./可执行文件的路径,因为.$PATH.

答案3

目前尚不清楚您的实际目标是什么。您唯一清楚地说的是您想要在三个单独的行上test2 'A B C' 输出A,B和 。C

这是一个不同的解决方案,可以为您提供该结果。一般来说,它的输出似乎与 凯勒姆的回答

  • 保持test2相同。
  • 更改test1如下:

    #!/bin/sh
    
    for letter in $*
    do
        printf '%s\n' "$letter"
    done
    

    这自然会改变 的行为test1。现在它会考虑所有的论点,而不仅仅是$1。因此test1 'A B C' 'D E F'将在六个单独的行上输出A, B, C, D,E和 F (而不是仅仅A,B和 C)。

我们通常不鼓励使用,$*因为它将包含空格的字符串分解为单独的单词。例如,"lazy dog"将变成两个单词(参数)。但这似乎正是您想要的行为。

printfecho比文件名开头的情况稍微安全一些-或包含\.这个网站已经对此进行了详细讨论;如果您想了解更多信息,只需搜索即可。

相关内容