【问题标题】:Count lines of code recursively, including compressed (zip) files递归计算代码行数,包括压缩 (zip) 文件
【发布时间】:2021-08-13 10:55:45
【问题描述】:

我使用以下 Bash 脚本来计算我的一个项目中的代码行数:

echo "--- CLIENT"
cd "/mypath/client"

# Count classes:
a=`find . -name \*.java -print | wc -l`
echo ""
echo "Number of Java classes: $a"

# Total count:
b=`find . -name \*.java -exec cat {} \; | wc -l`
echo ""
echo "Java lines: $b"

c=`find . -name \*.css -exec cat {} \; | wc -l`
echo ""
echo "CSS lines: $c"

d=`find . -name \*.json -exec cat {} \; | wc -l`
echo ""
echo "JSON lines: $d"

f=$((`find . -name \*.h -exec cat {} \; | wc -l` + `find . -name \*.m -exec cat {} \; | wc -l`))
echo ""
echo "iOS Objective-C lines: $f"

echo ""
echo "--- SERVER"
cd "/mypath/server"
# Count classes:
h=`find . -name \*.java -print | wc -l`
echo ""
echo "Number of Java classes: $h"

# Total count:
i=`find . -name \*.java -exec cat {} \; | wc -l`
echo ""
echo "Java lines: $i"


echo ""
echo "Total lines of code: $((b + c + d + e + f + i))"

cd ~

只要可以通过这种方式搜索所有源代码,此脚本就可以正常工作。现在我有一个不同的用例:一些源代码仍然可以通过这个脚本访问,其中一些在压缩的 zip 文件中(位于“/mypath/client”的各个子文件夹中)。这些 zip 文件可以在根目录或其中的各种子文件夹中包含源代码。

我想可以调整我的脚本以考虑计数中的压缩文件,但我不知道该怎么做。

【问题讨论】:

  • 为简化起见,我的答案只考虑如何修改“a=find . -name \*.java -print | wc -l”这一行就足够了,其他的一切都会随之而来。
  • 您可以将 zip 特定部分添加到您的脚本中。类似j=`find . -name \*.zip -exec unzip -l {} \; | grep '\.java$' | wc -l`

标签: bash


【解决方案1】:

计数文件

当您搜索.xyz 文件时,同时搜索.zip 文件并搜索它们的文件列表。 您可以使用zipinfo archive.zip 列出 zip 存档中的所有文件名。 zipinfo 还支持通配符以仅打印匹配的文件名。例如,zipinfo archive.zip '*.java' 仅打印以 .java 结尾的文件名。

find . -name \*.java -print \
    -o -name \*.zip -exec zipinfo -1 {} '*.java' \; |
wc -l

此命令假定文件名不包含换行符。

计数线

您可以打印压缩文件,而无需使用unzip -p archive.zip file1 file2 ... 显式提取它们。此命令还接受通配符。

顺便说一句:您可以使用函数大大简化脚本,因为find . -name \*.xyz -exec cat {} \; | wc -l 通常是相同的,除了xyz。此外,-exec cat {} +-exec cat {} \; 快得多。

#! /bin/bash

countLines() {
  local ext=$1
  find . -name "*.$ext" -exec cat {} + \
      -o -name \*.zip -exec unzip -p {} "*.$ext" \; |
  wc -l
}

for ext in java css json; do
  echo "$ext lines: $(countLines "$ext")"    
done

如果没有.java 文件,unzip -p archive.zip '*.java' 可能会打印警告caution: filename not matched: *.java。您可以通过在find 命令后添加2> /dev/null 来抑制这种情况。

请记住,这种方法效率很低。 find 必须为每个文件扩展名运行。压缩文件也被多次读取。最好先过滤掉所有要检查的文件,然后对所有文件运行wc -l,然后汇总它们的行数。

【讨论】:

  • 谢谢!在我接受你的答案之前,我需要做一些实验并研究它,因为它似乎给我的数值结果比我预期的要多。我对 Bash 的了解仅限于基本知识,我需要一些时间来了解您所写的内容。
  • 我已经彻底和广泛地尝试了您的解决方案。在简单的情况下,它可以工作。在非常复杂的项目中,出现问题,有时报告的数字远高于真实的数字,有时即使没有代码或只有几行代码,也会报告数万行代码。我不知道为什么会这样,有些问题,但我不知道是什么。
  • 通过解决方法解决:我使用*.com/a/22384233 中指示的命令将所有内容解压缩到一个临时文件夹中,并应用我的问题中指示的相同计算脚本。这样计数是正确的,和我预期的一样。
  • @FrancescoGalgani 感谢您的测试。我想我找到了问题所在。 unzip -p 接受通配符。可能是在有问题的项目中,您的一个压缩源文件的名称包含*?[] 之一吗?如果是这样,那可能会导致打印更多文件。有趣的是,这个修复让整个程序变得更简单了。
  • 我接受了您的回答:修复后,即使在复杂的项目中,计算现在也是正确的。谢谢。