目前,要计算宽度超过 80 列的行,我使用以下命令:
$ git grep -h -c -v '^.\{,80\}$' **/*.{c,h,p{l,y}} \
|awk 'BEGIN { i=0 } { i+=$1 } END { printf ("%d\n", i) }'
44984
不幸的是,该存储库使用制表符进行缩进,因此grep
模式不准确。无论如何,是否有regex
像 8 个字符的标准宽度一样的处理选项卡wc -L
?
出于这个问题的目的,我们可以假设贡献者有足够的纪律来一致缩进,或者他们有git commit
钩子代替纪律。
出于与性能相关的原因,我更喜欢在内部工作的解决方案
git-grep(1)
或其他grep
工具,无需预处理文件。
答案1
通过管道传输文件来预处理文件expand
。该expand
实用程序将适当地扩展制表符(在每 8 个字符处使用标准制表符停止位)。
find . -type f \( -name '*.[ch]' -o -name '*.p[ly]' \) -exec expand {} + |
awk 'length > 80 { n++ } END { print n }'
答案2
GNUwc -L
不会将 TAB 视为 8 个字符,而是将 TAB 视为在终端中显示,每 8 列有一个 TAB 停止位,因此“宽度”范围为 1 到 8 个字符,具体取决于它们在行中的位置。wc -L
还考虑其他字符的显示宽度(无论它们是 0、1 还是 2 列宽)并“正确”处理\f
和。\r
$ printf 'abcde\t\n' | wc -L
8
在这里,您可以使用expand
(默认情况下也假设每 8 列制表符停止一次,尽管您可以使用选项更改它)将这些制表符扩展为空格:
git grep -h '' ./**/*.{c,h,p{l,y}} | expand | tr '\f\r' '\n\n' | grep -cE '.{81}'
(将 CR(发送到终端时将光标移回行首)和 FF(某些显示设备将其理解为分页符)转换为 LF 以获得与 相同的行为,wc -L
但忽略其他行为无论如何,我们无法判断它们会对显示宽度产生什么影响)。
这涵盖了制表符,但不包括单角或双角字符。请注意expand
,如果存在多字节字符(更不用说零宽度或双宽度字符),当前的GNU 实现无法正确扩展制表符。
$ printf 'ééééé\t\n' | wc -L
8
$ printf 'ééééé\t\n' | expand | wc -L
11
还请注意,默认情况下会跳过隐藏文件或隐藏目录中的文件。由于括号扩展会扩展到多个 glob,因此如果其中一个 glob 不匹配,./**/*.{c,h,p{l,y}}
您也会收到错误(使用zsh
或会导致严重错误)。bash -O failglob
对于zsh
,您可以使用./**/*.(c|h|p[ly])(D.)
which is一glob,其中D
包含隐藏文件并.
限制常规的文件。
对于考虑字符实际宽度的解决方案(假设所有文本文件都以区域设置的字符编码进行编码),您可以使用:
git grep -h '' ./**/*.(c|h|p[ly])(.) | tr '\r\f' '\n\n' |
perl -Mopen=locale -MText::Tabs -MText::CharWidth=mbswidth -lne '
$n++ if mbswidth(expand($_)) > 80;
END{print 0+$n}'
请注意,至少在 GNU 系统上,mbswidth()
将控制字符视为宽度为-1
且 1 为expand()
。我们假设文件中没有找到除 CR、NL、TAB、FF 之外的控制字符。
答案3
如果我们可以根据你的评论假设标签字符只会出现在行首,那么我们可以将替代字符计算为至少 80 个字符。
- 无制表符,至少 81 个字符
- 一个选项卡,至少 73 个字符
- 两个选项卡,至少 65 个字符
- ETC。
由此产生的混乱如下,您的awk
语句将各个行计数相加以提供总计
git grep -hcP '^(.{81,}|\t.{73,}|\t{2}.{65,}|\t{3}.{57,}|\t{4}.{49,}|\t{5}.{41,}|\t{6}.{33,}|\t{7}.{25,}|\t{8}.{17,}|\t{9}.{9,}|\t{10}.)' **/*.{c,h,p{l,y}} |
awk '{ i+=$1 } END { printf ("%d\n", i) }'
答案4
解决方案与前任(从六)。虽然很慢。
由于 vi 能够正确处理 UTF-8 数据:
它可以将制表符扩展为空格,将控制字符计为 1,\r
\t
\f
\v
正确处理并处理大多数有效的UNICODE 值。包括组合重音 (NKC) 和分解重音 (NKD),以及来自西里尔文、阿拉伯文、希腊文、中文等的字符。
$ cat script.sh
#!/bin/bash --
declare -i count=0
for i do
# Set ex script in one variable
a='set expandtab " Expand tabs to spaces
r '"$i"' " Read original file
g/^.\{,80\}$/d " Remove all lines shorter than the value used
wq " Quit '
o=outfile; :>"$o" # Clean output file
ex -s "$o" <<<"$a" # process lines in $i file
count+=$(wc -l <"$o") # count and accumulate number of lines.
done
echo "$count"
调用脚本为:
$ script.sh **/*.{c,h,p{l,y}}
44984