管道 globs 到 ls

lon*_*nix 6 linux ls bash pipe cat

文件内容filelist

/some/path/*.txt
/other/path/*.dat
/third/path/example.doc
Run Code Online (Sandbox Code Playgroud)

我想列出这些文件,所以我这样做:

cat filelist | xargs ls
Run Code Online (Sandbox Code Playgroud)

但我没有扩大这些球体,而是得到:

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
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 10

贝壳扩大球体。在这里,这是一种非常罕见的情况,其中隐式 split+glob 运算符在 zsh 以外的类似 Bourne 的 shell 中对未加引号的命令替换调用可能有用:

IFS='
' # split on newline only
set +o noglob # make sure globbing is not disabled
ls -ld -- $(cat filelist) # split+glob
Run Code Online (Sandbox Code Playgroud)

在 中zsh,你会这样做:

ls -ld -- ${(f)~"$(<filelist)"}
Run Code Online (Sandbox Code Playgroud)

f要在换行符上拆分的参数扩展标志在哪里,并~ 请求globbing,否则在参数扩展或命令替换时默认情况下不会执行此操作。

请注意,如果匹配文件的列表很大,您可能会遇到Argument list too long错误(execve()大多数系统上的系统调用限制),xargs否则本来可以解决的。在 中zsh,您可以zargs改用:

autoload zargs
zargs --eof= -- ${(f)~"$(<filelist)"} '' ls -ld --
Run Code Online (Sandbox Code Playgroud)

Wherezargs将拆分列表并运行ls多次以避免必要时的限制xargs

或者您可以将列表传递给内置命令(因此不涉及execve()系统调用):

只打印文件列表:

print -rC1 -- ${(f)~"$(<filelist)"}
Run Code Online (Sandbox Code Playgroud)

或者将其提供xargs给以 NUL 分隔的:

print -rNC1 -- ${(f)~"$(<filelist)"} |
  xargs -r0 ls -ld --
Run Code Online (Sandbox Code Playgroud)

请注意,如果任何 glob 无法匹配文件 in zsh,您将收到错误消息。如果您希望这些Nglob扩展为空,您可以将glob 限定符添加到 globs(nullglob在每个 glob 的基础上启用):

print -rNC1 -- ${(f)^~"$(<filelist)"}(N) |
  xargs -r0 ls -ld --
Run Code Online (Sandbox Code Playgroud)

添加这(N)也会将所有没有 glob 运算符的行转换为 globs,允许过滤掉路径引用的文件和不存在的文件;但是,这意味着您不能在 glob 中使用 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 --
Run Code Online (Sandbox Code Playgroud)

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 --
Run Code Online (Sandbox Code Playgroud)

在任何情况下,请注意受支持的 glob 运算符列表随 shell 的不同而有很大差异。不过,您在示例中使用的唯一一个 ( *) 应该得到所有人的支持。


Dan*_*ini 5

您可以稍微修改脚本,然后shxargs以下位置调用:

cat filelist | xargs -I{} sh -c 'ls $1' xargs-sh {}
Run Code Online (Sandbox Code Playgroud)

或者让我们xargs阅读文件本身:

xargs -a filelist -I{} /bin/sh -c 'ls $1' xargs-sh {}
Run Code Online (Sandbox Code Playgroud)

  • 我的意思是,它不是 `--`,而是 `_` 或更好的 `xargs-sh` 左右,参见 [this](https://unix.stackexchange.com/a/156010/236063)。`--` 可以工作,但它可能会与 `--` 的其他用法混淆 (2认同)