该命令 find 不适用于 sh 文件中的 -name 选项

rai*_*man 4 shell find shell-script basename

我正在使用以下命令来检索名称包含的文件数svjson远程服务器中给定目录中的文件数:

nbs_files=`ssh -q -i ${sshkey} ${user}@${server} "find ${path}/ -maxdepth 1 -mindepth 1 -type f -name '*sv*' -o -name '*.json' -exec basename {} \; | wc -l"` 
Run Code Online (Sandbox Code Playgroud)

此命令仅返回.json文件数,而sv名称中包含的文件存在于${path}.

当我删除该-o -name '*.json'部分时,该命令运行良好,并返回名称中包含“sv”的文件数。

有谁知道如何修改命令以检索sv其名称中包含的文件.json以及带有扩展名的文件?

Gor*_*son 5

问题在于find表达式中的和/或运算符优先级。具体来说,相邻测试之间的隐式 AND 比-o两个名称测试之间的OR ( )具有更高的优先级。所以测试表达式被解析为:

    -maxdepth 1 -mindepth 1 -type f -name '*sv*'
OR
    -name '*.json' -exec basename {} \;
Run Code Online (Sandbox Code Playgroud)

...并且由于-name '*.json'是唯一一个与 同一个分支的一部分-exec,因此-exec唯一运行的 json 文件。

解决方案是在-name测试周围使用显式括号覆盖正常优先级:

nbs_files=$(ssh -q -i ${sshkey} ${user}@${server} "find ${path}/ -maxdepth 1 -mindepth 1 -type f '(' -name '*sv*' -o -name '*.json' ')' -exec basename {} \; | wc -l")
Run Code Online (Sandbox Code Playgroud)

顺便说一句,我还冒昧地将反引号替换为$( )- 它们是更现代的选择,更易于阅读,并且没有反引号所具有的那种奇怪的转义异常。请参阅此问题BashFAQ #82

  • 当然,人们必须想知道 OP 在计算结果之前必须保证运行 basename 的目录名称。如果没有嵌入的换行符,则删除 `-exec basename {} \;` 将得到相同的结果,但不会为每个匹配的文件运行一个进程。如果 OP 正在使用 gnu find - 可能给定了 `linux` 标签,那么 `nbs_files=$(ssh -q -i ${sshkey} ${user}@${server} "find ${path}/ -maxdepth 1 -mindepth 1 -type f '(' -name '*sv*' -o -name '*.json' ')' -printf '%f\n' | wc -l")` 将避免每个匹配文件的进程即使在目录名称中嵌入了换行符。 (2认同)