我试图对文件夹进行一些跟踪,并希望使用文件中的 find -name 过滤器。因此,我准备了一个名为 filter_files 的文件,其中包含过滤器,例如:
cat filter_files:
-name "a.txt" -o -name "b.txt"
我尝试运行 find 命令并添加上述过滤器,但它什么也不返回,如果我写入文件的内容并将其粘贴到 find 命令中,它就会成功。
我的 bash 文件中的相关行是:
local filter=$(cat $filter_files)
find_files=$(find "$path" -type f \( $filter \) )
你能向我解释一下我是否可以做我想做的事吗?如果是的话,我怎样才能让它发挥作用?
答案1
如果文件包含
-name "a.txt" -o -name "b.txt"
你跑
find "$path" -type f \( $(cat file) \)
然后,shellcat
通过分词运行输出,产生参数-name
, "a.txt"
, -o
, -name
, 和"b.txt"
,其中引号实际上是参数的一部分。 (请注意,引号也不会保护空格。像这样的输入字符串"foo bar"
将导致两个字段"foo
和bar"
。此外,拆分的结果将通过文件名生成/通配符,因此类似的内容foo*.txt
会在此时扩展,而不是按find
原样传递。)
这是因为分词相当简单,仅有的在空白处分割,确实如此不是通过完整的 shell 语法解析运行输出。 (如果确实如此,那将非常危险,因为它还意味着始终递归地运行任何扩展,包括命令替换。不可能相对安全地使用 shell 来处理任意数据。)
如果您想让文件中的部分经过完整的语法处理,您必须使用 运行命令eval
,例如:
eval 'find "$path" -type f \( '"$(cat file)"' \)'
这里,外壳处理完引号和命令替换后,eval
得到下面的字符串,然后将其作为shell命令进行处理,包括引号处理和所有扩展。
find "$path" -type f \( -name "a.txt" -o -name "b.txt" \)
(请注意,这意味着如果文件包含 eg "$(reboot)"
,该命令将运行。但是,我留待"$path"
内壳扩展,以便它不会被双重处理。)
您可以分多个步骤执行上述操作,例如,通过将文件中的单词分配给带有 的数组eval
,然后在最终命令中使用该数组。想法和注意事项是相同的。
您可能希望重新考虑文件的格式。即使例如将每个参数放在单独的行上(并取消引号)也会使其更易于解析。
例如,如果文件是:
-name
a.txt
-o
-name
foo bar.txt
您可以执行类似的操作,将readarray
每个输入行读取到单独的数组元素:
#!/bin/bash
readarray -t args < file
find "$path" -type f \( "${args[@]}" \)
(这仍然受到限制,因为它无法处理带有换行符的文件名,但这些情况可能比较罕见,并且也会产生其他问题。)
话又说回来,如果您需要的只是-name
匹配,您可以让文件仅列出模式,并-o -name
以编程方式生成样板文件。
也可以看看:
- https://mywiki.wooledge.org/WordSplitting
- 我们如何运行存储在变量中的命令?(包含相关问题和构建数组命令行的通用示例)
- 从参数构建 find 命令(包含一个更具体的构建
find
命令行的示例)
答案2
使用:
您需要单独的行,并-o
从文件中删除,以使其变得容易。
find "$path" -type f $(sed '1q;d' filter_files) -o $(sed '2q;d' filter_files)
或者:
单弦。
filter_conditions=$(<sed '1q;d' filter_files)
find_files=$(find "$path" -type f \( $filter_conditions \))