抑制 `wc -l` 输出中的摘要信息
Suppressing summary information in `wc -l` output
我使用命令 wc -l
计算文本文件中的行数(我还想通过管道对所有内容进行排序),如下所示:
wc -l $directory-path/*.txt | sort -rn
输出包括"total"行,这是所有文件行的总和:
10 total
5 ./directory/1.txt
3 ./directory/2.txt
2 ./directory/3.txt
有什么办法可以抑制这个总结行吗?或者更好的是,改变摘要行的措辞方式?例如,单词 "lines" 代替“10”,单词 "file".
代替 "total"
你能用另一个厕所吗?
POSIX wc
(man -s1p wc
) 显示
如果指定了多个输入文件操作数,则应写入额外的一行,其格式与其他行相同,但应写入单词 total(在 POSIX 语言环境中)路径名和每列的总数应酌情书写。这样的附加行(如果有的话)写在输出的末尾。
你说 Total 行是第一行,手册说它是最后一行,而其他 wc 根本不显示它。删除第一行或最后一行是危险的,所以我会 grep -v
带有总计的行(在 POSIX 语言环境中......),或者只是 grep 所有其他行的斜杠:
wc -l $directory-path/*.txt | grep "/"
这实际上相当棘手。
我基于 wc
命令的 GNU coreutils 版本。请注意,total
行通常是最后打印的,而不是最先打印的(请参阅我对该问题的评论)。
wc -l
为每个输入文件打印一行,由文件中的行数和文件名组成。 (如果没有文件名参数,文件名将被省略;在这种情况下,它计算标准输入中的行数。)
当且仅当存在多个文件名参数时,它才会打印包含总行数和单词 total
的最后一行。文档表明无法禁止该摘要行。
除了它前面有其他输出之外,该行与名称恰好为 total
.
的文件的输出没有区别
所以要可靠地过滤掉 total
行,您必须读取 wc -l
的所有输出,并且仅当输出的总长度大于时才删除最后一行1.(如果文件名中包含换行符,即使这样也会失败,但您可以忽略这种可能性。)
更可靠的方法是对每个文件分别调用 wc -l
,避免 total
行:
for file in $directory-path/*.txt ; do wc -l "$file" ; done
如果你想对输出进行排序(你在评论中提到但在你的问题中没有提到):
for file in $directory-path/*.txt ; do wc -l "$file" ; done | sort -rn
如果您碰巧知道没有名为 total
的文件,一个快速而简单的方法是:
wc -l $directory-path/*.txt | grep -v ' total$'
如果您想 运行 wc -l
所有文件,然后过滤掉 total
行,这里有一个 bash 脚本可以完成这项工作。根据需要调整 *.txt
。
#!/bin/bash
wc -l *.txt > .wc.out
lines=$(wc -l < .wc.out)
if [[ lines -eq 1 ]] ; then
cat .wc.out
else
(( lines-- ))
head -n $lines .wc.out
fi
rm .wc.out
另一种选择是这个 Perl 单行代码:
wc -l *.txt | perl -e '@lines = <>; pop @lines if scalar @lines > 1; print @lines'
@lines = <>
将所有输入都输入到一个字符串数组中。 pop @lines
如果有多于一个,则丢弃最后一行,即如果最后一行是 total
行。
程序wc,当有两个或两个以上文件时总是显示总数(wc.c的片段):
if (argc > 2)
report ("total", total_ccount, total_wcount, total_lcount);
return 0;
同样最简单的方法是只对一个文件使用 wc 并找到当前 - 一个接一个 - 文件到 wc:
find $dir -name '*.txt' -exec wc -l {} \;
或由 liborm 指定。
dir="."
find $dir -name '*.txt' -exec wc -l {} \; | sort -rn | sed 's/\.txt$//'
不是最优化的方式,因为您可以使用 cat
、echo
、coreutils
、awk
、sed
、[=17= 的组合], 等等,但这会得到你想要的:
wc -l ./*.txt | awk 'BEGIN{print "Line\tFile"}1' | sed '$d'
wc -l ./*.txt
将提取行数。 awk 'BEGIN{print "Line\tFile"}1'
将添加 header 标题。 1
对应于标准输入的第一行。 sed '$d'
将打印除最后一行以外的所有行。
示例结果
Line File
6 ./test1.txt
1 ./test2.txt
仅使用 grep -c
的简单性
由于这些问题,我很少在我的脚本中使用 wc -l
。我改用 grep -c
。虽然它不如 wc -l
高效,但我们无需担心其他问题,例如摘要行、白色 space 或分叉额外进程。
例如:
/var/log# grep -c '^' *
alternatives.log:0
alternatives.log.1:3
apache2:0
apport.log:160
apport.log.1:196
apt:0
auth.log:8741
auth.log.1:21534
boot.log:94
btmp:0
btmp.1:0
<snip>
单个文件非常简单:
line_count=$(grep -c '^' my_file.txt)
性能比较:grep -c
vs wc -l
/tmp# ls -l *txt
-rw-r--r-- 1 root root 721009809 Dec 29 22:09 x.txt
-rw-r----- 1 root root 809338646 Dec 29 22:10 xyz.txt
/tmp# time grep -c '^' *txt
x.txt:7558434
xyz.txt:8484396
real 0m12.742s
user 0m1.960s
sys 0m3.480s
/tmp/# time wc -l *txt
7558434 x.txt
8484396 xyz.txt
16042830 total
real 0m9.790s
user 0m0.776s
sys 0m2.576s
您可以像这样使用 GNU Parallel 非常简洁地解决它(以及许多其他似乎需要 for
循环的问题):
parallel wc -l ::: tmp/*txt
示例输出
3 tmp/lines.txt
5 tmp/unfiltered.txt
42 tmp/file.txt
6 tmp/used.txt
还有 sed
解决方案!
1。简短快捷
由于总计在最后一行,$d
是删除最后一行的sed命令。
wc -l $directory-path/*.txt | sed '$d'
2。添加 header 行:
wc -l $directory-path/*.txt | sed '$d;1ilines total'
很遗憾,没有对齐。
3。使用对齐方式:将左列格式化为 11 个字符宽度。
wc -l $directory-path/*.txt |
sed -e '
s/^ *\([0-9]\+\)/ /;
s/^ *\([0-9 ]\{11\}\) / /;
/^ *[0-9]\+ total$/d;
1i\ lines filename'
会做这份工作
lines file
5 ./directory/1.txt
3 ./directory/2.txt
2 ./directory/3.txt
4。但是,如果您的 wc
版本真的可以将总计放在第一行:
这个是为了好玩,因为我不相信有 wc
版本把总数放在第一行,但是...
此版本在所有地方删除 total 行,并在输出顶部添加 header 行。
wc -l $directory-path/*.txt |
sed -e '
s/^ *\([0-9]\+\)/ /;
s/^ *\([0-9 ]\{11\}\) / /;
1{
/^ *[0-9]\+ total$/ba;
bb;
:a;
s/^.*$/ lines file/
};
bc;
:b;
1i\ lines file' -e '
:c;
/^ *[0-9]\+ total$/d
'
这更复杂,因为我们不会删除第一行,即使它是 总 行。
类似于,您也可以使用带有显式分隔符的xargs
:
ls | xargs -I% wc -l %
然后 xargs
明确不会将所有输入发送到 wc
,而是一次发送一个操作数行。
最短答案:
ls | xargs -l wc
这是一份 tailor-made 负责人的工作:
wc -l | head --lines=-1
这样,您仍然可以在一个过程中运行。
我使用命令 wc -l
计算文本文件中的行数(我还想通过管道对所有内容进行排序),如下所示:
wc -l $directory-path/*.txt | sort -rn
输出包括"total"行,这是所有文件行的总和:
10 total
5 ./directory/1.txt
3 ./directory/2.txt
2 ./directory/3.txt
有什么办法可以抑制这个总结行吗?或者更好的是,改变摘要行的措辞方式?例如,单词 "lines" 代替“10”,单词 "file".
代替 "total"你能用另一个厕所吗?
POSIX wc
(man -s1p wc
) 显示
如果指定了多个输入文件操作数,则应写入额外的一行,其格式与其他行相同,但应写入单词 total(在 POSIX 语言环境中)路径名和每列的总数应酌情书写。这样的附加行(如果有的话)写在输出的末尾。
你说 Total 行是第一行,手册说它是最后一行,而其他 wc 根本不显示它。删除第一行或最后一行是危险的,所以我会 grep -v
带有总计的行(在 POSIX 语言环境中......),或者只是 grep 所有其他行的斜杠:
wc -l $directory-path/*.txt | grep "/"
这实际上相当棘手。
我基于 wc
命令的 GNU coreutils 版本。请注意,total
行通常是最后打印的,而不是最先打印的(请参阅我对该问题的评论)。
wc -l
为每个输入文件打印一行,由文件中的行数和文件名组成。 (如果没有文件名参数,文件名将被省略;在这种情况下,它计算标准输入中的行数。)
当且仅当存在多个文件名参数时,它才会打印包含总行数和单词 total
的最后一行。文档表明无法禁止该摘要行。
除了它前面有其他输出之外,该行与名称恰好为 total
.
所以要可靠地过滤掉 total
行,您必须读取 wc -l
的所有输出,并且仅当输出的总长度大于时才删除最后一行1.(如果文件名中包含换行符,即使这样也会失败,但您可以忽略这种可能性。)
更可靠的方法是对每个文件分别调用 wc -l
,避免 total
行:
for file in $directory-path/*.txt ; do wc -l "$file" ; done
如果你想对输出进行排序(你在评论中提到但在你的问题中没有提到):
for file in $directory-path/*.txt ; do wc -l "$file" ; done | sort -rn
如果您碰巧知道没有名为 total
的文件,一个快速而简单的方法是:
wc -l $directory-path/*.txt | grep -v ' total$'
如果您想 运行 wc -l
所有文件,然后过滤掉 total
行,这里有一个 bash 脚本可以完成这项工作。根据需要调整 *.txt
。
#!/bin/bash
wc -l *.txt > .wc.out
lines=$(wc -l < .wc.out)
if [[ lines -eq 1 ]] ; then
cat .wc.out
else
(( lines-- ))
head -n $lines .wc.out
fi
rm .wc.out
另一种选择是这个 Perl 单行代码:
wc -l *.txt | perl -e '@lines = <>; pop @lines if scalar @lines > 1; print @lines'
@lines = <>
将所有输入都输入到一个字符串数组中。 pop @lines
如果有多于一个,则丢弃最后一行,即如果最后一行是 total
行。
程序wc,当有两个或两个以上文件时总是显示总数(wc.c的片段):
if (argc > 2)
report ("total", total_ccount, total_wcount, total_lcount);
return 0;
同样最简单的方法是只对一个文件使用 wc 并找到当前 - 一个接一个 - 文件到 wc:
find $dir -name '*.txt' -exec wc -l {} \;
或由 liborm 指定。
dir="."
find $dir -name '*.txt' -exec wc -l {} \; | sort -rn | sed 's/\.txt$//'
不是最优化的方式,因为您可以使用 cat
、echo
、coreutils
、awk
、sed
、[=17= 的组合], 等等,但这会得到你想要的:
wc -l ./*.txt | awk 'BEGIN{print "Line\tFile"}1' | sed '$d'
wc -l ./*.txt
将提取行数。 awk 'BEGIN{print "Line\tFile"}1'
将添加 header 标题。 1
对应于标准输入的第一行。 sed '$d'
将打印除最后一行以外的所有行。
示例结果
Line File
6 ./test1.txt
1 ./test2.txt
仅使用 grep -c
的简单性
由于这些问题,我很少在我的脚本中使用 wc -l
。我改用 grep -c
。虽然它不如 wc -l
高效,但我们无需担心其他问题,例如摘要行、白色 space 或分叉额外进程。
例如:
/var/log# grep -c '^' *
alternatives.log:0
alternatives.log.1:3
apache2:0
apport.log:160
apport.log.1:196
apt:0
auth.log:8741
auth.log.1:21534
boot.log:94
btmp:0
btmp.1:0
<snip>
单个文件非常简单:
line_count=$(grep -c '^' my_file.txt)
性能比较:grep -c
vs wc -l
/tmp# ls -l *txt
-rw-r--r-- 1 root root 721009809 Dec 29 22:09 x.txt
-rw-r----- 1 root root 809338646 Dec 29 22:10 xyz.txt
/tmp# time grep -c '^' *txt
x.txt:7558434
xyz.txt:8484396
real 0m12.742s
user 0m1.960s
sys 0m3.480s
/tmp/# time wc -l *txt
7558434 x.txt
8484396 xyz.txt
16042830 total
real 0m9.790s
user 0m0.776s
sys 0m2.576s
您可以像这样使用 GNU Parallel 非常简洁地解决它(以及许多其他似乎需要 for
循环的问题):
parallel wc -l ::: tmp/*txt
示例输出
3 tmp/lines.txt
5 tmp/unfiltered.txt
42 tmp/file.txt
6 tmp/used.txt
还有 sed
解决方案!
1。简短快捷
由于总计在最后一行,$d
是删除最后一行的sed命令。
wc -l $directory-path/*.txt | sed '$d'
2。添加 header 行:
wc -l $directory-path/*.txt | sed '$d;1ilines total'
很遗憾,没有对齐。
3。使用对齐方式:将左列格式化为 11 个字符宽度。
wc -l $directory-path/*.txt |
sed -e '
s/^ *\([0-9]\+\)/ /;
s/^ *\([0-9 ]\{11\}\) / /;
/^ *[0-9]\+ total$/d;
1i\ lines filename'
会做这份工作
lines file
5 ./directory/1.txt
3 ./directory/2.txt
2 ./directory/3.txt
4。但是,如果您的 wc
版本真的可以将总计放在第一行:
这个是为了好玩,因为我不相信有 wc
版本把总数放在第一行,但是...
此版本在所有地方删除 total 行,并在输出顶部添加 header 行。
wc -l $directory-path/*.txt |
sed -e '
s/^ *\([0-9]\+\)/ /;
s/^ *\([0-9 ]\{11\}\) / /;
1{
/^ *[0-9]\+ total$/ba;
bb;
:a;
s/^.*$/ lines file/
};
bc;
:b;
1i\ lines file' -e '
:c;
/^ *[0-9]\+ total$/d
'
这更复杂,因为我们不会删除第一行,即使它是 总 行。
类似于xargs
:
ls | xargs -I% wc -l %
然后 xargs
明确不会将所有输入发送到 wc
,而是一次发送一个操作数行。
最短答案:
ls | xargs -l wc
这是一份 tailor-made 负责人的工作:
wc -l | head --lines=-1
这样,您仍然可以在一个过程中运行。