在 `wc -l` 输出中抑制汇总信息

4

我使用命令wc -l来计算文本文件中的行数(同时我想通过管道对所有内容进行排序),就像这样:

wc -l $directory-path/*.txt | sort -rn

输出结果包括“total”行,它是所有文件行数的总和:
10 total
5 ./directory/1.txt
3 ./directory/2.txt
2 ./directory/3.txt

有没有办法抑制这个摘要行?或者更好的是,改变摘要行的措辞方式?例如,将“10”改为“行”,将“total”改为“文件”。


wcman 页面没有提到这样的功能。你可以编写一个脚本(或者可能使用管道和 awk)来改变输出的外观。 - user554546
1
将其管道传输到 tail +2 以跳过第一行。 - Barmar
@Barmar: 那是不可靠的。只有在存在多个文件时才会打印total行。至少在我的系统上,total行是在最后打印的——正如POSIX明确要求的那样。ipo:你真的会得到你展示的输出吗?它会在顶部显示10 total行吗? - Keith Thompson
2
根据您的评论,我认为您在顶部看到“总共10个”是因为您正在对输出进行排序。您需要在问题中提到这一点。向我们展示您正在运行的确切命令及其确切输出。而$directory-path不是一个有效的变量名。 - Keith Thompson
12个回答

6

仍然是一个sed解决方案!

1. 简短快速

由于总数出现在最后一行,$d删除最后一行的命令。

wc -l $directory-path/*.txt | sed '$d'

2. 添加标题行:

wc -l $directory-path/*.txt | sed '$d;1ilines total'

很遗憾,没有对齐设置。
3. 带有对齐:将左列格式设置为11个字符宽度。
wc -l $directory-path/*.txt |
    sed -e '
        s/^ *\([0-9]\+\)/          \1/;
        s/^ *\([0-9 ]\{11\}\) /\1 /;
        /^ *[0-9]\+ total$/d;
        1i\      lines filename'

会完成工作

      lines file
          5 ./directory/1.txt
          3 ./directory/2.txt
          2 ./directory/3.txt

4. 但如果您的wc版本真的可以将总数放在第一行:

这个是为了好玩,因为我不相信有一个wc版本会把总数放在第一行,但是......

这个版本在输出中去掉了总数行,并在输出顶部添加了标题行。

wc -l $directory-path/*.txt |
    sed -e '
        s/^ *\([0-9]\+\)/          \1/;
        s/^ *\([0-9 ]\{11\}\) /\1 /;
        1{
            /^ *[0-9]\+ total$/ba;
            bb;
           :a;
            s/^.*$/      lines file/
        };
        bc;
       :b;
        1i\      lines file' -e '
       :c;
        /^ *[0-9]\+ total$/d
    '

这更加复杂,因为即使是总计行,我们也不会删除第一行。


我相当确定他在第一行看到了“total”,因为他正在对输出进行排序。他在评论中提到了这一点,但需要在问题中说明。而且没有迹象表明他想要或需要你的解决方案生成的“lines filename”标题。 - Keith Thompson
2
对于这么小的操作来说,似乎过于复杂了。 - codeforester
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - F. Hauri - Give Up GitHub
@codeforester,它看起来很复杂,因为你不想去理解。它并不是那么简单,但它是一个非常快速且作为独立解决方案的工具。 - F. Hauri - Give Up GitHub
@codeforester 好的,这里有一个更简单的 [tag:sed] 版本:只有2个字符 - F. Hauri - Give Up GitHub

1
这是一份量身定制给头儿的工作:
wc -l | head --lines=-1

这样,你仍然可以在一个进程中运行。

有很多人为了解决问题而提出了复杂的解决方案,但在排序之前使用 head -n -1 似乎是最好的。令人惊讶的是,wc 没有安静模式或脚本使用模式。 - Kevin

1

这实际上相当棘手。

我基于GNU coreutils版本的wc命令。请注意,total行通常是最后打印的,而不是第一行(请参见我的问题评论)。

wc -l为每个输入文件打印一行,其中包括文件中的行数和文件名。(如果没有文件名参数,则省略文件名;在这种情况下,它计算stdin中的行数。)

仅当有多个文件名参数时,它会打印一个包含总行数和单词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 -l,因为我还需要对它们进行排序。当我在每个文件上执行wc -l时,这是不可能的。快速而肮脏的方法也不是很好。也许我有一个名为“total”的文件。 - idontknowwhoiamgodhelpme
@ipo:当然可以对输出进行排序:for file in $directory-path/*.txt ; do wc -l "$file" ; done | sort -rn。(我假设您正在使用类似于bash的Bourne衍生shell。) - Keith Thompson
1
@gniourf_gniourf:完成了。(我以为我已经完成了,不确定我是怎么错过的。) - Keith Thompson
你错过了:/bin/ls -1 *.txt | xargs -n1 wc -l 和/或 find . -maxdepth 1 -name '*.txt' -exec wc -l {} \; ;-) - F. Hauri - Give Up GitHub
@F.Hauri:我不会说我“错过了”那些。我并不打算展示所有可能的解决方案。 - Keith Thompson

1
程序wc,当有两个或两个以上的文件时,总是显示总数(wc.c片段):
if (argc > 2)
     report ("total", total_ccount, total_wcount, total_lcount);
   return 0;

最简单的方法是只使用一个文件来使用 wc 命令,并逐个查找文件并进行计数:

find $dir -name '*.txt' -exec wc -l {} \;

或者按照liborm的规定。

dir="."
find $dir -name '*.txt' -exec wc -l {} \; | sort -rn | sed 's/\.txt$//'

这几乎是解决方案!但我还需要将其管道传递到 | sort -rn | sed 's/\.txt$//'。我应该在哪里放置这个管道?我尝试了 find $dicitonary-path/*.txt-exec wc -l {} \ | sort -rn | sed 's/\.txt$//'; ...但这是错误的。 - idontknowwhoiamgodhelpme
我认为你的find命令中缺少了一个-name参数。 - Keith Thompson
@ipo 就是这样,但没有错别字.. find $PATH -name '*.txt' -exec wc -l {} \; | sort -rn | sed 's/\.txt$//' - liborm
@Keith Thompson:你说得对,谢谢你的帮助。 - V. Michel
这是2个或更多文件,不超过2个文件。argc是参数数量,包括argv[0],即程序名称(“wc”)。 - Keith Thompson
显示剩余2条评论

0

你能用另一个wc吗?

POSIX的wc(man -s1p wc)显示:
如果指定了多个输入文件操作数,则将写入一行附加行,格式与其他行相同,但在POSIX语言环境中,应写入单词总计而不是路径名,并适当地写入每列的总计。如果有任何这样的附加行,则将其写在输出末尾。

你说总行是第一行,但手册说明它是最后一行,其他wc根本不显示它。删除第一行或最后一行是危险的,所以我会grep -v带有总计(在POSIX语言环境中...)的行,或者只是grep所有其他行中的斜杠:

wc -l $directory-path/*.txt | grep "/"

0

Mark Setchell的答案类似,您也可以使用xargs和显式分隔符:

ls | xargs -I% wc -l %

然后,xargs 显式地将所有输入都发送到 wc,但是每次只发送一个操作数行。


0

对于使用sed与下面的模式移除选项,只有在总行存在时才会删除它(但也会删除任何包含total的文件),你怎么看?

wc -l $directory-path/*.txt | sort -rn | sed '/total/d'


0
最短答案:
ls | xargs -l wc

0

这不是最优化的方法,因为你可以使用catechocoreutilsawksedtac等组合,但这将得到你想要的结果:

wc -l ./*.txt | awk 'BEGIN{print "Line\tFile"}1' | sed '$d'

wc -l ./*.txt 将提取行数。 awk 'BEGIN{print "Line\tFile"}1' 将添加标题。 1 对应于 stdin 的第一行。 sed '$d' 将打印除最后一行外的所有行。

示例结果

Line    File
      6 ./test1.txt
      1 ./test2.txt

我得到的只有这样的东西:“Line File” 在“总计超过10行”的上方。就像你的例子一样,但是再次包含了总体信息。 - idontknowwhoiamgodhelpme
@ipo:你在使用什么系统?我在OSX系统上使用zsh。我的总行数显示在末尾。尝试使用这个命令:wc -l ./*.txt | awk 'BEGIN{print "Line\tFile"}1' | sed '2d'。唯一的区别是现在sed应该删除第二行,而不是最后一行。 - jojo

0

只使用grep -c的简便性

由于存在这些问题,我很少在我的脚本中使用wc -l。相反,我使用grep -c。虽然它不像wc -l那样高效,但我们不需要担心其他问题,如摘要行、空格或分叉额外进程。

例如:

/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

2
但是 grep -c . 是计算非空行的数量。你可能想要使用 grep -c '' 来近似 wc -l(如果最后一行没有以换行符结束,这两者会相差一个)。 - gniourf_gniourf
2
很棒的观察,@gniourf_gniourf。我将命令更改为grep -c '^' - codeforester
1
如果最后一行没有以换行符结尾,那么 grep -c '^'wc -l 的结果会相差一个。实际上,grep(至少是 GNU 版本)总是在最后一行没有换行符的情况下默默地添加一个换行符。 - Keith Thompson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接