Bash:最佳练习以迭代目录内容,直到条件匹配为止



我有以下情况:

我想浏览目录$1 中的文件,直到其中一个符合我的状况为止。

详细:我想测试目录是否包含音频文件。找到第一个音频文件后,应进行process_audio_dir;如果DIR中没有音频文件,process_noaudio将发生。

到目前为止我的解决方案:

if [[ -z $(file -b "$1"/* | grep -i audio) ]]; then
  echo "there are no audio files"; process_noaudio
else
  echo "at least one audio file"; process_audio_dir
fi

file -b告诉我文件是什么。

我从用set -x查看它的猜测是,这将在所有文件上运行 file -b,将结果放入一行,并抓住该行以进行匹配。(也许这是一个错误的假设)。

我宁愿有一个循环,直到找到第一个音频文件(一个足以匹配条件)并在那里停止/断开,或者如果没有音频文件,则继续使用process_noaudio

我感觉到/直到要实现这一目标,但我无法弄清楚。

将是什么(您的首选|最佳实践|最优雅|最低成本|最快|最快的)方法,可以在第一次匹配之前检查目录中的每个文件?

最安全的方法可能是直接迭代全球结果,因此您不容易受到包含特殊字符的文件名:

for path in "$1"/*; do
    if file -b -- "$path" | grep -qi audio; then
        printf 'Found an audio file %sn' "$path"
        process_audio_dir
        exit
   fi
done
# since we didn't exit above, most be no audio files
printf "Didn't find any audio filesn"
process_noaudio

替代,如果您不想退出那里,则可以设置一个标志,指示您找到它并在循环后检查该标志,然后在if内使用break,一旦找到一个循环,就可以退出循环。

您的原因是将grep应用于所有file结果的输出,是因为Glob首先扩展,然后您运行命令,例如

file -b dir/file1 dir/file2 dir/file3 ...

然后,该命令的输出将被馈送到grep我的解决方案将球放在命令的"外部"上,因此我们将单独运行每个文件。当然,在很多次启动file时,还有更多的开销,因此对我来说并不是很明显的,这将更加有效。这可能取决于有多少文件,第一个音频文件通常在列表中的距离以及类似的内容。

正如评论中提到的那样,在findls的印刷文件名结果上迭代是危险的,因为这些结果将受到单词分割的约束,并有可能取决于您的确切操作。使用上面的for循环是建议这样做的推荐方法。有关更多信息,请参见不要解析

效率低下,但兼容

find "$1" -maxdepth 1 -type f 
  -exec sh -c 'file -b -- "$1" | grep -qi audio' _ {} ; 
  -print 
  -quit

在这里,我们正在执行每个单独名称上运行file的Shell管道,然后调用grep检查其结果。这显然效率低下,但是由于-exec运行的Shell命令返回非零退出代码时,find仍将在第一个结果中提早退出,而grep将返回真相值(因此允许-print-quit操作运行)。


有效,但仅GNU-

shopt -s nocaseglob # enable case-insensitive matching
while IFS= read -r -d '' filename && IFS= read -r type; do
  if [[ $type = *audio* ]]; then
    break # exit the loop with the name in "$filename" and the type in "$type"
  fi
done < <(find "$1" -type f -maxdepth 1 -exec file -b -0 -- '{}' +)
echo "Found file $filename of type $type"

在这里,我们正在运行尽可能少的file实例(使用-exec ... {} +将多个文件名传递给每个file调用),使用GNU扩展-0在每个文件名中输出后打印一个NUL。然后使用一对read命令对此输出进行解析,并检查了子字符串audio

在posix c api中,我们拥有opendir()/readdir(),这是将目录递增为流的最灵活的方法。

没有这些功能的映射。

ls是您在bash中列出目录内容的主要方式。您可以使用ls进行以下操作以将列表处理为流(但是我很确定ls是否会缓冲列表。ls确实支持对可以进行缓冲的排序):

>
ls "$1" | while read f; do test -f $f && echo $f | grep -q "audio" && echo $f && break; done

另一个常见工具是find。逐步查找作品。以下内容适用于您的用例,即"迭代目录内容直到条件匹配",并且比ls更好。这只是打印第一个找到的文件名。调整条件匹配时您想做什么:

find -maxdepth 1 -type f -exec "bash" -c "file -b '{}' | grep -qi audio" ";" -print -quit

最新更新