BASH-当$ @包含不存在的文件时,如何与$ @一起使用find?

时间:2018-06-27 15:37:44

标签: bash unix

我有一个传递几个文件名作为参数的函数。某些文件名可能会引用不存在或已被删除的文件。因为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会很好用。即使文件丢失,有没有办法进行相同的呼叫?

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

someFunction () {
    # remove nonexistent names from argument list
    for pathname do
        [ -e "$pathname" ] && set -- "$@" "$pathname"
        shift
    done

    # exit with failure if none of the given pathnames existed
    [ "$#" -eq 0 ] && return 1

    # find regular files in the given existing paths and process these
    find "$@" -type f -exec sh -c '
        for pathname do
            # process found regular file "$pathname"
        done' sh {} +
}
  • 用双引号$@,否则shell将对元素进行单词拆分和文件名遍历。
  • 请勿将find的输出保存到字符串中。如果任何文件名包含空格,那将会中断。