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

发布于 2025-02-13 23:44:42 字数 2193 浏览 1 评论 0 原文

tl; dr

给出了输入,

a
b
c

我试图执行命令 foo ,每个输入作为单独的参数(例如'n'输入是指带有'n'的 foo 的单个调用参数)...

foo a b c

我不是要执行 foo 一个参数(例如,n'输入意思是'n'调用 foo 每个参数迭代

# Not this!
foo a
foo b
foo c

)这样做在字符串串联与 eval 之外?

总结,

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

这比解释要容易得多。

考虑使用三个Swift源文件的硬编码列表(请注意,有些有空格并且在子文件夹中)的硬编码列表……

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

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

但是...我正在尝试自动化事物,因此我希望该源文件列表成为递归查找操作的结果。

这是查找命令我开始的...

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

上述为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

肯定它有效。但是...我忍不住觉得这是在解决方法之后的解决方案之后的解决方法。

是否有更简单的方法可以将输出从查找(或 ls 等)作为单个输入参数命令传递给编译(或任何其他)命令?还如何解决?推荐/首选的方法是什么?

注意:我正在使用ZSH,但是如果它也与bash兼容,那也很好。

TL;DR

Given the input

a
b
c

I am trying to execute command foo with each input as a separate argument (e.g. 'n' inputs means a single call to foo with 'n' arguments)...

foo a b c

I am not trying to execute foo once per argument (e.g. 'n' inputs means 'n' calls to foo each with a single argument iteration)

# Not this!
foo a
foo b
foo c

Can this be done outside of string concatenation with eval?

Summary

I'm trying to find the recommended way to pass the multi-line output of one command as individual arguments to a second command. To be clear, I am not trying to execute that second command, once per argument. I'm trying to pass all the arguments to a single command instance.

This is easier demonstrated than explained.

Consider this shell command with a hard-coded list of three swift source files (note some have spaces and are in subfolders)...

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

It's pretty straightforward and executes as expected.

But... I'm trying to automate things so I want that list of source files to be the result of a recursive find operation.

Here's the find command I started with...

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

Executing that yields the following results...

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

Attempt 1

As such, for my first attempt, I tried this...

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

...but it didn't work. I (incorrectly) thought it was because the filenames weren't properly quoted.

Attempt 2

Because of the above I changed it to this to wrap each filename accordingly...

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

This did properly add the quotes to the filenames, but that's when I realized my earlier error... it's not treating it as separate arguments, one per filename, it's treating it as a single argument with one giant filename with embedded quotes (and last time it was one giant filename without embedded quotes).

Attempt 3 - Success!

Ok, that's when I realized I could go brute-force and just build the exact command I wanted as a literal string, then execute it using eval. Armed with that knowledge, I finally went with this...

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

...and sure enough it worked. BUT... I can't help but feel this is workaround after workaround after workaround.

Is there a simpler way to pass the output from the find (or ls, etc.) command as individual input arguments to the compile (or any other) command? How else can this be solved? What's the recommended/preferred way to do this?

Note: I'm using zsh but if it's also bash-compatible, that would be good too.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

來不及說愛妳 2025-02-20 23:44:42

您正在努力。

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

You're trying too hard.

find * -type f -name "*.swift" -print0 | xargs -0 xcrun -sdk macosx swiftc -o "$OUTPUT_FILE" 
醉梦枕江山 2025-02-20 23:44:42

首先

将您的多行输出保存在阵列中的

第二段

将数组通过行示例

示例 -

按行q进行测试运行行。

# 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

这是否仍然在每个参数上运行?

答:一个<​​strong> array 一个一个一个责任,一个人使用命令我们使用的是什么和哪种行为。

例如,如果将数组传递到 echo 它可以工作。因为 echo 接受n个参数,因此无限制。
另一方面

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.

first

Save yourr multi-line output in an array

second

Pass the array to a desired command

example - test

Run line by line

# 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

Q. Doesn't this still operate on each argument one by one?
A. It is not the array responsibility to operate one by one, it is the command we use that has what and which behavior.

For example if you pass the array to echo it works. Because echo accepts N argument, no limitation.
On the other hand if you pass the array to diff it does not work, simply because it only accepts two argument

echo test

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 test

diff  ${list[@]}
diff: extra operand './file-07.txt'
diff: Try 'diff --help' for more information.
顾冷 2025-02-20 23:44:42

尽管我得到了Xpusostomos的答案 find -print0 | XARGS -0 为了正常工作,它对我不敏感,仅当输出用作命令末尾的参数时(如评论中所述)而不是中间。

这是一种对我有用的更直观的方法。使用shell `

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

Backticks告诉您的外壳先在tick中执行命令,然后输出替换ticked文本好像您已经键入了。因此,您可以使用它将输出放在命令中,而不仅仅是命令的末尾。您还可以在tick中管道,这使您可以操纵输出更多(例如排序), find -exec 仅无法做到。

Although I got Xpusostomos' answer of find -print0 | xargs -0 to work, it is unintuitive to me and only works if the output is used as arguments at the end of a command (as discussed in the comments), not in the middle.

Here's a more intuitive method that works for me. Using the shell ` backtick:

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

The backticks tell your shell to execute the command in the ticks first and then the output replaces the ticked text as if you had typed it. So you could use it to put output wherever you want in your commands, not just at the end of a command. You can also pipe within the ticks which lets you manipulate output more (e.g. sorting), which a find -exec alone cannot do.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文