mirror of
https://github.com/coreutils/coreutils.git
synced 2026-10-28 09:44:34 +08:00
printenv,env: quote problematic variables
* doc/coreutils.texi (printenvAlwaysQuoted): New macro. (printenv invocation): Use it. (env invocation): Use it. * src/printenv.h: New file. * src/local.mk (noinst_HEADERS): Add it. (src_env_SOURCES): Likewise. Group with src_printenv_SOURCES. (src_printenv_SOURCES): New variable. * src/printenv.c: Include argmatch.h and printenv.h. (main): Quote printed variable names and values. * src/env.c: Include argmatch.h and printenv.h. (main): Quote printed variable names and values. * tests/misc/printenv.sh: Add test cases. * tests/env/env.sh: Likewise. Adjust invalid UTF-8 test cases. * NEWS: Mention the change in behavior.
This commit is contained in:
1 parent
6b8c706233
commit
328322ca64
8 files changed
+181
-17
No files matched your search
@@ -67,6 +67,11 @@ GNU coreutils NEWS -*- outline -*-
|
||||
|
||||
** Changes in behavior
|
||||
|
||||
'env' and 'printenv' now quote printed environment variables honoring the
|
||||
QUOTING_STYLE environment variable, defaulting to shell-escape style. This
|
||||
avoids printing arbitrary data to the terminal and allows the output to be
|
||||
sourced by a POSIX shell.
|
||||
|
||||
'ls' -w,--width no longer includes '\n' in the width of a line.
|
||||
I.e., the width or $COLUMNS is interpreted to be an _inclusive_ maximum.
|
||||
|
||||
|
||||
@@ -15514,6 +15514,15 @@ The program accepts the following option. Also see @ref{Common options}.
|
||||
|
||||
@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:
|
||||
@quotingStyles
|
||||
@end macro
|
||||
@printenvAlwaysQuoted
|
||||
|
||||
@cindex exit status of @command{printenv}
|
||||
Exit status:
|
||||
|
||||
@@ -17477,6 +17486,8 @@ If no command name is specified following the environment
|
||||
specifications, the resulting environment is printed. This is like
|
||||
specifying the @command{printenv} program.
|
||||
|
||||
@printenvAlwaysQuoted
|
||||
|
||||
For some examples, suppose the environment passed to @command{env}
|
||||
contains @samp{LOGNAME=rms}, @samp{EDITOR=emacs}, and
|
||||
@samp{PATH=.:/gnubin:/hacks}:
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
#include <c-ctype.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "argmatch.h" /* argmatch($QUOTING_STYLE). */
|
||||
#include "system.h"
|
||||
#include "operand2sig.h"
|
||||
#include "printenv.h"
|
||||
#include "quote.h"
|
||||
#include "sig2str.h"
|
||||
|
||||
@@ -842,6 +844,22 @@ main (int argc, char **argv)
|
||||
++optind;
|
||||
}
|
||||
|
||||
bool quote_output = false;
|
||||
|
||||
/* Get the value from QUOTING_STYLE before unsetting environment
|
||||
variables. */
|
||||
if (!opt_nul_terminate_output)
|
||||
{
|
||||
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 (ignore_environment)
|
||||
{
|
||||
devmsg ("cleaning environ\n");
|
||||
@@ -887,12 +905,11 @@ main (int argc, char **argv)
|
||||
|
||||
if (! program_specified)
|
||||
{
|
||||
char const terminator = opt_nul_terminate_output ? '\0' : '\n';
|
||||
|
||||
/* Print the environment and exit. */
|
||||
for (char *const *e = environ; *e; ++e)
|
||||
{
|
||||
fputs (*e, stdout);
|
||||
putchar (opt_nul_terminate_output ? '\0' : '\n');
|
||||
}
|
||||
print_envvar (*e, terminator, quote_output);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -58,6 +58,7 @@ noinst_HEADERS = \
|
||||
src/ls.h \
|
||||
src/octhexdigits.h \
|
||||
src/operand2sig.h \
|
||||
src/printenv.h \
|
||||
src/prog-fprintf.h \
|
||||
src/remove.h \
|
||||
src/set-fields.h \
|
||||
@@ -398,7 +399,6 @@ src_cp_SOURCES = src/cp.c $(copy_sources) $(selinux_sources)
|
||||
src_date_SOURCES = src/date.c src/show-date.c
|
||||
src_dir_SOURCES = src/ls.c src/ls-dir.c
|
||||
src_du_SOURCES = src/du.c src/show-date.c
|
||||
src_env_SOURCES = src/env.c src/operand2sig.c
|
||||
src_vdir_SOURCES = src/ls.c src/ls-vdir.c
|
||||
src_id_SOURCES = src/id.c src/group-list.c
|
||||
src_groups_SOURCES = src/groups.c src/group-list.c
|
||||
@@ -421,6 +421,9 @@ src_rmdir_SOURCES = src/rmdir.c src/prog-fprintf.c
|
||||
src_mkfifo_SOURCES = src/mkfifo.c $(selinux_sources)
|
||||
src_mknod_SOURCES = src/mknod.c $(selinux_sources)
|
||||
|
||||
src_env_SOURCES = src/env.c src/operand2sig.c src/printenv.h
|
||||
src_printenv_SOURCES = src/printenv.c src/printenv.h
|
||||
|
||||
src_df_SOURCES = src/df.c src/find-mount-point.c
|
||||
src_stat_SOURCES = src/stat.c src/find-mount-point.c
|
||||
|
||||
|
||||
+22
-6
@@ -32,7 +32,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "argmatch.h" /* argmatch($QUOTING_STYLE). */
|
||||
#include "system.h"
|
||||
#include "printenv.h"
|
||||
|
||||
/* Exit status for syntax errors, etc. */
|
||||
enum { PRINTENV_FAILURE = 2 };
|
||||
@@ -107,14 +109,27 @@ main (int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
bool quote_output = false;
|
||||
|
||||
if (!opt_nul_terminate_output)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool ok;
|
||||
char const terminator = opt_nul_terminate_output ? '\0' : '\n';
|
||||
|
||||
if (optind >= argc)
|
||||
{
|
||||
for (char **env = environ; *env != NULL; ++env)
|
||||
{
|
||||
fputs (*env, stdout);
|
||||
putchar (opt_nul_terminate_output ? '\0' : '\n');
|
||||
}
|
||||
print_envvar (*env, terminator, quote_output);
|
||||
ok = true;
|
||||
}
|
||||
else
|
||||
@@ -137,8 +152,9 @@ main (int argc, char **argv)
|
||||
{
|
||||
if (*ep == '=' && *ap == '\0')
|
||||
{
|
||||
fputs (ep + 1, stdout);
|
||||
putchar (opt_nul_terminate_output ? '\0' : '\n');
|
||||
char const *val = ep + 1;
|
||||
fputs (quote_output ? quoteN (val) : val, stdout);
|
||||
putchar (terminator);
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Common definitions for 'printenv' and 'env'
|
||||
Copyright (C) 2026 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/>. */
|
||||
|
||||
#ifndef PRINTENV_H
|
||||
# define PRINTENV_H 1
|
||||
|
||||
static inline void
|
||||
print_envvar (char const *entry, char terminator, bool quoted)
|
||||
{
|
||||
if (! quoted)
|
||||
fputs (entry, stdout);
|
||||
else
|
||||
{
|
||||
idx_t const entry_len = strlen (entry);
|
||||
char const *equal = memchr (entry, '=', entry_len);
|
||||
|
||||
/* If the parent process manipulates ENVIRON directly, it is possible
|
||||
that an entry does not contain an equal sign. */
|
||||
idx_t const var_len = equal ? equal - entry : entry_len;
|
||||
fputs (quoteN_mem (entry, var_len), stdout);
|
||||
|
||||
if (equal)
|
||||
{
|
||||
putchar ('=');
|
||||
char const *val = equal + 1;
|
||||
idx_t const val_len = entry_len - (val - entry);
|
||||
/* Prefer "VAR=" over "VAR=''". */
|
||||
if (0 < val_len)
|
||||
fputs (quoteN_mem (val, val_len), stdout);
|
||||
}
|
||||
}
|
||||
putchar (terminator);
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+36
-6
@@ -91,12 +91,19 @@ EOF
|
||||
compare exp out || fail=1
|
||||
|
||||
# env shouldn't care what encoding name or value is
|
||||
for nv in 'NON_UTF8_TEST=\240' 'NON_UTF8_TEST\240=1'; do
|
||||
env $(printf "$nv") env > all || fail=1
|
||||
grep '^NON_UTF8_TEST' all | LC_ALL=C sort > out || framework_failure_
|
||||
printf "$nv\\n" > exp || framework_failure_
|
||||
compare exp out || fail=1
|
||||
done
|
||||
cat <<\EOF >exp || framework_failure_
|
||||
NON_UTF8_TEST=''$'\240'
|
||||
EOF
|
||||
env $(printf 'NON_UTF8_TEST=\240') env > all || fail=1
|
||||
grep '^NON_UTF8_TEST' all | LC_ALL=C sort > out || framework_failure_
|
||||
compare exp out || fail=1
|
||||
|
||||
cat <<\EOF >exp || framework_failure_
|
||||
'NON_UTF8_TEST'$'\240'=1
|
||||
EOF
|
||||
env $(printf 'NON_UTF8_TEST\240=1') env > all || fail=1
|
||||
grep "^'NON_UTF8_TEST" all | LC_ALL=C sort > out || framework_failure_
|
||||
compare exp out || fail=1
|
||||
|
||||
# PATH modifications affect exec.
|
||||
mkdir unlikely_name || framework_failure_
|
||||
@@ -186,4 +193,27 @@ EOF
|
||||
compare err_exp err || fail=1
|
||||
done
|
||||
|
||||
# QUOTING_STYLE affects redirected output.
|
||||
cat <<\EOF >exp-noargs-literal || framework_failure_
|
||||
a b=c d
|
||||
EOF
|
||||
cat <<\EOF >exp-noargs-shell || framework_failure_
|
||||
'a b'='c d'
|
||||
EOF
|
||||
tr "'" '"' <exp-noargs-shell >exp-noargs-c || framework_failure_
|
||||
for qs in literal shell c; do
|
||||
env -i PATH="$PATH" QUOTING_STYLE=$qs 'a b'='c d' \
|
||||
env >out-t 2>err || fail=1
|
||||
grep -vE 'QUOTING_STYLE|PATH' out-t > out || framework_failure_
|
||||
compare exp-noargs-$qs out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
done
|
||||
|
||||
# Check the behavior with an invalid value for QUOTING_STYLE.
|
||||
printf 'env: ignoring invalid value of environment variable %s\n' \
|
||||
"QUOTING_STYLE: 'invalid'" >exp || framework_failure_
|
||||
env QUOTING_STYLE=invalid env >out 2>err || fail=1
|
||||
grep '^QUOTING_STYLE=invalid$' out || fail=1
|
||||
compare exp err || fail=1
|
||||
|
||||
Exit $fail
|
||||
@@ -80,4 +80,38 @@ compare exp out || fail=1
|
||||
returns_ 1 env a=b=c printenv a=b > out || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
# QUOTING_STYLE affects redirected output.
|
||||
cat <<\EOF >exp-noargs-literal || framework_failure_
|
||||
a b=c d
|
||||
EOF
|
||||
cat <<\EOF >exp-args-literal || framework_failure_
|
||||
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_
|
||||
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
|
||||
grep -vE 'QUOTING_STYLE|PATH' out-t > out || framework_failure_
|
||||
compare exp-noargs-$qs out || fail=1
|
||||
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-args-$qs out || fail=1
|
||||
compare /dev/null err || fail=1
|
||||
done
|
||||
|
||||
# Check the behavior with an invalid value for QUOTING_STYLE.
|
||||
printf 'printenv: ignoring invalid value of environment variable %s\n' \
|
||||
"QUOTING_STYLE: 'invalid'" >exp || framework_failure_
|
||||
env QUOTING_STYLE=invalid printenv >out 2>err || fail=1
|
||||
grep '^QUOTING_STYLE=invalid$' out || fail=1
|
||||
compare exp err || fail=1
|
||||
|
||||
Exit $fail
|
||||
Reference in new issue
Block a user