首页 > 解决方案 > xargs -I {} ls {}* : 无法访问没有这样的文件或目录

问题描述

假设我有一个名为test. 在这个文件夹中,我有许多子目录:

$ ls */*
00/0a:
file1.txt

01/0b:
file2.txt

02/0c:
file3.txt

现在我想得到相同的结果,但使用这样的 xargs :

$ ls | xargs -I {} ls {}/*
ls: cannot access 00/*: No such file or directory
ls: cannot access 01/*: No such file or directory
ls: cannot access 02/*: No such file or directory

我不明白为什么它不能使用*. 有替代方案吗?

标签: bashxargs

解决方案


为什么使用 * 不起作用

文件名扩展(即用*文件的参数列表替换)由 shell 完成,因此要扩展*为文件名列表,您必须调用 shell。因为xargs在传递参数时不调用 shell,所以没有什么可以扩展*为文件列表。因为您没有按*字面命名的文件,所以会ls出现错误退出。

有替代方案吗?

你可以:

# DO NOT PARSE LS.
# Do not use xargs without -d when you do not want ' " \ to be handled specially.
# Do not pass arguments right into subshell, it's as unsafe as eval.
ls | xargs -I{} sh -c 'ls {}/*'
# Not really better as it parses ls.
ls | xargs -d'\n' -n1 sh -c 'ls "$1"/*' _

但不要解析 ls - 而是根据文件名扩展生成列表:

# acceptable - newline separated list, properly passing arguments
printf "%s\n" * | xargs -d'\n' -n1 sh -c 'ls "$1"/*' _
# great - zero separated list, properly passing arguments
# ie. use this
printf "%s\0" * | xargs -0 -n1 sh -c 'ls "$1"/*' _

或者使用具有相似但不同行为的 find 来代替 shell 文件名扩展:

find . -maxdepth 1 -mindepth 1 | xargs -d'\n' ...
find . -maxdepth 1 -mindepth 1 -print0 | xargs -0 ...

您还可以将ls调用从文件名扩展中分离出来,并分两遍进行 - 首先对文件运行文件名扩展,然后将结果列表传递给 to ls

printf "%s\0" * | xargs -0 -n1 sh -c 'printf "%s\0" "$1"/*' _ | xargs -0 ls

潜在地,您可以正确引用参数列表并为它们添加后缀/*,然后重新eval调整列表以触发*所有参数的文件名扩展,这将只调用一个ls和一个子 shell,因此速度最快(而 eval 看起来很危险,我害怕它,它似乎工作正常):

printf "%q/*\0" * | xargs -0 sh -c 'eval ls "$@"' _

推荐阅读