22

We have env(1) to modify the environment of the command we want to run (for example env MANPAGER=more man dtrace). Is there something similar but for modifying the directory that the command is going to be started in?

Ideally, I would like it to look like this:

theMagicCommand /new/cwd myProgram

This way it could be "chained" with other env(1)-like commands, e.g.,

daemon -p /tmp/pid env VAR=value theMagicCommand /new/cwd myProgram

So far I can think of the following solution, which unfortunately does not have the same interface as env(1):

cd /new/cwd && myProgram

Also, I can just create a simple shell script like this:

#! /bin/sh -
cd "${1:?Missing the new working directory}" || exit 1
shift
exec "${@:?Missing the command to run}"

but I am looking for something that already exists (at least on macOS and FreeBSD).

myProgram is not necessarily a desktop application (in which case I could just use the Path key in a .desktop file).

Mateusz Piotrowski
  • 4,623
  • 5
  • 36
  • 70
  • 6
    Why does `cd /new/cwd && env VAR=value myProgram` not meet your critera? – jpaugh Nov 09 '18 at 19:27
  • That's already in the question, where M. Piotrowski explained that that is not the same interface as `env`. Look at `env`. Compare it to `rtprio`, `idprio`, `numactl`, `jexec`, `chrt`, and indeed the commands in the toolsets mentioned in the answers. There's a pattern, and it is chain loading. – JdeBP Nov 10 '18 at 00:08
  • 3
    What do you mean by “interface”? And why not use `(cd the/cwd; cmd)`? – Konrad Rudolph Nov 10 '18 at 10:16
  • 2
    @KonradRudolph For example, you cannot easily pass `(cd the/cwd; cmd)` to env(1) without wrapping it with sh(1). – Mateusz Piotrowski Nov 10 '18 at 20:41

4 Answers4

20

AFAIK, there is no such dedicated utility in the POSIX tool chest. But it's common to invoke sh to set up an environment (cwd, limits, stdout/in/err, umask...) before running a command as you do in your sh script.

But you don't have to write that script in a file, you can just inline it:

sh -c 'CDPATH= cd -P -- "$1" && shift && exec "$@"' sh /some/dir cmd args

(assuming the directory is not -). Adding CDPATH= (in case there's one in the environment) and -P for it to behave more like a straight chdir().

Alternatively, you could use perl whose chdir() does a straight chdir() out of the box.

perl -e 'chdir(shift@ARGV) or die "chdir: $!"; exec @ARGV or die "exec: $!"
         ' /some/dir cmd args
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • Oh, yeah. This is indded a very handy variation of the script I included in my question. I was afraid that the utility I am looking for does not exist since its functionality is already available with a relatively short sh(1) oneliner. – Mateusz Piotrowski Nov 09 '18 at 15:17
  • 8
    If you're already running `sh`, you can also do `(cd /wherever && exec /my/command)`. The `()` implicitly opens a subshell to run the wrapped commands, and of course, `exec` gets rid of the extra shell process as quickly as `/my/command` starts running. – jpaugh Nov 09 '18 at 19:24
  • 2
    That was suggested in a now-deleted answer. The questioner explained that that was not an answer to what xe asked. – JdeBP Nov 10 '18 at 00:22
15

The toolsets used in the daemontools world, and elsewhere, have this and more besides; have had for many years; and are widely available.

  • Wayne Marshall's perp has runtool:
    runtool -c /new/cwd myProgram
  • Laurent Bercot's execline has cd:
    cd /new/cwd myProgram
  • my nosh toolset has chdir:
    chdir /new/cwd myProgram

All of these are chain-loading tools, designed to be used in exactly these sorts of chains. There is a wide selection of chain-loading tools in these toolkits for other purposes.

Further reading

JdeBP
  • 66,967
  • 12
  • 159
  • 343
11

There is such a popular program. It is called ... hold onto your chair... drumroll... env. The GNU version, since version 8.28, not POSIX, has the -C option which lets you set the directory just as you require:

    NAME
           env - run a program in a modified environment

    SYNOPSIS
           env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]

    DESCRIPTION
           Set each NAME to VALUE in the environment and run COMMAND.

           Mandatory arguments to long options are mandatory for short options too.

           -i, --ignore-environment
                  start with an empty environment

           -0, --null
                  end each output line with NUL, not newline

           -u, --unset=NAME
                  remove variable from the environment

           -C, --chdir=DIR
                  change working directory to DIR

           --help display this help and exit

           --version
                  output version information and exit

           A mere - implies -i.  If no COMMAND, print the resulting environment.

terdon
  • 234,489
  • 66
  • 447
  • 667
1

Certain programs have an option for this, like Git:

-C <path>

Run as if git was started in <path> instead of the current working directory.

and Make:

-C dir, --directory=dir

Change to directory dir before reading the makefiles or doing anything else.

and Tar:

-C, --directory=DIR

Change to DIR before performing any operations. This option is order-sensitive, i.e. it affects all options that follow.

Zombo
  • 1
  • 5
  • 43
  • 62
  • Yes. Although my question is what if `myProgram` does not provide such an option... Thank you for your contribution but I'm afraid it does not answer my question at all. – Mateusz Piotrowski Nov 10 '18 at 20:43