将 glob 管道传输到 ls

将 glob 管道传输到 ls

文件内容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)"}

或者将其输入xargsNUL 分隔的:

print -rNC1 -- ${(f)~"$(<filelist)"} |
  xargs -r0 ls -ld --

请注意,如果任何 glob 无法匹配 中的文件,zsh您将收到错误消息。如果您希望这些 glob 扩展为空,则可以将Nglob 限定符添加到 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/*.txtto ls

while read filepattern
do
    ls "$filepattern"
done < filelist

相关内容