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

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

我想用来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,而且还会返回不应该返回的。

我该如何实现我想要做的事情?谷歌没有帮助我 :(

答案1

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

#!/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 的扩展通配符功能和通配符:

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

bashglobstar选项允许您使用**递归匹配:

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

那么数组怎么样?

$ 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 本身应该能够做到这一点。例如:

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

答案3

现在它有点过时了,但是,如果它可以帮助任何人解决这个问题,那么使用 RE 的排序符号[[.space.]]而不$SEARCH在查找命令行参数中引用变量,只要扩展路径名中没有特殊字符代替星号就可以工作。

set -x
mkdir -p te{flon,nnis,rrine}/my\ files/more
SEARCH=te*/my[[.space.]]files/more
find $SEARCH

得到以下结果:

+ mkdir -p 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
+ SEARCH='te*/my[[.space.]]files/more'
+ find 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
teflon/my files/more
tennis/my files/more
terrine/my files/more

为了防止不需要的字符被覆盖,可以*用任何排序元素(字符)替换星号():

set -x
mkdir -p -- te{,-,_}{flon,nnis,rrine}/my\ files/more
SEARCH=te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more
find $SEARCH

得到以下结果:

+ mkdir -p -- 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more' \
'te-flon/my files/more' 'te-nnis/my files/more' 'te-rrine/my files/more' \
'te_flon/my files/more' 'te_nnis/my files/more' 'te_rrine/my files/more'
+ SEARCH='te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more'
+ find 'te-flon/my files/more' 'te_flon/my files/more' 'teflon/my files/more' \
'te-nnis/my files/more' 'te_nnis/my files/more' 'tennis/my files/more' \
'te-rrine/my files/more' 'te_rrine/my files/more' 'terrine/my files/more'
te-flon/my files/more
te_flon/my files/more
teflon/my files/more
te-nnis/my files/more
te_nnis/my files/more
tennis/my files/more
te-rrine/my files/more
te_rrine/my files/more
terrine/my files/more

请注意,为了缩短行, 可以用或[.hyphen.]替换 ,也 可以用或替换。[.-.]-[.underscore.][._.]_



\为了便于阅读,我自己添加了用反斜杠 ( ) 截断的行。

答案4

我终于找到了答案。

在所有空格前添加反斜杠:

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

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

然后,使用eval

eval find ${SEARCH} -print

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

相关内容