]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
du: quote problematic names on tty
authorPádraig Brady <P@draigBrady.com>
Fri, 31 Jul 2026 16:35:52 +0000 (17:35 +0100)
committerPádraig Brady <P@draigBrady.com>
Sat, 1 Aug 2026 11:38:28 +0000 (12:38 +0100)
* doc/coreutils.texi (readlink invocation): Mention $QUOTING_STYLE
is significant.
* src/du.c (main): Quote output when appropriate.
* tests/du/basic.sh: Add a test case.
* NEWS: Mention the improvement.

NEWS
doc/coreutils.texi
src/du.c
tests/du/basic.sh

diff --git a/NEWS b/NEWS
index 2db6c99ae0e067cdafb1e51e1e1269cb9fd1b95a..3a7b175e30801cb991c241d77cd16e133bdaf7a8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -84,9 +84,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   'install -C' will now avoid updating file metadata when the destination
   already has the appropriate ownership and permissions.
 
-  'basename', 'readlink', and 'realpath' now quote output in shell-escape style
-  when standard output is a terminal.  The QUOTING_STYLE environment variable
-  can be used to adjust or disable the quoting.
+  'basename', 'du', 'readlink', and 'realpath' now quote output in shell-escape
+  style when standard output is a terminal.  The QUOTING_STYLE environment
+  variable can be used to adjust or disable the quoting.
 
   'ls -m' now quotes files names containing commas when appropriate,
   so users can better distinguish separating commas.
index b675b6345abb329e5b3339e80069c7eeca9a3936..441b80c3c5f1f9b01338b1982c56375976b1977a 100644 (file)
@@ -12436,6 +12436,11 @@ the argument being processed is on.
 
 @end table
 
+When standard output is a terminal, file names are quoted using
+the @samp{shell-escape} style.  The environment variable
+@env{QUOTING_STYLE} can select the quoting style.  Valid quoting styles are:
+@quotingStyles
+
 Since @command{du} relies on information reported by the operating
 system, its output might not reflect the space consumed in the
 underlying devices.  For example;
index eefb24f05aab6d19d2e908c1aaed58af3f778166..7c9c486f1caddb1355f9aef6b5bfcad04cd86591 100644 (file)
--- a/src/du.c
+++ b/src/du.c
@@ -26,7 +26,7 @@
 #include <config.h>
 #include <getopt.h>
 #include <sys/types.h>
-#include "argmatch.h"
+#include "argmatch.h"  /* argmatch($QUOTING_STYLE).  */
 #include "system.h"
 #include "argv-iter.h"
 #include "assure.h"
@@ -135,6 +135,9 @@ static bool hash_all;
 /* If true, output the NUL byte instead of a newline at the end of each line. */
 static bool opt_nul_terminate_output = false;
 
+/* If true, quote output pathnames according to the selected quoting style.  */
+static bool quote_output;
+
 /* If true, print a grand total at the end.  */
 static bool print_grand_total = false;
 
@@ -445,10 +448,11 @@ print_only_size (uintmax_t n_bytes)
          stdout);
 }
 
-/* Print size (and optionally time) indicated by *PDUI, followed by STRING.  */
+/* Print size (and optionally time) indicated by *PDUI, followed by STRING,
+   quoting STRING if QUOTE is true.  */
 
 static void
-print_size (const struct duinfo *pdui, char const *string)
+print_size (const struct duinfo *pdui, char const *string, bool quote)
 {
   print_only_size (opt_inodes
                    ? pdui->inodes
@@ -466,7 +470,7 @@ print_size (const struct duinfo *pdui, char const *string)
         }
     }
   putchar ('\t');
-  fputs (string, stdout);
+  fputs (quote ? quoteN (string) : string, stdout);
   putchar (opt_nul_terminate_output ? '\0' : '\n');
   if (fflush (stdout) < 0)
     write_error ();
@@ -718,7 +722,7 @@ process_file (FTS *fts, FTSENT *ent)
       if (opt_threshold < 0
           ? v <= -opt_threshold
           : v >= opt_threshold)
-        print_size (&dui_to_print, file);
+        print_size (&dui_to_print, file, quote_output);
     }
 
   return ok;
@@ -983,6 +987,18 @@ main (int argc, char **argv)
   if (!ok)
     usage (EXIT_FAILURE);
 
+  if (!opt_nul_terminate_output && isatty (STDOUT_FILENO))
+    {
+      int qs = getenv_quoting_style ();
+      if (qs < 0)
+        qs = shell_escape_quoting_style;
+      if (qs != literal_quoting_style)
+        {
+          set_quoting_style (NULL, qs);
+          quote_output = true;
+        }
+    }
+
   if (opt_all && opt_summarize_only)
     {
       error (0, 0, _("cannot both summarize and show all entries"));
@@ -1192,7 +1208,7 @@ main (int argc, char **argv)
     error (EXIT_FAILURE, 0, _("error reading %s"), quoteaf (files_from));
 
   if (print_grand_total)
-    print_size (&tot_dui, _("total"));
+    print_size (&tot_dui, _("total"), false);
 
   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
 }
index 3bf74f712b203c1b562d21f0261b35d54df07f65..45567f71d70f10ac4ce3473d8fcf526dbb129b3b 100755 (executable)
@@ -28,6 +28,21 @@ printf '%*s' 257 make-sure-the-file-is-non-empty > a/b/F || framework_failure_
 printf %4096s x > d/1
 cp d/1 d/sub/2
 
+# QUOTING_STYLE does not affect redirected output.
+touch 'q name' || framework_failure_
+printf '0\tq name\n' > exp || framework_failure_
+for style in literal shell-always invalid; do
+  QUOTING_STYLE="$style" du -b 'q name' > out 2> err || fail=1
+  compare exp out || fail=1
+  compare /dev/null err || fail=1
+done
+
+# --null disables quoting, and does not inspect QUOTING_STYLE.
+QUOTING_STYLE=invalid du -0b 'q name' > out 2> err || fail=1
+printf '0\tq name\0' > exp || framework_failure_
+compare exp out || fail=1
+compare /dev/null err || fail=1
+
 
 B=$(stat --format=%B a/b/F)