问题
我有一个充满文件的 git 存储库,其中大部分是文本。
我想知道有
n
以以下结尾的文件中的代码行.py
m
以以下结尾的文件中的代码行.md
o
以以下结尾的文件中的代码行.yaml
p
文件中的代码行没有扩展名- ETC
笔记:
- 我希望这是递归的,检查文件夹内部
- 我想排除
.git
顶级文件夹中的目录 - 我想忽略二进制文件是可能的(注意我有一些没有扩展名的文本文件,以及一些没有扩展名的二进制文件)
- 我想不区分大小写。组
.csv
与.CSV
- 我想忽略空行(或仅空白行)
- 如果有像我这样的文件,
myfile.yaml.j2
不在乎它是否被计入.j2
组或.yaml.j2
.
答案1
尝试这个:
find ./ -not -path "./.git/*" -type f -exec wc -l {} + |
awk '{print tolower($0)}' |
sed -e '$ d' |
sed -e "s#/.*/##g" |
sed -e "s/\./ \./g" |
awk '
{ if ( NF <= 2 ) { count["none"] += $1 } else { count[$NF] += $1 } }
{ next }
END { for (group in count) printf("%d%s%s\n", count[group], OFS, group) }
' |
sort -n
细分:
find ./
递归查找该目录下的对象-not -path "./.git/*"
排除.git
-type f
文件而不是目录-exec wc -l {} +
对于每个文件,运行字数统计实用程序 (wc
)。这包括空行,因此不满足问题的所有要求。awk '{print tolower($0)}'
变为小写sed -e '$ d'
删除最后一行,这是所有文件的行总和sed -e "s#/.*/##g"
删除文件的路径,例如,a/something.egg/blah
应该算作无扩展名,而不是.egg/blah
扩展名sed -e "s/\./ \./g"
搜索/替换.
为.
,因此文件扩展名是它自己的单词awk '{ if ( NF <= 2 ) { count["none"] += $1 } else { count[$NF] += $1 } } { next } END { for (group in count) printf("%d%s%s\n", count[group], OFS, group) }'
这是一件大事。awk
功能强大,但不是超级清晰count
是一本字典if (NF <= 2)
如果少于 3 个“单词”,即没有扩展名count["none"] += $1
增加字典中的一个元素,键是字符串文字none
,通过添加该文件中的行数来增加它,这是第一个单词,这是$1
count[$NF] += $1
字典中增加一个元素,key是$NF
(行中的最后一个单词),这是扩展名,by$1
(行中的第一个单词),是这个文件的行数{ next }
对所有行重复for (group in count)
循环for
、内联printf(...)
将输出字符串格式化为number extension
;例如,123 .abc
(如果以 结尾的文件有 123 行.abc
)
sort -n
按升序对结果进行排序;-n
表示按数字排序,而不是字符串
答案2
如果我理解正确,并且我的测试很好,我建议这样做(假设您想跳过隐藏的目录和文件,请告诉我是否情况并非如此):
shopt -s globstar
declare -A arr
for f in test/**; do
# if a directory, skip
[[ -d "$f" ]] && continue
lines=0
# strip the extension
ext="${f##*.}"
# convert it to lowercase
ext="${ext,,}"
# if no dot in the name, extension is "empty"
[[ ! $(basename "$f") =~ \. ]] && ext="empty"
# count the lines
lines=$(wc -l "$f"| cut -d' ' -f1)
# if lines equals to 0, skip
[[ $lines -eq 0 ]] && continue
# append the number of line to the array
lines=$(( "${arr[$ext]}"+$lines ))
arr[$ext]=$lines
done
# loop over the array
for n in ${!arr[@]}; do
echo "files $n: total lines ${arr[$n]}"
done
输出(来自我的示例文件):
files yaml: total lines 3
files md: total lines 3
files empty: total lines 4
files csv: total lines 6
files py: total lines 5
答案3
我将其分解为函数以使其更易于理解:
#!/bin/bash
# For the next two functions, we will use "-print0", which will print out \0 instead of \n.
# This will help prevent whitespace problems when piping the filenames into xargs.
find_extension()
{
find "$1" -type f -name "*.$2" -print0 2>/dev/null
}
find_no_extension()
{
find "$1" -type f -regex '^.*/[^.]+$' -print0 2>/dev/null
}
concat_files()
{
xargs -0 cat
}
delete_empty_lines()
{
sed -E '/^[[:space:]]*$/d'
}
line_count_of_files()
{
concat_files | delete_empty_lines | wc -l
}
print_usage()
{
echo "Usage: $0 [EXTENSION]... [SEARCH_DIRECTORY]";
}
NUMBER_OF_EXTENSIONS=$(($# - 1))
SEARCH_DIR="${*: -1}"
if [ $# -lt 2 ];
then
echo "Not enough parameters.";
print_usage;
exit 1;
fi
if ! [ -d "$SEARCH_DIR" ];
then
echo "$SEARCH_DIR does not exist, or is not a directory."
print_usage;
exit 1;
fi
for EXTENSION in "${@:1:$NUMBER_OF_EXTENSIONS}";
do
printf ".$EXTENSION: %s\n" $(find_extension "$SEARCH_DIR" "$EXTENSION" | line_count_of_files)
done
printf "No extension: %s\n" $(find_no_extension "$SEARCH_DIR" | line_count_of_files)
这更像是一个通用脚本,可让您指定要搜索的任意文件扩展名。但它总是会搜索没有扩展名的文件。
您应该将其保存到一个文件中,授予它可执行权限,然后将其放入您的 PATH 中。假设您将其命名为 count_lines.sh。你可以这样称呼它:count_lines.sh py md yaml ~/Code
。这将在目录中搜索以、和~/Code
结尾的文件,以及根本没有扩展名的文件。您可以选择任意数量的扩展名进行搜索,只需确保至少有一个即可。.py
.md
.yaml