aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--doc/coreutils.texi19
-rw-r--r--src/env.c37
-rwxr-xr-xtests/misc/env.sh15
4 files changed, 66 insertions, 8 deletions
diff --git a/NEWS b/NEWS
index 4c63397e7..e6e732caf 100644
--- a/NEWS
+++ b/NEWS
@@ -88,6 +88,9 @@ GNU coreutils NEWS -*- outline -*-
split supports a new --hex-suffixes[=from] option to create files with
lower case hexadecimal suffixes, similar to the --numeric-suffixes option.
+ env now has a --chdir (-C) option to change the working directory before
+ executing the subsidiary program.
+
expr supports multibyte strings for all string operations.
** Improvements
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 84b03f26d..5c424c166 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -16968,6 +16968,25 @@ environment.
@opindex --ignore-environment
Start with an empty environment, ignoring the inherited environment.
+@item -C @var{dir}
+@itemx --chdir=@var{dir}
+@opindex -C
+@opindex --chdir
+Change the working directory to @var{dir} before invoking @var{command}.
+This differs from the shell built-in @command{cd} in that it starts
+@var{command} as a subprocess rather than altering the shell's own working
+directory; this allows it to be chained with other commands that run commands
+in a different context. For example:
+
+@example
+# Run 'true' with /chroot as its root directory and /srv as its working
+# directory.
+chroot /chroot env --chdir=/srv true
+# Run 'true' with /build as its working directory, FOO=bar in its
+# environment, and a time limit of five seconds.
+env --chdir=/build FOO=bar timeout 5 true
+@end example
+
@end table
@cindex exit status of @command{env}
diff --git a/src/env.c b/src/env.c
index 63d5c2c2c..38769f813 100644
--- a/src/env.c
+++ b/src/env.c
@@ -38,6 +38,7 @@ static struct option const longopts[] =
{"ignore-environment", no_argument, NULL, 'i'},
{"null", no_argument, NULL, '0'},
{"unset", required_argument, NULL, 'u'},
+ {"chdir", required_argument, NULL, 'C'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -64,6 +65,9 @@ Set each NAME to VALUE in the environment and run COMMAND.\n\
-0, --null end each output line with NUL, not newline\n\
-u, --unset=NAME remove variable from the environment\n\
"), stdout);
+ fputs (_("\
+ -C, --chdir=DIR change working directory to DIR\n\
+"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
@@ -81,6 +85,7 @@ main (int argc, char **argv)
int optc;
bool ignore_environment = false;
bool opt_nul_terminate_output = false;
+ char const *newdir = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -91,7 +96,7 @@ main (int argc, char **argv)
initialize_exit_failure (EXIT_CANCELED);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "+iu:0", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "+C:iu:0", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -103,6 +108,9 @@ main (int argc, char **argv)
case '0':
opt_nul_terminate_output = true;
break;
+ case 'C':
+ newdir = optarg;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
@@ -120,7 +128,7 @@ main (int argc, char **argv)
}
optind = 0; /* Force GNU getopt to re-initialize. */
- while ((optc = getopt_long (argc, argv, "+iu:0", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "+C:iu:0", longopts, NULL)) != -1)
if (optc == 'u' && unsetenv (optarg))
die (EXIT_CANCELED, errno, _("cannot unset %s"), quote (optarg));
@@ -139,19 +147,34 @@ main (int argc, char **argv)
optind++;
}
- /* If no program is specified, print the environment and exit. */
- if (argc <= optind)
+ bool program_specified = optind < argc;
+
+ if (opt_nul_terminate_output && program_specified)
{
+ error (0, 0, _("cannot specify --null (-0) with command"));
+ usage (EXIT_CANCELED);
+ }
+
+ if (newdir && ! program_specified)
+ {
+ error (0, 0, _("must specify command with --chdir (-C)"));
+ usage (EXIT_CANCELED);
+ }
+
+ if (! program_specified)
+ {
+ /* Print the environment and exit. */
char *const *e = environ;
while (*e)
printf ("%s%c", *e++, opt_nul_terminate_output ? '\0' : '\n');
return EXIT_SUCCESS;
}
- if (opt_nul_terminate_output)
+ if (newdir)
{
- error (0, errno, _("cannot specify --null (-0) with command"));
- usage (EXIT_CANCELED);
+ if (chdir (newdir) != 0)
+ die (EXIT_CANCELED, errno, _("cannot change directory to %s"),
+ quoteaf (newdir));
}
execvp (argv[optind], &argv[optind]);
diff --git a/tests/misc/env.sh b/tests/misc/env.sh
index f2f6ba8df..fc589cccb 100755
--- a/tests/misc/env.sh
+++ b/tests/misc/env.sh
@@ -18,7 +18,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
-print_ver_ env
+print_ver_ env pwd
# A simple shebang program to call "echo" from symlinks like "./-u" or "./--".
echo "#!$abs_top_builddir/src/echo simple_echo" > simple_echo \
@@ -150,4 +150,17 @@ test "x$(sh -c '\c=d echo fail')" = xpass && #dash 0.5.4 fails so check first
returns_ 125 env -u a=b true || fail=1
returns_ 125 env -u '' true || fail=1
+# Verify changing directory.
+mkdir empty || framework_failure_
+returns_ 125 env --chdir=empty/nonexistent true || fail=1
+returns_ 125 env -C empty 2>out || fail=1
+printf '%s\n' \
+ 'env: must specify command with --chdir (-C)' \
+ "Try 'env --help' for more information." > exp ||
+ framework_failure_
+compare exp out || fail=1
+exp=$(cd empty && env pwd) || framework_failure_
+got=$(env --chdir=empty pwd) || fail=1
+test "$exp" = "$got" || fail=1
+
Exit $fail