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


9

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

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

find  te*/my\ files/more   -print
find  te*/'my files'/more  -print
find  te*/my' 'files/more  -print

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

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

SEARCH='te*/my\ files/more'
find ${SEARCH} -print

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

find: te*/my\\’: No such file or directory
find: files/more’: No such file or directory

尝试使用引号也会失败。

SEARCH="te*/'my files'/more"
find ${SEARCH} -print

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

find: te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory

这是另一个例子。

SEARCH='te*/my files/more'
find ${SEARCH} -print

如预期的那样:

find: te*/my’: No such file or directory
find: files/more’: No such file or directory

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

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

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print

这等效于:

find te*/my?files/more -print

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

我如何才能实现自己的目标?谷歌没有帮助我:(


@KasiyA我正在使用bash;您必须使用其他东西,因为我之前从未见过这种构造。该命令导致SEARCH: command not found命令find -print被执行。
Paddy Landau 2014年

在黑暗中开枪,但如何报价?find "${SEARCH}" -print
Alaa Ali

@AlaaAli不,它不起作用,因为引用会阻止Bash使用通配符。它将寻找一个带有名称的路径(在我的示例中)te*/'my files'/more
Paddy Landau 2014年

Answers:


9

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

#!/usr/bin/env bash
find  te*/my\ files/ -print

如果需要将其作为变量,它将变得更加复杂:

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print

警告:

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

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

#!/usr/bin/env bash
find "$@" -name "file*"

另一种方法是find完全避免使用bash的扩展globlob功能和glob:

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done

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.

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

#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done

您甚至echo可以直接使用它们而无需循环:

echo te*/my\ files/**

2
我将其设置为答案是因为terdon提出了有益的评论(不要忘记别人的有益评论)。我在命令行上使用了Bash的遍历功能,以将多个路径传递给脚本,而不是脚本试图对其进行整理。它运作良好。
帕迪兰道2014年

2

数组呢?

$ tree Desktop/ Documents/
Desktop/
└── my folder
    └── more
        └── file
Documents/
└── my folder
    ├── folder
    └── more

5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}" 
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder

(*)扩展为与通配符匹配的数组。并"${SEARCH[@]}"扩展为数组([@])中的所有元素,并分别引用每个元素。

迟来的是,我意识到自己应该有能力做到这一点。就像是:

find . -path 'D*/my folder/more/'

聪明的主意,但是a,这行不通。为什么?因为路径本身保存在变量中;因此,INPUTPATH='te*/my files/moreSEARCH=(${INPUTPATH})。无论我如何改变执行此操作的方式,最终仍然会导致无法正常运行的结果。这似乎是不可能的!
Paddy Landau 2014年

当然,这都是正确的,但是OP需要在脚本中完成。这改变了事情,因为通配符的扩展变得更加复杂,这是行不通的。
terdon 2014年

@PaddyLandau在这种情况下,为什么不能使用find-path过滤器?它使用通配符,并且绝对不需要扩展。
muru

@muru很有意思;我不知道-path。不过,在过去的10分钟内,我已经找到了答案:使用eval!似乎比容易-path
Paddy Landau 2014年

2
@terdon和muru以及大家:谢谢。我已经听到了大家都说的话,并且我意识到我应该让我的脚本做一件事情,让Bash遍历传递多个文件或脚本路径。我因此修改了我的脚本。它运行良好,并且更适合Linux理念。再次感谢你!
帕迪·兰道2014年

0

我终于找到答案了。

向所有空格添加反斜杠:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }

此时,SEARCH包含te*/my\ files/more

然后,使用eval

eval find ${SEARCH} -print

就这么简单!使用eval绕过${SEARCH}来自变量的解释。



@terdon谢谢您的警告。回到绘图板!
帕迪·兰道2014年

是的,这令人惊讶地棘手。我刚刚用另一种方法更新了答案,为什么不使用全局替换呢?如果仍然不能解决问题,我建议您在Unix&Linux上发布一个新问题,解释您的最终目标是什么以及为什么需要将模式作为变量。这种事情更有可能在那儿得到更好的答案。
terdon 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.