Subject: bug#70860: Add an option to remove filenames for wc util

From: Laurent Lyaudet <[email-masked]>
Date: Fri, 10 May 2024 13:36:29 +0200

Hello :),

Thanks for your hard work :)
I had a need for a variant of wordcount without filenames in the output,
tuesday.
https://github.com/LLyaudet/DevOrSysAdminScripts/blob/main/build_and_checks_dependencies/lines_filters.sh
https://github.com/LLyaudet/DevOrSysAdminScripts/commit/f0cb3291de4ad0bc63e40baf31364d1af6849f12
I started to make a patch on GNU coreutils on that day,
but I had to interrupt and continue today.
Here is the patch joined to this email.

Best summary of the feature is just to copy-paste my commit message:
wc: Add an option -n --no-filenames.

wc now has an option -n or --no-filenames to remove the filenames
from the output lines.
It is useful when used in conjunction with a single option
-c, -m, -l, -L, or -w,
since the result is a single number per line that can be easily used
in shell (Bash) scripts,
without having to pipe the result with cut.

doc/coreutils.texi: Add option -n to documentation.
NEWS: Mention the change option -n added and justify it.
src/wc.c: Modify the source code to have an option -n.
tests/wc/wc-no-filenames.sh: Test --no-filenames option for wc.

Note that it should be a -f --filenames option if the default was different.
If the default was configurable,
then we should have -f --filenames -n --no-filenames options,
like is possible in some of the latest versions of argparse in Python
(argparse.BooleanOptionalAction).
I don't know how to handle that in C code.
Please accept my patch and improve upon it if needed.

Best regards,
     Laurent Lyaudet
From ef4e810d9bbd6dac65c18cb328cd5e24e480d85e Mon Sep 17 00:00:00 2001
From: Laurent Lyaudet <[email-masked]>
Date: Fri, 10 May 2024 13:20:29 +0200
Subject: [PATCH] wc: Add an option -n --no-filenames

wc now has an option -n or --no-filenames to remove the filenames
from the output lines.
It is useful when used in conjunction with a single option
-c, -m, -l, -L, or -w,
since the result is a single number per line that can be easily used
in shell (Bash) scripts,
without having to pipe the result with cut.

doc/coreutils.texi: Add option -n to documentation.
NEWS: Mention the change option -n added and justify it.
src/wc.c: Modify the source code to have an option -n.
tests/wc/wc-no-filenames.sh: Test --no-filenames option for wc.
---
 NEWS                        |  8 ++++++++
 doc/coreutils.texi          | 17 +++++++++++----
 src/wc.c                    | 13 ++++++++++--
 tests/wc/wc-no-filenames.sh | 41 +++++++++++++++++++++++++++++++++++++
 4 files changed, 73 insertions(+), 6 deletions(-)
 create mode 100755 tests/wc/wc-no-filenames.sh

diff --git a/NEWS b/NEWS
index febb9ac68..b5d7dfd90 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,14 @@ GNU coreutils NEWS                                    -*- outline -*-
   This was previously 16KiB and increasing to 256KiB was seen to increase
   wc -l performance by about 10% when reading cached files on modern systems.
 
+  wc now has an option -n or --no-filenames to remove the filenames
+  from the output lines.
+  It is useful when used in conjunction with a single option
+  -c, -m, -l, -L, or -w,
+  since the result is a single number per line that can be easily used
+  in shell (Bash) scripts,
+  without having to pipe the result with cut.
+
 
 * Noteworthy changes in release 9.5 (2024-03-28) [stable]
 
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 3762c6f96..6c2cd4224 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -3814,10 +3814,13 @@ wc [@var{option}]@dots{} [@var{file}]@dots{}
 @end example
 
 @cindex total counts
-@command{wc} prints one line of counts for each file, and if the file was
-given as an argument, it prints the file name following the counts. By default
-if more than one @var{file} is given, @command{wc} prints a final line
-containing the cumulative counts, with the file name @file{total}.
+@command{wc} prints one line of counts for each file,
+and if the file was given as an argument,
+it prints the file name following the counts,
+unless the -n option (--no-filenames) is specified.
+By default if more than one @var{file} is given,
+@command{wc} prints a final line containing the cumulative counts,
+with the file name @file{total}.
 This @samp{total} line can be controlled with the @option{--total} option,
 which is a GNU extension.
 The counts are printed in this order: newlines, words, characters, bytes,
