我正在尝试计算通过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
毫无疑问比它更健壮,locate
但locate
速度要快很多倍,并且语法更容易记住。
确实,您必须记住运行sudo updatedb
或一次性将参数传递-u
给locate
命令以更新数据库以包含今天创建的文件或排除今天删除的文件。但另一方面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
- 结合
test
andecho
(这可以通过参数循环来改进,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$PWD
for ,不要将它们解释为通配符运算符(对于具有更多运算符(包括文件名中常见的 , )的 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}'