我正在尝试按 LoC 对文件目录进行排序。
但sort
如果将线路通过管道输入,则似乎不会执行任何操作:
paths=`find ./src/ | egrep "\.(cpp|h)$"`
for path in $paths; do
wc -l $path | sort -n;
done
结果如下(按 预先排序find
,但wc
数字被忽略):
50 /a/a.cpp
10 /a/a.h
200 /b/b.cpp
13 /b/b.h
...
如果我对文件而不是管道使用排序:
for path in $paths; do
wc -l $path >> test.txt;
done
sort -n test.txt
它做工作:
```bash
10 /a/a.h
13 /b/b.h
50 /a/a.cpp
200 /b/b.cpp
...
为什么管道版本不起作用?
答案1
您将每个人wc
的输出sort
分别通过管道传输到 , 。如果移动管道来处理循环的完整输出,它应该可以工作:
paths=`find ./src/ | egrep "\.(cpp|h)$"`
for path in $paths; do
wc -l $path
done | sort -n
你应该避免循环find
的输出;您也不需要使用egrep
to 过滤find
的输出。您可以使用以下方式处理以上所有内容
find ./src/ \( -name '*.cpp' -o -name '*.h' \) -exec wc -l {} \; | sort -n
或者更有效,如果你不介意有一个“总”行,
find ./src/ \( -name '*.cpp' -o -name '*.h' \) -exec wc -l {} + | sort -n
(如果您的文件名包含换行符,这仍然不太有效。)
答案2
您的第一个循环对每个循环的单行输出wc -l
分别进行排序,并一个接一个地输出。不起作用(这是预期的!)。
第二种方法首先聚合所有wc
呼叫的所有线路,然后对它们进行排序:这是正确的方法。中间是否有文件不是这里的问题 - 问题是在你的第一个循环中你实际上没有对任何东西进行排序。
所以,
( for path in $paths; do
wc -l $path
done ) | sort -n
应该管用。
您的find
调用很奇怪,因为它使用egrep来过滤输出(这将导致有趣的结果,就像您对以.cpp结尾的文件夹进行操作一样,因为您有时会在例如CMake构建中找到它们)而不是简单地find -type f '(' -iname '*.cpp' -o -iname '*.h' ')'
;然而,我不鼓励你find
在这里一起使用,因为带有空格(非常常见)、换行符等的文件名会无缘无故地破坏这一切。
相反,使用你的 shell(我猜是 bash)直接给你的:
shopt -s nullglob ## don't fail on empty globs
shopt -s globstar
for path in **/**.{h,cpp} ; do
wc -l "${path}"
done | sort -n
事实上,我们可以让它变得更短:
shopt -s nullglob ## don't fail on empty globs
shopt -s globstar
wc -l **/*.{h,cpp} | sort -n
答案3
find
对于,wc
和实用程序的 GNU 实现head
,假设文件路径不包含换行符:
{
find . '(' -name '*.h' -o -name '*.cpp' ')' -print0
printf '%s\0' /dev/null
} |
wc -l --files0-from=- |
head -n -2 | # remove up to 2 trailing lines to remove the /dev/null
# and possibly "total" lines
sort -n
与该-exec wc -l {} +
方法相反,该方法保证仅输出一个“总”行。
我们仍然有一个问题,如果传递多个文件,则wc
仅输出一行。total
在这里,我们通过添加一个额外的内容来解决这个问题/dev/null
,并在最后删除它。