Pádraig Brady <[email protected]> writes:

>>>    user_color=$(run0 --user="$user" printenv user_color)
>>>
>>> E.g. if $user_color contained ANSI escapes we'd want them unquoted.
>>> Note the trailing newline would be auto stripped in the above,
>>> allowing such usage.
>> I think I agree. It feels a little weird to behave differently based
>> on
>> the number of arguments, but I think that case warrants it.
>
> It would be a bit more consistent to key on whether args were specified at 
> all.
> So 1 or more args would only quote if connected to a tty.
>
>> I've attached an incomplete patch to do that. I'll probably adjust NEWS
>> and the documentation a bit.
>
> NEWS is fine I think.
> Details on this are best restricted to the texinfo description.
>
>> One thing I'm unsure about is whether a test for tty output should be
>> added to the same test or in misc/tty-quoting.sh. It seems like the ones
>> there are tailored to a single argument, where we should test both
>> single argument and multiple arguments in this case, I think. I don't
>> want that look to be *too* full of edge cases such that it gets
>> difficult to understand. WDYT?
> It would be good to add the arg specified case to tty-quoting.sha
> to document the commands that are tty sensitive.
> A single arg case is fine here.
>
> The existing cases are fine in misc/printenv.sh,
> but tweaked to the adjusted non quoting.

How does the attached patch look?

I'm sort of leaning towards it being worth expanding on the rationale in
the manual, but I feel 'run0' is a little too Linux specific. I couldn't
think of any better examples, though, so I left it out for now. AFAIK,
sudo only allows you to preserve the entire environment or specific
variables. I.e., there is no way to set a variable to a given value.

Collin

>From 20c3044b68e9494e1ad9c6332fb1e48dbdf7d62b Mon Sep 17 00:00:00 2001
Message-ID: <20c3044b68e9494e1ad9c6332fb1e48dbdf7d62b.1788142310.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Sat, 29 Aug 2026 12:49:06 -0700
Subject: [PATCH v2] printenv: don't quote output when given arguments and
 redirected

* src/printenv.c (main): Make sure quoting is disabled when we have
arguments and standard output is not a terminal.
* tests/misc/printenv.sh: Adjust single argument test cases. Add
multiple argument test cases.
* tests/misc/tty-quoting.sh: Add test cases to check if isatty is used.
* doc/coreutils.texi (printenvAlwaysQuoted): Remove macro.
(printenv invocation): Describe the adjusted quoting behavior.
(env invocation): Move the text from printenvAlwaysQuoted here since it
is no longer used for 'printenv'.
---
 doc/coreutils.texi        | 21 +++++++++++++--------
 src/printenv.c            |  5 +++--
 tests/misc/printenv.sh    | 18 +++++++++++++-----
 tests/misc/tty-quoting.sh | 14 ++++++++++----
 4 files changed, 39 insertions(+), 19 deletions(-)

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 9fe4ba032..46854db4a 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -15519,14 +15519,15 @@ @node printenv invocation
 
 @end table
 
-@macro printenvAlwaysQuoted
-The printed environment variables and their values are quoted using the
-@samp{shell-escape} style.   The environment variable
-@env{QUOTING_STYLE} specifies the quoting style.  Valid quoting styles
-are:
+If no @var{variable}s are specified, @command{printenv} quotes
+environment variables and their values using the @samp{shell-escape}
+style.  If @var{variable}s are specified, @command{printenv} only quotes
+their values using the @samp{shell-escape} style if standard output is a
+terminal.
+
+The environment variable @env{QUOTING_STYLE}
+specifies the quoting style.  Valid quoting styles are:
 @quotingStyles
-@end macro
-@printenvAlwaysQuoted
 
 @cindex exit status of @command{printenv}
 Exit status:
@@ -17496,7 +17497,11 @@ @node env invocation
 specifications, the resulting environment is printed.  This is like
 specifying the @command{printenv} program.
 
-@printenvAlwaysQuoted
+The printed environment variables and their values are quoted using the
+@samp{shell-escape} style.   The environment variable
+@env{QUOTING_STYLE} specifies the quoting style.  Valid quoting styles
+are:
+@quotingStyles
 
 For some examples, suppose the environment passed to @command{env}
 contains @samp{LOGNAME=rms}, @samp{EDITOR=emacs}, and
diff --git a/src/printenv.c b/src/printenv.c
index 540f56334..dba87f373 100644
--- a/src/printenv.c
+++ b/src/printenv.c
@@ -111,8 +111,9 @@ main (int argc, char **argv)
     }
 
   bool quote_output = false;
