文件内容filelist
:
/some/path/*.txt
/other/path/*.dat
/third/path/example.doc
我想列出这些文件,所以我这样做:
cat filelist | xargs ls
但我没有扩大这些范围,而是得到:
ls: cannot access '/some/path/*.txt': No such file or directory ls: cannot access '/other/path/*.dat': No such file or directory /third/path/example.doc
答案1
壳会扩大球体。在这里,这是一种非常罕见的情况,在类似于 Bourne 的 shell(除了 zsh)中,在不带引号的命令替换时调用隐式 split+glob 运算符可能很有用:
IFS='
' # split on newline only
set +o noglob # make sure globbing is not disabled
ls -ld -- $(cat filelist) # split+glob
在 中zsh
,你会这样做:
ls -ld -- ${(f)~"$(<filelist)"}
f
在换行符上分割的参数扩展标志在哪里,以及~
要求通配符默认情况下不会在参数扩展或命令替换时完成。
请注意,如果匹配文件列表很大,您可能会遇到参数列表太长错误(大多数系统上系统调用的限制execve()
),xargs
否则可以解决。在 中zsh
,您可以使用zargs
:
autoload zargs
zargs --eof= -- ${(f)~"$(<filelist)"} '' ls -ld --
其中zargs
将拆分列表并运行ls
多次,以避免必要时的限制xargs
。
或者您可以将列表传递给内置命令(因此不涉及系统execve()
调用):
仅打印文件列表:
print -rC1 -- ${(f)~"$(<filelist)"}
或者将其输入xargs
NUL 分隔的:
print -rNC1 -- ${(f)~"$(<filelist)"} |
xargs -r0 ls -ld --
请注意,如果任何 glob 无法匹配 中的文件,zsh
您将收到错误消息。如果您希望这些 glob 扩展为空,则可以将N
glob 限定符添加到 glob 中(这nullglob
在每个 glob 的基础上启用):
print -rNC1 -- ${(f)^~"$(<filelist)"}(N) |
xargs -r0 ls -ld --
添加该操作(N)
还会将所有没有 glob 运算符的行转换为 glob,从而过滤掉路径引用的文件和不存在的文件;但是,这意味着您不能在 glob in 中使用 glob 限定符,filelist
除非您将它们表示为(#q...)
并启用该extendedglob
选项。另请注意,由于限定符可以运行任意代码,因此文件内容filelist
来自受信任的来源非常重要。
在其他类似 Bourne 的 shell 中,包括bash
,不匹配的 glob 会按原样保留,因此会按字面意思传递给ls
可能会报告相应文件不存在的错误。
在 中bash
,您可以使用该nullglob
选项(从 zsh 复制的)并处理没有任何 glob 专门匹配的情况:
shopt -s nullglob
IFS=$'\n'
set +o noglob
set -- $(<filelist)
(( $# == 0 )) || printf '%s\0' "$@" | xargs -r0 ls -ld --
bash
, 没有任何与zsh
的 glob 限定符等效的内容。为了确保没有 glob 运算符的行(例如您的/third/path/example.doc
)被视为 glob 并在它们与实际文件不对应时被删除,您可以添加@()
到这些行(需要extglob
)。然而,这对于以字符结尾的行不起作用/
。但是,您可以添加@()
到最后一个非字符并依赖始终存在的/
事实/
shopt -s nullglob extglob
IFS=$'\n'
set +o noglob
set -- $(LC_ALL=C sed 's|.*[^/]|&@()|' filelist)
(( $# == 0 )) || printf '%s\0' "$@" | xargs -r0 ls -ld --
无论如何,请注意,受支持的 glob 运算符列表随 shell 的不同而有很大差异。不过,您在示例中使用的唯一一个 ( *
) 应该得到所有人的支持。
答案2
您可以稍微修改您的脚本,然后sh
从以下位置调用xargs
:
cat filelist | xargs -i -- /bin/sh -c 'ls $1' _X_ {}
或者让我们xargs
读取文件本身:
xargs -a filelist -i -- /bin/sh -c 'ls $1' _X_ {}
答案3
while read filepattern
do
ls $filepattern
done < filelist
在这种情况下,您希望通过文件通配来扩展变量值,因此必须使用不带引号的变量。
shell 将替换/some/path/*.txt
为匹配文件的列表。
如果您只想列出不需要的文件名ls
。您可以使用echo
类似echo $filepattern
.
与上面的代码相反,使用引号,您将得到与问题示例中相同的错误,因为 shell 将传递未更改的字符串,例如/some/path/*.txt
to ls
。
while read filepattern
do
ls "$filepattern"
done < filelist