正确引用 bash 别名定义

Properly quote bash alias definition

我试图将以下命令放入 bash 别名中。该命令本身运行良好,但当我尝试为其设置别名时,出现以下错误:

命令

find . -maxdepth 1 -mindepth 1 -type d -exec sh -c 'echo "$(find "{}" -type f | wc -l)" {}' \; | sort -nr

别名

alias csfiles='find . -maxdepth 1 -mindepth 1 -type d -exec sh -c 'echo "$(find "{}" -type f | wc -l)" {}' \; | sort -nr'

错误:

-sh: alias 0: not found
-sh: alias {} \; | sort nr: not found

我认为这意味着我没有正确使用引号,但我无法确定正确的组合。帮忙?

您可以在定义周围使用双引号,如下所示:

alias foo="find . -maxdepth 1 -mindepth 1 -type d -exec sh -c 'echo \"$(find \"{}\" -type f | wc -l)\" {}' \; | sort -nr"

定义中的每个文字 " 都被转义:\".

注意:您还需要转义内部命令替换,以防止它在别名定义时扩展。像这样... $(...)


作为对 chepners 评论的后续,您应该将文件名作为参数传递给内部查找命令。否则,如果您的其中一个文件夹的名称中包含 ",您将 运行 遇到问题:

alias foo="find . -maxdepth 1 -mindepth 1 -type d -exec bash -c 'echo \"$(find \"${1}\" -type f | wc -l) \"${1}\" \"' -- \"{}\" \; | sort -nr"

你的外部 find 不会做任何你不能用简单的 glob 做的事情。这消除了一层引号(连同找到的每个目录的 sh 过程)。

# Ignoring the issue of assuming no file name contains a newline
for d in ./*/; do
   echo "$(find "$d" -type f | wc -l) $d"
done

只需定义一个shell函数来消除强加给alias的参数的第二层。

csfiles () {
  for d in ./*/; do
    echo "$(find "$d" -type f | wc -l) $d"
  done
}

其余对 find 的调用也可以替换为 for 循环,消除每个文件名一行的有问题的假设:

csfiles () {
  for d in ./*/; do
    echo "$(for f in "$d"/*; do [ -f "$f" ] && echo; done | wc -l) $d"
  done
}

你可以保留 find 如果它支持 -printf 主,因为你不关心文件的实际名称,只关心每个文件只有一行输出。

csfiles () {
  for d in ./*/; do
    echo "$(find "$d" -type f -printf . | wc -l) $d"
  done
}