使用 find 命令转义问题

使用 find 命令转义问题

我需要查找目录中的所有内容,不包括某些子目录和文件。我的脚本需要将其作为函数调用:

function findStuff() {
  # define exclusions
  ignore_dirs=("$1" "*foo*")                        # exclude base dir
  ignore_files=("one.txt" "two.txt" "*three*.txt")
  # build patterns for find command
  dir_pattern=""
  file_pattern=""
  for i in "${ignore_dirs[@]}"; do dir_pattern=$dir_pattern" ! -path \"$i\""; done
  for i in "${ignore_files[@]}"; do file_pattern=$file_pattern" ! -name \"$i\""; done
  # find
  find "$1 $dir_pattern $file_pattern"
  # now do other stuff with the results...
}

findStuff /some/base/dir

但这给了我一个No such file or directory错误。

所以我想看看命令到底是什么,并尝试echo find "$1 $dir_pattern $file_pattern"将其粘贴到命令行上,它起作用了。然后我将其粘贴到脚本中并运行它,它也起作用了!

所以我认为它失败是因为一些逃避问题。我做错了什么?

答案1

find将使用它获取的第一个参数(直到第一个以-or 开头的参数,即!or ()作为要搜索的顶级路径。find当您在函数中调用它时,您将给出一个参数,即字符串$1 $dir_pattern $file_pattern(变量已展开)。找不到该路径。

您还可以在您打算提供给 的参数中包含文字双引号find。双引号的作用是防止 shell 扩展全局模式以及在空格(或变量IFS包含的任何内容)上进行分割,但如果您使用例如,那么双引号将成为用于与文件名进行比较的! -name \"thing\"模式的一部分。find

使用数组,并正确引用单独的参数find

myfind () {
  local ignore_paths=( "$1" "*foo*" )
  local ignore_names=( "one.txt" "two.txt" "*three*.txt" )

  local path_args=()
  for string in "${ignore_paths[@]}"; do
      path_args+=( ! -path "$string" )
  done

  local name_args=()
  for string in "${ignore_names[@]}"; do
      name_args+=( ! -name "$string" )
  done

  find "$1" "${path_args[@]}" "${name_args[@]}"
}

每次我们追加到path_argsname_args上面时,我们都会向列表中添加三个元素:!-path-name、 和"$string"。当扩展"${path_args[@]}"and "${name_args[@]}"(注意双引号)时,元素将被单独引用。


等效实现适用于/bin/sh

myfind () (
    topdir=$1

    set --

    # paths to ignore
    for string in "$topdir" "*foo*"; do
        set -- "$@" ! -path "$string"
    done

    # names to ignore
    for string in "one.txt" "two.txt" "*three*.txt"; do
        set -- "$@" ! -name "$string"
    done

    find "$topdir" "$@"
)

shshell 中,我们只有一个可用的数组,它是位置参数列表,$@因此我们find在其中收集选项。显然,特定的解决方案bash也可以编写为使用单个数组,并且sh变体也会运行bash


最后,测试的输出echo并不是函数执行的命令的准确表示。

考虑一下:

cat "my file name"

它运行cat在名为 的东西上my file name,并且

echo cat "my file name"

输出字符串cat my file name.这是因为 shell 在执行命令之前删除了字符串周围的引号。跑步命令,cat会寻找文件,没有一个。

当您将命令复制粘贴到 shell 中时,它运行良好,因为您在输出的字符串中包含了文字双引号echo(通过转义它们),但这并不是您的函数执行的实际命令。

相关内容