如何将一个命令的多行输出作为 n 个参数传递给单个命令,而不是带有单个参数的 n 个命令?

问题描述 投票:0回答:3

TL;博士

鉴于输入

a
b
c

我正在尝试将每个输入作为单独的参数来执行命令

foo
(例如“n”个输入意味着使用“n”个参数对
foo
进行一次调用)...

foo a b c

我不想为每个参数执行一次

foo
(例如“n”个输入意味着“n”次调用
foo
,每个参数都有一个参数迭代)

# Not this!
foo a
foo b
foo c

这可以在字符串连接之外使用

eval
完成吗?

总结

我正在尝试找到推荐的方法,将一个命令的多行输出作为单独的参数传递给第二个命令。需要明确的是,我并不是试图对每个参数执行一次第二个命令。我正在尝试将all参数传递给单个命令实例。

这比解释更容易演示。

考虑这个 shell 命令,其中包含三个 swift 源文件的硬编码列表(注意有些有空格并且位于子文件夹中)...

xcrun -sdk macosx swiftc -o "$OUTPUT_FILE" "main.swift" "car.swift" "Models/Road Bike.swift"

它非常简单并且按预期执行。

但是......我正在尝试自动化操作,所以我希望源文件列表是递归

find
操作的结果。

这是我开始使用的

find
命令...

find * -type f -name "*.swift"

执行会产生以下结果...

Models/Road Bike.swift
car.swift
main.swift

尝试1

因此,对于我的第一次尝试,我尝试了这个......

SOURCE_FILES=$(find * -type f -name "*.swift" | tr '\n' ' ')
xcrun -sdk macosx swiftc -o "$OUTPUT_FILE" $SOURCE_FILES

...但是没有成功。我(错误地)认为这是因为文件名没有正确引用。

尝试2

由于上述原因,我将其更改为这样以相应地包装每个文件名...

SOURCE_FILES=$(find * -type f -name "*.swift" | while read fn; do echo \"$fn\"; done | tr '\n' ' ')
xcrun -sdk macosx swiftc -o "$OUTPUT_FILE" $SOURCE_FILES

这确实正确地将引号添加到文件名中,但就在那时我意识到了我之前的错误...它没有将其视为单独的参数,每个文件名一个,而是将其视为带有嵌入引号的巨大文件名的单个参数(并且上次它是一个巨大的文件名,没有嵌入引号)。

尝试 3 - 成功!

好吧,就在那时我意识到我可以通过暴力方式构建我想要的确切命令作为文字字符串,然后使用

eval
执行它。有了这些知识,我终于同意了......

SOURCE_FILES=$(find * -type f -name "*.swift" | while read file; do echo \"$file\"; done | tr '\n' ' ')
cmd='xcrun -sdk macosx swiftc -o "$OUTPUT_FILE" '$SOURCE_FILES
eval $cmd

...果然有效。但是...我忍不住觉得这是一个又一个的解决方法。

是否有更简单的方法将

find
(或
ls
等)命令的输出作为单独的输入参数传递给编译(或任何其他)命令?还有什么办法可以解决这个问题?推荐/首选的方法是什么?

注意:我使用的是 zsh,但如果它也兼容 bash,那就太好了。

bash shell sh zsh xargs
3个回答
2
投票

你太努力了。

find * -type f -name "*.swift" -print0 | xargs -0 xcrun -sdk macosx swiftc -o "$OUTPUT_FILE" 

1
投票

首先

将多行输出保存在数组中

第二

将数组传递给所需的命令

示例 - 测试

逐行运行

# cd into a test directory
cd `mktemp -d`

# generate list of files
touch file-{01..09}.txt

# save the list in an array + remove trailing newlines 
mapfile -t list < <(find -type f -name \*.txt)

# check the output
echo "${list[@]}"

# see the size of the array
echo "${#list[*]}"

# see indexes 
echo "${list[0]}"
echo "${list[1]}"
echo "${list[2]}"

# pass the array to a command you liked 
# for example substitute txt with TXT
sed 's/txt/TXT/g' <<< "${list[*]}"

# output
./file-09.TXT ./file-08.TXT ./file-07.TXT ./file-06.TXT ./file-05.TXT ./file-04.TXT ./file-03.TXT ./file-02.TXT ./file-01.TXT

问。这不还是对每个参数一一进行操作吗?
A. 逐一操作并不是数组的责任,而是我们使用的命令有什么、有哪些行为。

例如,如果将数组传递给

echo
,它就可以工作。因为
echo
接受 N 个参数,没有限制。
另一方面,如果将数组传递给
diff
它不起作用,因为它只接受两个参数

回声测试

echo ${list[@]}
./file-09.txt ./file-08.txt ./file-07.txt ./file-06.txt ./file-05.txt ./file-04.txt ./file-03.txt ./file-02.txt ./file-01.txt

差异测试

diff  ${list[@]}
diff: extra operand './file-07.txt'
diff: Try 'diff --help' for more information.

0
投票

虽然我得到了 Xpusostomos 的答案

find -print0
可以工作,但它对我来说不直观,只有当输出用作命令末尾的参数时才有效(如评论中所述),而不是中间。

这里有一个更适合我的直观方法。使用 bash

`
反引号:

xcrun -sdk macosx swiftc -o "$OUTPUT_FILE" `find * -type f -name "*.swift"`

反引号告诉您的 shell 首先执行引号中的命令,然后输出将替换它,就像您键入它一样。因此,您可以使用它将输出放置在命令中的任何位置,而不仅仅是命令的末尾。

© www.soinside.com 2019 - 2024. All rights reserved.