计算“locate”输出中非符号链接的文件数量

计算“locate”输出中非符号链接的文件数量

我正在尝试计算通过locate 命令传递的非符号链接文件的数量。我尝试了多种选择,认为这是最有希望的:

locate -r "$PWD.*\.c$" | xargs -0 -I{} test -f {} && echo "regular file" | wc -l

问题是它不起作用。

总共有 30 个文件,其中 1 个是符号链接,所以wc -l应该是29.

我尝试过xargs完全跳过:

locate -r "$PWD.*\.c$" | test -f && echo "regular file" | wc -l

我试过了不是符号链接:

locate -r "$PWD.*\.c$" | test ! -h && echo "regular file" | wc -l
locate -r "$PWD.*\.c$" | test ! -L && echo "regular file" | wc -l

locate管道输出和计算常规文件与符号链接数量的最有效方法是什么?


回复评论

有的人喜欢locate指挥,有的人喜欢find指挥。我希望我是不可知论者,但我喜欢locate尽可能使用。评论已发布,我想在问题中回复它们:

  • updatedb第一次运行需要 30 秒,但后续只需 4 秒。每 5 分钟运行cron一次是对每天一次默认的下意识反应。但笔记本电脑的 CPU 使用率只有 10% 到 20%,而且从来没有任何延迟。
  • 清除缓存后find需要 1 分 9 秒才能找到单个文件。
  • 清除缓存后locate需要 1 秒才能找到相同的文件。

以下是您可以在系统上复制的一些基准测试:

$ sudo -i
# sync; echo 1 > /proc/sys/vm/drop_caches; sync; echo 2 > /proc/sys/vm/drop_caches; sync; echo 3 > /proc/sys/vm/drop_caches; exit
logout

$ time locate .hidden.c | wc -l
1

real    0m0.790s
user    0m0.758s
sys     0m0.028s

$ sudo -i
# sync; echo 1 > /proc/sys/vm/drop_caches; sync; echo 2 > /proc/sys/vm/drop_caches; sync; echo 3 > /proc/sys/vm/drop_caches; exit
logout

$ time find / iname '.hidden.c'  2>/dev/null | wc -l
1888926

real    1m9.044s
user    0m5.158s
sys     0m15.004s

$ sudo -i
# sync; echo 1 > /proc/sys/vm/drop_caches; sync; echo 2 > /proc/sys/vm/drop_caches; sync; echo 3 > /proc/sys/vm/drop_caches; exit
logout

$ time sudo updatedb

real    0m29.323s
user    0m1.267s
sys     0m4.784s

$ time sudo updatedb

real    0m3.592s
user    0m0.479s
sys     0m1.211s

find毫无疑问比它更健壮,locatelocate速度要快很多倍,并且语法更容易记住。

确实,您必须记住运行sudo updatedb或一次性将参数传递-ulocate命令以更新数据库以包含今天创建的文件或排除今天删除的文件。但另一方面find你必须记住传递2>/dev/null参数。

我选择每五分钟cron运行一次,updatedb因为首先我的笔记本电脑工作不足,其次我很懒。

答案1

这个命令很接近:

locate -r "$PWD.*\.c$" | xargs -0 -I{} test -f {} && echo "regular file" | wc -l

问题:

  • 您将 nul 分隔输入与 xargs 一起使用,但locate不提供 nul 分隔输出。
  • && echo是针对locate | xargs整个管道运行的,而不是针对单个管道运行的test

尝试:

locate -0r "$PWD.*\.c$" | xargs -0 -I{} sh -c 'test -f "$1" && echo "regular file"' _  {} | wc -l
  • locate启用with 的nul 分隔输出-0
  • 结合testand echo(这可以通过参数循环来改进,sh -c以便每次调用处理多个文件sh

仍然存在正则表达式运算符出现在$PWD.

答案2

zsh

set -o extendedglob # best in ~/.zshrc
c_regular_files=(
  ${(0)^"$(locate -0 "${${PWD%/}//(#m)[]\\*?]/\\$MATCH}/*.c")"}(N.)
)
echo there are at least $#c_regular_files regular files whose name ends in .c
  • 我们需要转义[, ?,\*in $PWDfor ,不要将它们解释为通配符运算符(对于具有更多运算符(包括文件名中常见的 , )的 for 正则表达式locate,情况会更糟)-r.
  • $PWD==/必须特殊对待;使用$PWD而不是${PWD%/},我们将运行locate -0 "//*.c"它不会返回任何内容。
  • -0文件以 NUL 分隔(换行符不起作用,因为文件路径中允许换行符)。
  • .是为了常规的文件。与 相反[ -f,它排除常规文件的符号链接。如果您想要每个非符号链接.c文件(允许任何其他类型的文件,如目录、fifo、套接字...),请替换.^@.

无论如何,请注意,locate返回的列表基于上次locate更新数据库的时间,这可能无法反映当前的实际情况。

答案3

而不是解析locate( 这是脆弱的,可能会错过自上次更新数据库以来已更改的内容,或者无法使用的内容全部用户),使用find.

以下命令将查找.c当前目录中所有常规文件(不是符号链接)的文件:

find . -type f -name '*.c'

给定目录结构

.
|-- file-a.c
|-- file-b.c
|-- file-c.c
|-- file-d.c
|-- link-b.c -> file-b.c
`-- link-d.c -> file-d.c

这将返回

./file-a.c
./file-b.c
./file-c.c
./file-d.c

计算它们:

find . -type f -name '*.c' | wc -l

或者,如果您的文件名中包含换行符,

find .//. -name '*.c' -type f | grep -c //

对符号链接执行相同的操作将涉及将 更改-type f-type l

答案4

使用 GNU Parallel 时,它看起来像这样:

locate -r "$PWD.*\.c$" | parallel 'test -f {} && echo "regular file"' | wc -l

正如您所看到的,它与您最初的尝试非常接近。

如果点击次数少于 100 次,则可以使用 GNU Parallel 设置 $?失败作业的数量最多为 100(这无法扩展):

ls  *txt | parallel \! test -f {}
echo $?

如果您需要更快:

locate -r "$PWD.*\.c$" |
  perl -ne 'chomp; -l $_ or $s+= -f $_; END{print "$s\n"}'

或者组合:

locate -r "$PWD.*\.c$" |
  parallel --block 10k --pipe -q perl -ne 'chomp; -l $_ or $s+= -f $_; END{print "$s\n"}' |
  awk '{s+=$1} END {print s}'

相关内容