@@ -3896,6 +3899,12 @@ Tabs are set at every 8th column.
 Display widths of wide characters are considered.
 Non-printable characters are given 0 width.
 
+@item -n
+@itemx --no-filenames
+@opindex -n
+@opindex --no-filenames
+Do not print the filenames in output lines.
+
 @item --total=@var{when}
 @opindex --total=@var{when}
 Control when and how the final line with cumulative counts is printed.
diff --git a/src/wc.c b/src/wc.c
index 21ffa74d9..e628bfb11 100644
--- a/src/wc.c
+++ b/src/wc.c
@@ -64,6 +64,7 @@ static intmax_t max_line_length;
 /* Which counts to print. */
 static bool print_lines, print_words, print_chars, print_bytes;
 static bool print_linelength;
+static bool print_filenames;
 
 /* The print width of each count.  */
 static int number_width;
@@ -103,6 +104,7 @@ static struct option const longopts[] =
   {"chars", no_argument, nullptr, 'm'},
   {"lines", no_argument, nullptr, 'l'},
   {"words", no_argument, nullptr, 'w'},
+  {"no-filenames", no_argument, nullptr, 'n'},
   {"debug", no_argument, nullptr, DEBUG_PROGRAM_OPTION},
   {"files0-from", required_argument, nullptr, FILES0_FROM_OPTION},
   {"max-line-length", no_argument, nullptr, 'L'},
@@ -179,6 +181,8 @@ the following order: newline, word, character, byte, maximum line length.\n\
                            If F is - then read names from standard input\n\
   -L, --max-line-length  print the maximum display width\n\
   -w, --words            print the word counts\n\
+  -n, --no-filenames     do not print the filenames at the end of\
+ each line\n\
 "), stdout);
       fputs (_("\
       --total=WHEN       when to print a line with total counts;\n\
@@ -238,7 +242,7 @@ write_counts (uintmax_t lines,
     }
   if (print_linelength)
     printf (format_int, number_width, imaxtostr (linelength, buf));
-  if (file)
+  if (file && print_filenames)
     printf (" %s", strchr (file, '\n') ? quotef (file) : file);
   putchar ('\n');
 }
@@ -745,9 +749,10 @@ main (int argc, char **argv)
 
   print_lines = print_words = print_chars = print_bytes = false;
   print_linelength = false;
+  print_filenames = true;
   total_lines = total_words = total_chars = total_bytes = max_line_length = 0;
 
-  while ((optc = getopt_long (argc, argv, "clLmw", longopts, nullptr)) != -1)
+  while ((optc = getopt_long (argc, argv, "clLmnw", longopts, nullptr)) != -1)
     switch (optc)
       {
       case 'c':
@@ -770,6 +775,10 @@ main (int argc, char **argv)
         print_linelength = true;
         break;
 
+      case 'n':
+        print_filenames = false;
+        break;
+
       case DEBUG_PROGRAM_OPTION:
         debug = true;
         break;
diff --git a/tests/wc/wc-no-filenames.sh b/tests/wc/wc-no-filenames.sh
new file mode 100755
index 000000000..0dec0f5d5
--- /dev/null
+++ b/tests/wc/wc-no-filenames.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Show that wc's --no-filenames option works.
+
+# Copyright (C) 2022-2024 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.
+# If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ wc
+require_sparse_support_ # for 'truncate --size=$BIG'
+getlimits_
+
+echo "test test test" > test || framework_failure_
+
+returns_ 1 wc -l --filenames test > out || fail=1
+
+wc -l --no-filenames test > out1 || fail=1
+echo "1" > exp1 || framework_failure_
+compare exp1 out1 || fail=1
+
+wc -w --no-filenames test > out2 || fail=1
+echo "3" > exp2 || framework_failure_
+compare exp2 out2 || fail=1
+
+wc -L --no-filenames test > out3 || fail=1
+echo "14" > exp3 || framework_failure_
+compare exp3 out3 || fail=1
+
+Exit $fail
-- 
2.40.1