我的目录结构如下:
rust/
├── dir1/
│ └── Cargo.toml
└── dir2/
└── Cargo.toml
我想创建一个将从该rust
目录运行的 zsh 脚本,并且对于每个带有Cargo.toml
文件的子目录,cargo
使用用户指定的参数运行命令。
例子:
run.sh "test -- --ignored"
应该运行cargo -v test -- --ignored --manifest-path ./dir1/Cargo.toml
并且cargo -v test -- --ignored --manifest-path ./dir2/Cargo.toml
.
双引号对于防止 shell 弄乱--
.用户可以传递其他参数而不带--
.
我已经尝试过find . -name 'Cargo.toml' -type f -print -exec cargo -v "$@" --manifest-path {} \;
,但收到错误“错误:没有这样的子命令:测试 -- --ignored”。显然,整个内容作为字符串传递,而不是作为单独的字符串传递。
这个怎么做?
答案1
外壳没有搞乱--
。
做就是了:
#! /bin/zsh -
for toml (**/Cargo.toml(N.)) cargo -v "$@" --manifest-path $toml
并将其称为:
that-script test -- --ignored
使用 zsh globbing 相比有几个优点find
:
- 隐藏的文件和目录将被忽略(
D
如果不需要,请添加限定符) - 列表已排序
- 可以将包含的参数
{}
传递给cargo
.
如果您想将一个参数传递给脚本,并且 shell 将其拆分为空格字符,并将生成的单词作为单独的参数传递给cargo
,您可以这样做:
#! /bin/zsh -
for toml (**/Cargo.toml(N.)) cargo -v ${(s[ ])1} --manifest-path $toml
或者按字符(默认为空格、制表符换行符和 nul)$1
进行分割:$IFS
$=1
然后,你可以调用:
that-script 'test -- --ignored'
但这意味着用户无法将包含空格(或 IFS 字符)的参数传递给cargo
.
或者,您可以告诉 shell 使用z
orZ[options]
和Q
参数扩展标志对该一个参数进行 shell 标记化和引号删除,使用"${(Q@)${(Z[n])1}}"
(Z[n]
对于n
ewline 也被接受为分隔符,另请参阅z[Cn]
识别和删除C
注释,@
在双引号内保留空元素),也许只进行一次标记化,以避免每次在循环中都这样做,甚至将它们存储在$argv
(又名$@
)中,所以我们回到了第一个:
#! /bin/zsh -
argv=( "${(Q@)${(Z[n])1}}" )
for toml (**/Cargo.toml(N.)) cargo -v "$@" --manifest-path $toml
然后能够做到:
that-script "test -- --opt1='foo bar' --opt2=$'blah\nblah' --opt3 ''"
例如, and test
、--
、--opt1=foo bar
、--opt2=blah<newline>blah
和--opt3
空字符串作为单独的参数传递给cargo
。
但同样,当您可以让用户将所有参数单独传递给您的脚本时(语法为他们的shell / 语言,而上面的Z
/Q
标志需要引用语法)以及将它们与标准zsh
一起传递的脚本,如上面的第一个示例中所示。cargo
"$@"
现在,事实证明,您的问题是 was 位于子命令的选项分隔--manifest-path path/to/Cargo.toml
符之前。您始终可以将这些参数插入用户传递的参数列表中,如下所示:--
test
#! /bin/zsh -
for toml (**/Cargo.toml(N.)) (
argv[2,0]=(--manifest-path $toml)
cargo -v "$@"
)
这样,当用户调用 时that-script test -- --ignored
,脚本最终会调用cargo -v test --manifest-path path/to/Cargo.toml -- --ignored
。