首页 > 解决方案 > BASH - 当 $@ 包含不存在的文件时如何使用 $@ 查找?

问题描述

我有一个函数,它传递了几个文件名作为参数。某些文件名可能指的是不存在或已被删除的文件。因为 args 中也可能有目录,所以我想获取作为 args 传递的所有文件的递归列表,以及目录中包含的所有可能作为 args 包含的文件的列表。

function SomeFunction () {
  # get list of files recursively
  fList=$(find $@ -type f) 
  do something with $fList
  .
  .
  .
}

[~]$SomeFunction existentFile nonexistentFile existentNonEmptyDir
find: `nonexistentFile': No such file or directory

fList=$(find $@ -type f)如果不是因为它会很好用nonexistentFile。即使文件丢失,有没有办法进行同样的调用?

标签: bashunix

解决方案


忽略来自的警告是完全合理的find。但是,如果您想过滤参数列表以查找在调用之前实际存在的内容,则可以显式执行此操作:

SomeFunction() {
  local -a existing_args=( )
  for arg in "$@"; do
    [[ -e "$arg" ]] && existing_args+=( "$arg" )
  done
  while IFS= read -r -d '' result; do
    printf 'Processing result: %q\n'  ## put your own code here
  done < <(find "${existing_args[@]}" -type f -print0)
}

一些注意事项:

  • function关键字取自 ; 的 ksh 函数声明语法function SomeFunction {。函数名后面的括号取自 POSIX sh 函数声明语法SomeFunction() {。将它们结合起来会得到既不兼容传统 ksh 也不兼容 POSIX sh 的结果。请参阅http://wiki.bash-hackers.org/scripting/obsolete
  • 引用"$@"确保正确处理带有空格、全局字符等的文件名;同样在数组扩展上,例如"${existing_args[@]}".
  • existing_args被定义为一个数组——一个字符串列表——而不是一个单独的字符串。这很重要,因为文件名本身就是字符串,并且尝试像列表一样迭代字符串通常是错误的(再次影响名称中包含空格、文字换行符、全局字符等的文件)。
  • 使用find -print0确保我们使用 NUL 分隔文件名。这很重要,因为 UNIX 中的文件名在内部作为 NUL 分隔的字符串传递;因此,NUL 是唯一保证不会出现在路径中的字符。IFS= read -r -d '' var从这样一个 NUL 分隔的字符串中读取单个条目。
  • Using< <(...)确保我们从...' 的输出重定向到作为该重定向主题的循环,同时将find- 而不是循环 - 放在子shell中。有关为什么这很重要的示例,请参阅BashFAQ #24

推荐阅读