+  idx_t const n_args = argc - optind;
 
-  if (!opt_nul_terminate_output)
+  if (!opt_nul_terminate_output && (n_args <= 0 || isatty (STDOUT_FILENO)))
     {
       int qs = getenv_quoting_style ();
       if (qs < 0)
@@ -130,7 +131,7 @@ main (int argc, char **argv)
   if (opt_nul_terminate_output)
     xset_binary_mode (STDOUT_FILENO, O_BINARY);
 
-  if (optind >= argc)
+  if (n_args <= 0)
     {
       for (char **env = environ; *env != NULL; ++env)
         print_envvar (*env, terminator, quote_output);
diff --git a/tests/misc/printenv.sh b/tests/misc/printenv.sh
index 8ec228a6a..a6eab09f6 100755
--- a/tests/misc/printenv.sh
+++ b/tests/misc/printenv.sh
@@ -84,17 +84,21 @@ compare /dev/null out || fail=1
 cat <<\EOF >exp-noargs-literal || framework_failure_
 a b=c d
 EOF
+cat <<\EOF >exp-arg-literal || framework_failure_
+c d
+EOF
 cat <<\EOF >exp-args-literal || framework_failure_
 c d
+c d
 EOF
 cat <<\EOF >exp-noargs-shell || framework_failure_
 'a b'='c d'
 EOF
-cat <<\EOF >exp-args-shell || framework_failure_
-'c d'
-EOF
-tr "'" '"' <exp-noargs-shell >exp-noargs-c || framework_failure_
-tr "'" '"' <exp-args-shell >exp-args-c || framework_failure_
+cp exp-arg-literal exp-arg-shell &&
+cp exp-args-literal exp-args-shell || framework_failure_
+for t in noargs arg args; do
+  tr "'" '"' <exp-$t-shell >exp-$t-c || framework_failure_
+done
 for qs in literal shell c; do
   env -i PATH="$PATH" QUOTING_STYLE=$qs 'a b'='c d' \
     printenv >out-t 2>err || fail=1
@@ -104,6 +108,10 @@ for qs in literal shell c; do
   compare /dev/null err || fail=1
   env -i PATH="$PATH" QUOTING_STYLE=$qs 'a b'='c d' \
     printenv 'a b' >out 2>err || fail=1
+  compare exp-arg-$qs out || fail=1
+  compare /dev/null err || fail=1
+  env -i PATH="$PATH" QUOTING_STYLE=$qs 'a b'='c d' \
+    printenv 'a b' 'a b' >out 2>err || fail=1
   compare exp-args-$qs out || fail=1
   compare /dev/null err || fail=1
 done
diff --git a/tests/misc/tty-quoting.sh b/tests/misc/tty-quoting.sh
index a0dc1d4b8..833dfaedb 100755
--- a/tests/misc/tty-quoting.sh
+++ b/tests/misc/tty-quoting.sh
@@ -44,16 +44,22 @@ run_tty_ env test -t 1 ||
 run_tty_ env printf foo >printf.t &&
  skip_ 'libc buffering induced a tty probe'
 
-for cmd in basename du dirname 'ls -w0' readlink 'realpath --relative-to=.'; do
+for cmd in basename du dirname 'ls -w0' readlink 'realpath --relative-to=.' \
+           printenv; do
   test "$cmd" = 'du' && field=2 || field=1
-  test "$cmd" = 'dirname' && file='f oo/.' || file='f oo'
+  case "$cmd" in
+    dirname) file='f oo/.' ;;
+    printenv) file=TEST_ENV1 ;;
+    *) file='f oo' ;;
+  esac
 
-  run_tty_ $cmd "$file" >quoted.t || fail=1
+  TEST_ENV1='f oo' run_tty_ $cmd "$file" >quoted.t || fail=1
   cut -f$field- quoted.t >quoted || framework_failure_
 
   # Note ls theoretically doesn't need isatty() for a specified QUOTING_STYLE
   # but it does need it to determine appropriate output format.
-  QUOTING_STYLE=literal run_tty_ $cmd "$file" >unquoted.t || fail=1
+  TEST_ENV1='f oo' QUOTING_STYLE=literal run_tty_ $cmd "$file" \
+    >unquoted.t || fail=1
   cut -f$field- unquoted.t >unquoted || framework_failure_
 
   env printf '%q\n' "$(cat unquoted)" >printf_quoted || framework_failure_
-- 
2.55.0

Reply via email to