如何将find找到的文件作为参数传递?


9

首先要截断一些琐碎但不适用的答案:我既不能使用find+ xargs技巧,也不能使用其变体(如findwith -exec),因为每次调用我需要使用很少的此类表达式。最后,我会回到这一点。


现在来看一个更好的例子,让我们考虑:

$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

我如何将这些作为参数传递给program

只是做不成功

$ ./program $(find -L some/dir -name \*.abc | sort)

失败,因为program获取以下参数:

[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc

可以看出,带有空间的路径被分割,并program认为它是两个不同的参数。

报价直到生效

似乎像我这样的新手用户,遇到此类问题时,往往会随机添加引号,直到最终起作用为止-仅在这里似乎没有帮助…

"$(…)"

$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

因为引号可以防止单词拆分,所以所有文件都作为单个参数传递。

引用单个路径

一种有前途的方法:

$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"

报价在那里。但是它们不再被解释。它们只是字符串的一部分。因此,它们不仅不会阻止单词分裂,而且还会引起争论!

更改IFS

然后我试着和玩IFS。我宁愿find-print0sort-z反正-所以,他们会对“连接路径”自己没有问题。那么,为什么不强加字符拆分功能null呢?

$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc

因此它仍然会在空间上分裂,而不会在上分裂null

我试图将IFS作业放置在$(…)(如上所示)和之前./program。我也尝试了其他语法,例如\0\x0\x00都用'"以及用和不用引用$。这些似乎都没有任何作用……


在这里,我没主意了。我尝试了几件事,但似乎都遇到了与所列相同的问题。

我还能做什么?完全可行吗?

当然,我可以program接受模式并进行搜索。但是在将其固定为特定语法时,需要做很多双重工作。(grep例如,提供文件怎么样?)。

另外,我可以使programaccept带有路径列表的文件。然后,我可以轻松地将find表达式转储到某些临时文件中,并仅提供该文件的路径。可以沿直接路径进行支持,因此,如果用户只有一条简单路径,则可以不使用中间文件就可以提供该路径。但这似乎不太好-需要创建额外的文件并妥善保管,更不用说需要额外的实现了。(但是,从积极的一面来说,对于文件数量作为参数开始引起命令行长度问题的情况,这可能是一种拯救方法。)


最后,让我再次提醒您,就我而言,find+ xargs(和类似方式)技巧无效。为了简化描述,我仅显示一个参数。但是我的真实情况更像是这样:

$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES

因此,xargs从一个搜索中进行一次搜索仍然使我剩下如何处理另一个搜索…

Answers:


13

使用数组。

如果您不需要处理文件名中换行符的可能性,则可以避免使用

mapfile -t ABC_FILES < <(find -L some/dir -name \*.abc | sort)
mapfile -t XYZ_FILES < <(find -L other/dir -name \*.xyz | sort)

然后

./program --abc-files "${ABC_FILES[@]}" --xyz-files "${XYZ_FILES[@]}"

如果确实需要处理文件名中的换行符,并且bash> = 4.4,则可以在数组构造过程中使用-print0-d ''对名称进行空终止:

mapfile -td '' ABC_FILES < <(find -L some/dir -name \*.abc -print0 | sort -z)

(和相似XYZ_FILES)。如果没有较新的bash,则可以使用以空值结尾的读取循环将文件名附加到数组中,例如

ABC_FILES=()
while IFS= read -rd '' f; do ABC_FILES+=( "$f" ); done < <(find -L some/dir -name \*.abc -print0 | sort -z)

优秀的!我在考虑数组。但是不知何故我没有找到任何东西mapfile(或它的同义词readarray)。但这确实有效!
亚当·巴杜拉

但是您可以对其进行一些改进。具有while循环的Bash <4.4版本(我碰巧有...)无法清除数组。这意味着,如果找不到文件,则该数组是未定义的。如果已经定义,则将附加新文件(而不是替换旧文件)。似乎在此declare -a ABC_FILES='()';之前添加就while可以了。(尽管只是添加而ABC_FILES='()';没有。)
Adam Badura

还有什么< <意思呢?一样<<吗?我不认为将其更改为会<<产生语法错误(“意外标记`('”)。那么它是什么以及它如何工作?
Adam Badura

另一个改进(沿着我的特殊用法)是构造另一个数组。所以我们有那些ABC_FILES。那样就好。但是,ABS_ARGS如果将其设为空,则也可以将其设为一个空数组,否则将其设为ABC_FILES一个数组('--abc-files' "${ABC_FILES[@]}")。以后,我可以这样使用它:./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"并且确保无论哪个组(如果有)为空,它都将正确运行。或换种说法:只有在它后面跟随一些实际路径时,才提供这种方式--abc-files(和--xyz-files)。
亚当·巴杜拉

1
@AdamBadura:while read ... done < <(find blah)<PROCESS SUBSTITUTION创建的特殊文件进行常规的Shell重定向。这不同于管道,find blah | while read ... done因为管道while在子外壳中运行循环,因此后续命令中不会保留其中设置的变量。
dave_thompson_085

3

您可以使用IFS = newline(假设没有文件名包含换行符),但是必须在替换之前在外壳程序中进行设置:

$ ls -1
a file with spaces
able
alpha
baker
boo hoo hoo
bravo
$ # note semicolon here; it's not enough to be in the environment passed
$ # to printf, it must be in the environment OF THE SHELL WHILE PARSING
$ IFS=$'\n'; printf '%s\n' --afiles $(find . -name 'a*') --bfiles $(find . -name 'b*')
--afiles
./able
./a file with spaces
./alpha
--bfiles
./bravo
./boo hoo hoo
./baker

使用zsh但不能bash使用null $'\0'。即使在bash您可以处理换行符的情况下,如果有一个从未使用过的足够奇怪的字符,例如

 IFS=$'\1'; ... $(find ... -print0 | tr '\0' '\1') ...

但是,这种方法无法处理您在@steeldriver的答案注释中提出的其他请求,如果find a为空,则忽略--afiles。


因此,据我了解,在Bash中没有办法强迫IFS分裂null吗?
亚当·巴杜拉

@AdamBadura:我敢肯定不会;bash不允许在任何变量(包括IFS)中使用空字节。请注意read -d '',steeldriver方法中使用的是一个字符串,而不是包含空字节的字符串。(而且命令选项也不是这样的var。)
dave_thompson_085

您还必须set -o noglob在使用该split + glob运算符之前(在中除外zsh)禁用globlob()。
斯特凡Chazelas


@AdamBadura是的,在bash中,null与$'\0'和完全相同''
艾萨克(Isaac)2016年

1

我不确定我理解你为什么放弃了xargs

因此,xargs从一个搜索中进行一次搜索仍然使我剩下如何处理另一个搜索…

字符串--xyz-files只是许多参数之一,在程序解释之前,没有理由认为它很特殊。我认为您可以通过以下xargs两个find结果:

{ find -L some/dir -name \*.abc -print0 | sort -z; echo -ne "--xyz-files\0"; find -L other/dir -name \*.xyz -print0 | sort -z; } | xargs -0 ./program --abc-files

你是对的!这也一样!但是请注意,您错过-print0了第二名find。另外,如果采用这种方式,我也将其--abc-files作为一个例子echo-只是为了保持一致性。
亚当·巴杜拉

与阵列方法相比,这种方法看起来更简单,而且一线多。但是,这将需要一些额外的逻辑来涵盖以下情况:如果没有.abc文件,那么也应该没有文件--abc-files(与相同.xyz)。在基于阵列的解决方案通过steeldriver还需要为它额外的逻辑但逻辑是微不足道的存在,虽然可能不那么平凡这里破坏这种解决方案的主要优点-简洁。
亚当·巴杜拉

此外,我真的不知道,但我以为xargs永远不会尝试拆分参数,使而不是一个几个命令,除非它被明确指示这样做用-L--max-lines-l), (--max-args-n--max-chars-s)的参数。我对吗?还是有一些默认值?由于我的程序无法正确处理此类拆分,因此我宁愿调用失败...
Adam Badura

1
@AdamBadura失踪-print0-已修复,谢谢。我不知道所有的答案,但我同意我的解决方案很难包含额外的逻辑。现在,当我知道这种方法时,我可能会自己使用数组。我的回答不是真的适合您。您已经接受了另一个答案,我认为您的问题已解决。我只想指出,您可以通过传递来自多个源的参数xargs,乍一看这并不明显。您可以将其视为概念证明。现在我们都知道几种不同的方法,并且我们可以有意识地选择适合每个特定情况的方法。
卡米尔Maciorowski

是的,我已经实现了基于数组的解决方案,它的工作原理就像魅力。我特别为它如何处理可选性(如果没有文件则没有--abc-files)感到特别自豪。但是您是对的-很高兴知道您的替代方案!特别是我错误地认为这是不可能的。
亚当·巴杜拉
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.