Bash 将文件列表筛选到存在的文件

Bash 将文件列表筛选到存在的文件,bash,file,Bash,File,如何将文件列表筛选到存在的文件 比如说, echo 'a.txt does/not.exist b.txt' | <???> 您可以ls-d文件,并查看哪些文件得到一些输出。由于这些都在一个字符串中,只需通过管道传输列表并使用xargs即可ls它们 要隐藏错误,请将这些错误重定向到/dev/null。总之,xargs ls-d2>/dev/null使其: $ echo 'a.txt b.txt other' | xargs ls -d 2>/dev/null a.txt b.

如何将文件列表筛选到存在的文件

比如说,

echo 'a.txt
does/not.exist
b.txt' | <???>

您可以
ls-d
文件,并查看哪些文件得到一些输出。由于这些都在一个字符串中,只需通过管道传输列表并使用
xargs
即可
ls
它们

要隐藏错误,请将这些错误重定向到
/dev/null
。总之,
xargs ls-d2>/dev/null
使其:

$ echo 'a.txt
b.txt
other' | xargs ls -d 2>/dev/null
a.txt
b.txt

如您所见,
xargs ls-d
对给定的所有参数执行
ls-d
2>/dev/null
删除了stderr消息。

我想到的第一件事是,在while read循环中使用
stats
的退出代码:

<input> | while IFS= read -r f; do stat "$f" &>/dev/null && echo "$f"; done
|而IFS=read-rf;统计“$f”&>/dev/null&&echo“$f”;完成
请注意,此解决方案速度较慢,因为它在shell代码中循环,并调用 外部实用程序(在每次迭代中创建一个子进程,
stat

如果您有GNU
xargs
,请使用
-d'\n'
确保正确处理带有嵌入空格的文件名(包括目录),将输入拆分为整行,而不是行内空白

echo 'a.txt
does/not.exist
b.txt' | xargs -d '\n' ls -1df 2>/dev/null
注:

  • ls
    命令为每个不存在的输入路径发出一个错误,
    2>/dev/null
    忽略该错误,同时按原样回显现有路径

  • 选项
    -1
    在自己的行上打印每个路径,
    -d
    防止递归到目录中,
    -f
    防止对输入路径进行排序(如果确实需要排序,请省略
    f


macOS/BSD上,
xargs
不支持
-d
,这需要使用
tr
xargs
-0
选项通过NUL分隔输入解决:

echo 'a.txt
does/not.exist
b.txt' | tr '\n' '\0' | xargs -0 ls -1df 2>/dev/null

我会使用bash的if来检查文件。它最终会变得不那么紧凑,但我认为它更易于阅读,并且更容易处理结果中找到的每个文件

它与带空格的文件名兼容

echo 'a.txt
does/not.exist
b.txt' | while read filename
do
  if [[ -f "$filename" ]]
  then
    echo $filename # Or do something else with the files here
  fi
done

作为一个单行程序,纯bash的速度(从mklement0的答案改进而来,如果我有代表的话,我会评论):


... 关于解析
ls
输出的常见警告;例如,如果其中一个参数是一个目录,您将得到目录的内容,而不是它的名称。好的一个,可能是
ls-d
最好。在Ubuntu上,
echo”“|(xargs ls-d 2>/dev/null)
在输出中显示为
,这似乎是因为
ls-d
导致
。这似乎可以通过无条件地预先添加一个换行来纠正:
echo”“|(echo“\n”和&cat)|(xargs ls-d2>/dev/null)
。把第一个echo想象成可能导致一个空行的命令;可能值得添加
-1
,以确保逐行输出,并添加
-f
,以防止输入路径被排序(尽管您实际上可能更喜欢这样)。另外,请注意,带有嵌入空格的文件名将无法正确处理。如果将
stat“$f”&&
替换为
if![[-e“$f”|-h“$f”]];然后
,这就成了C级代码上非常薄的包装。实际上,我希望它会非常快。不错,虽然
而IFS=read-r filename
会使它更健壮(正确处理带有空格的文件名,不会意外解释转义序列)。不错,尽管你可能是说。至于速度优势:对于少量输入线,这可能比基于外部实用程序的解决方案性能更好,但对于大量输入线,后者可能更快。权衡是创建子进程的成本与缓慢的shell代码循环。
echo 'a.txt
does/not.exist
b.txt' | while read filename
do
  if [[ -f "$filename" ]]
  then
    echo $filename # Or do something else with the files here
  fi
done
{ ls; echo does/not.exist; } | while IFS= read -r f; do [[ -f "$f" ]] && echo "$f"; done