为什么 xargs 无法在子 shell 中正确调用 basename?

为什么 xargs 无法在子 shell 中正确调用 basename?

我遇到了以下问题:

user@machine:/$ echo ./dir/fil | xargs -I {} bash -c "echo $(basename {})"
./dir/fil

为什么不只是打印fil

因此basename似乎获得了预期的参数./dir/fil,但不知何故假设它是整个文件名。相反,当与dirname它一起使用时,只会打印出.。感觉好像/s 以某种方式被转义了。

我为何关心?

我实际上需要运行这样的操作:

find -name "*.foo" | xargs -I {} bash -c "cd $(dirname \"{}\"); thirdpartytool $(basename \"{}\") 2>&1 > /dev/null | sort"
  • 如果任何调用返回一个非零的返回代码,我需要一个非零的返回代码thirdpartytool,所以find ... -exec ...对我来说不起作用。
  • 我需要输出重定向(丢弃 stdout 并对 stderr 进行排序)所以我需要调用另一个 shell。
  • 我需要cd因为thirdpartytool必须从文件所在的目录调用,所以我需要dirname并且basename在子shell中。

答案1

echo ./dir/fil | xargs -I {} bash -c "echo $(basename {})"

这里$(basename {})是双引号,因此xargs它在运行之前就被当前 shell 扩展了。 的输出basename {}{},因此命令变成:

echo ./dir/fil | xargs -I {} bash -c "echo {}"

如果你用单引号括住 shell 代码,情况就会有所不同。嵌入{}shell 代码是错误的. 您应该将扩展的结果{}作为位置参数传递。

最终,您要使用find。真正强大的命令将采用以下形式find … -print0 | xargs -0 …(如果支持)或find … -exec …

如果我理解你的目标正确的话,你的命令将是这样的:

find . -name "*.foo" -print0 \
| xargs -0 -L1 sh -c 'cd "$(dirname "$1")" && thirdpartytool "$(basename "$1")" 2>&1 >/dev/null | sort' sh

在哪里

  • 我修正了引用,
  • 我确保thirdpartytool只有cd成功后才会运行,
  • 我使用它是sh因为 shell 代码中没有任何特定于 Bash 的内容。

补充笔记:

相关内容