如何通过脚本中的变量将“*”通配符传递给 find 命令的路径参数?

Pad*_*dau 12 scripts find wildcards

我想用来find在一组受通配符限制的文件夹中查找文件,但路径名中有空格。

从命令行,这很容易。以下示例均有效。

find  te*/my\ files/more   -print
find  te*/'my files'/more  -print
find  te*/my' 'files/more  -print
Run Code Online (Sandbox Code Playgroud)

例如,这些将在terminal/my files/more和 中找到文件tepid/my files/more

但是,我需要将其作为脚本的一部分;我需要的是这样的:

SEARCH='te*/my\ files/more'
find ${SEARCH} -print
Run Code Online (Sandbox Code Playgroud)

不幸的是,无论我做什么,我似乎都无法find在脚本中的命令中混合使用通配符和空格。上面的示例返回以下错误(注意反斜杠的意外加倍):

find: ‘te*/my\\’: No such file or directory
find: ‘files/more’: No such file or directory
Run Code Online (Sandbox Code Playgroud)

尝试使用引号也会失败。

SEARCH="te*/'my files'/more"
find ${SEARCH} -print
Run Code Online (Sandbox Code Playgroud)

这将返回以下错误,忽略引号的含义:

find: ‘te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory
Run Code Online (Sandbox Code Playgroud)

这里再举一个例子。

SEARCH='te*/my files/more'
find ${SEARCH} -print
Run Code Online (Sandbox Code Playgroud)

正如预期的那样:

find: ‘te*/my’: No such file or directory
find: ‘files/more’: No such file or directory
Run Code Online (Sandbox Code Playgroud)

我尝试过的每个变体都会返回一个错误。

我有一个解决方法,它有潜在危险,因为它返回太多文件夹。我将所有空格转换为问号(单字符通配符),如下所示:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print
Run Code Online (Sandbox Code Playgroud)

这相当于:

find te*/my?files/more -print
Run Code Online (Sandbox Code Playgroud)

这不仅会返回正确的文件夹,还会返回terse/myxfiles/more不应该的 .

我怎样才能实现我想要做的事情?谷歌没有帮助我:(

ter*_*don 10

完全相同的命令在脚本中应该可以正常工作:

#!/usr/bin/env bash
find  te*/my\ files/ -print
Run Code Online (Sandbox Code Playgroud)

如果你需要把它作为一个变量,它会变得更复杂一些:

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print
Run Code Online (Sandbox Code Playgroud)

警告:

那样使用eval是不安全的,如果您的文件名可以包含某些字符,则可能会导致执行任意且可能有害的代码。有关详细信息,请参阅bash 常见问题 48

最好将路径作为参数传递:

#!/usr/bin/env bash
find "$@" -name "file*"
Run Code Online (Sandbox Code Playgroud)

另一种方法是find完全避免并使用 bash 的扩展通配功能和通配符:

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done
Run Code Online (Sandbox Code Playgroud)

globstarbash的选项,您可以使用**递归匹配:

globstar
      If set, the pattern ** used in a pathname expansion con?
      text will match all files and zero or  more  directories
      and  subdirectories.  If the pattern is followed by a /,
      only directories and subdirectories match.
Run Code Online (Sandbox Code Playgroud)

要使其 100% 像查找和包含点文件(隐藏文件)一样,请使用

#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done
Run Code Online (Sandbox Code Playgroud)

您甚至echo可以在没有循环的情况下直接使用它们:

echo te*/my\ files/**
Run Code Online (Sandbox Code Playgroud)

  • 由于 terdon 提出的有用评论(不要忘记其他人的有用评论),我将其设置为答案。我在命令行上使用 Bash 的 globbing 功能将多个路径传递给我的脚本,而不是脚本试图对其进行排序。它运作良好。 (2认同)