我创建了以下两个最小的 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
然后B
是C
。
但是当我执行时test2 'A B C'
我期望test1 'A B C'
发生,但实际输出只是A
.
因此,看起来好像嵌套变量列表没有被读取——换句话说,正在执行的是test1 A
。
发生了什么事,我怎样才能达到预期的结果?
PS:如果相关的话,我正在使用 OS X。
答案1
您的代码中没有列表变量。你有一根绳子。
对的调用test1
使用未加引号的扩展$letterList
。这将使 shell 将其值拆分为空格、制表符和换行符(默认情况下)上的多个单词,并且每个单词都将进行文件名通配符(如果它们包含文件名通配符)。结果单词test1
作为单独的参数传递。
在中test1
,您只需将这些参数中的第一个放入letterList
.
如果您想将字符串$letterList
按原样传递给test1
from 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"
将变成两个单词(参数)。但这似乎正是您想要的行为。
printf
echo
比文件名开头的情况稍微安全一些-
或包含\
.这个网站已经对此进行了详细讨论;如果您想了解更多信息,只需搜索即可。