ls -l * 之间的区别| wc -l 和 ls -l | grep|厕所-l

ls -l * 之间的区别| wc -l 和 ls -l | grep|厕所-l

我有一个文件夹,里面有一个巨大的文件列表,我想计算它。我正在这样做ls -l *.json | wc -l,并且工作正常,直到列表明显变得足够大,以至于该命令不再起作用。

我后来尝试了一个ls -l | grep .json | wc -l,效果非常好。

所以我想知道两者有什么区别?我认为这是因为后者不会ls一次处理所有文件,而是将文件连续流式传输到 ,grep然后传输到wc?是这样吗,还是工作原理不同?

答案1

.json如果您想计算当前目录中具有扩展名的非隐藏文件的数量,您可以这样做:

(){echo $#} *.json(NoN)

N对于nullgloboN禁用我们在这里不需要的排序)。

ls -l | grep .json | wc -l由于多种原因是错误的:

  • .是匹配任何单个字符的正则表达式运算符。如果您想搜索为or或 的.json字符串。grep -F .jsongrep '\.json'grep '[.]json'
  • Xjson是在每行上寻找的。使用ls -l,您可以打印文件名、用户名、组名、符号链接的目标,所有这些都可以包含Xjson.
  • 文件名也可以包含除 0 和 of 之外的任何字节值/(链接目标可以包含/);其中包括换行符。因此,如果您有一个名为 that 的文件xjson\nyjson,则它是一个符号链接ajson\nbjson,如果没有-qls -l该文件将打印 3 行,所有行都将包含Xjson.如果某些文件名包含在当前语言环境中不形成有效字符的字节序列,您也可能会感到惊讶。
  • grep | wc -l一般可以替换为grep -c.

ls -l *.json | wc -l更糟糕的是。旁边arg 列表太长潜在问题@L.ScottJohnson 已经指出,还有:

  • 如果没有非隐藏.json文件,您还会收到0一个错误,zsh因为*.jsonglob 无法匹配。
  • 对于每个类型的参数目录ls -l列出它们的内容,因此如果您有一个dir.json目录,则其列表产生的所有行都将被计算在内。通常,您希望-d在将 glob 扩展传递给 时使用 a ls
  • 如果任何.json文件名以 开头-,则该文件名将被视为选项ls(特别是 GNU 或 busybox 实现,ls即使在非选项参数之后也接受选项)。
  • 如上所述,如果文件名或符号链接目标包含换行符,您将会遇到问题。

您可以使用以下方法修复大多数问题:

LC_ALL=C ls -qd -- *.json | wc -l

但在那里,所有实际工作都是由 shell 完成的。 shell 是一个扩展*.json匹配文件列表并将其传递给ls.ls仅用于将每个打印在单独的行上以供输入,wc以便可以对它们进行计数。ls还做了很多不必要的工作,例如stat()对每个进行系统调用以检查它是否存在(使用-l,它会对lstat()用户/组名称解析执行 a 和一些 uid/gid,以及readlink()s 表示符号链接),并再次对列表进行排序( shell 已经对*.json扩展进行了排序)。

shell 能够很好地自行计算该扩展。

对于(){echo $#} *.json(NoN),我们使用匿名函数,您也可以使用临时数组:files=(*.json(NoN)); echo $#files

另请注意,它只需要当前目录的内容来构建该列表,它不需要像ls以前那样尝试单独查找每个文件。

请注意,该语法特定于zsh. POSIX 中的等价物sh类似于:

set -- [*].json *.json
case $1$2 in
  ('[*].json*.json') shift 2;;
  (*) shift;;
esac
echo "$#"

(文件列表位于"$@"(已排序))。

答案2

如果扩展的文件列表太长,您的原始命令可能会失败,因为它依赖 shell 来扩展文件列表。

ls -l | grep .json | wc -l 

没问题,因为命令行很短(没有扩展)并且 grep 会逐行进行过滤。

请注意,点匹配任何字符,而不仅仅是点。使用 -F 表示固定字符串:

ls -l | grep -F .json | wc -l 

这样你就不会意外地匹配文件xjson或类似的愚蠢的东西。

该命令仍然有一些注意事项,以及其他(更强大的)方法来实现您的预​​期目标,但除了这些问题之外,这就是两个命令之间的区别:按文件名通配符过滤(即 shell 扩展)与管道-to-grep

相关内容