按 LoC 对文件进行排序 - 从 wc -l 进行管道传输时排序不起作用

按 LoC 对文件进行排序 - 从 wc -l 进行管道传输时排序不起作用

我正在尝试按 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的输出;您也不需要使用egrepto 过滤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,并在最后删除它。

相关内容