bash 中的命令替换声明语法 - 这两个中哪一个更好?

Esc*_*her 4 bash command-substitution variable

这是 bashfind循环的一部分,我想知道哪个语法更正确,为什么?

filename="$(echo "$i" | cut -c5-)";
filename=`echo "$i" | cut -c5-`;
Run Code Online (Sandbox Code Playgroud)

这两个函数都是为了获取文件名。

Mic*_*mer 5

猛砸手动注意到

POSIX $() 形式的命令替换已实现(请参阅命令替换),并且优先于 Bourne shell 的 ``(它也是为了向后兼容而实现的)。

So$(...)优先`...`于您正在编写的任何新的和 Bash 特定的内容¹。反引号替换不能很好地嵌套(你必须逃避它们),并且有一些稍微奇怪的引用行为。你可以$(... $(...) ...)随意嵌套很多没有问题。该手册将两者的行为描述为

当使用旧式反引号替换形式时,反斜杠保留其字面含义,除非后跟“$”、“`”或“\”。前面没有反斜杠的第一个反引号终止命令替换。使用 $(command) 形式时,括号之间的所有字符组成命令;没有被特殊对待。

通常被认为是最好的做法,所有的字符串扩展报价,以避免字分裂的问题,包括在其要求不严格的地方,所以"$(...)"也更好的做法在这里。

最后,来自同一羊毛页的两个相关部分:

  • 也是时候忘记了`...`。它与 Expansion 的语法不一致,并且在没有止痛药的情况下很难嵌套。使用$(...)来代替。
  • 看在上帝的份上,“多用引号!” 保护您的字符串和参数扩展免于分。如果你没有正确引用的话,分词会吃掉你的孩子。

¹实际上,即使在 POSIXsh脚本中,这些也被认为是最佳实践。周围仍然有较弱的炮弹,但您会知道是否会遇到它们。


Sté*_*las 5

关于与`...`vs无关的代码中的良好实践的评论$(...)

filename="$(echo "$i" | cut -c5-)"
Run Code Online (Sandbox Code Playgroud)

至少有 4 个潜在的警告(次要的,除非有利用它们的动机):

  • 您不能echo用于任意数据,因为您可能会遇到(取决于echo实现或环境)以反斜杠$i开头-或包含反斜杠的值的问题。处理任意数据时最好使用printf(here printf '%s\n' "$i")。
  • cut并且大多数文本过滤实用程序不太适合处理文件路径,因为它们作用于输入的每一行。因此,在这里,您要从每一行中$i删除前 4 个字符(换行符与文件名中的任何字符一样有效)。最好使用filename=${i#????}(尽管您可能还想考虑$i包含少于 4 个字符的情况)。
  • 命令替换(`...`$(...))从命令的输出中删除每个尾随换行符。因此,如果$i以换行符结尾,您将在$filename. ${i#????}也可以解决这个问题。
  • cut -c5-${i#????}删除 4 个字符。请注意,在某些语言环境中,字符可以由多个字节组成。您可能需要考虑到这一点。(另请注意,GNUcut尚不支持多字节字符cut -c与 相同cut -b))。