67

Is there a way to execute a command in a different directory without having to cd to it? I know that I could simply cd in and cd out, but I'm just interested in the possibilities of forgoing the extra steps :)

2
  • It's rather trivial to make a script that does this: echo "#!/bin/bash; cd $1; exec $2" > /usr/local/bin/execindirectory; chmod +x /usr/local/bin/execindirectory. Might need to put a little more effort if you want it to actually support option "tags" such as -d and stuff.
    – LawrenceC
    May 26, 2011 at 2:26
  • As user-unknown states in an answer, all the examples you've given here and in comments are better addressed by other solutions, so it's unclear if there is actually a problem that needs solving here. Can you come up with a better example?
    – Caleb
    May 26, 2011 at 9:43

7 Answers 7

77

I don't know if this counts, but you can make a subshell:

$ (cd /var/log && cp -- *.log ~/Desktop)

The directory is only changed for that subshell, so you avoid the work of needing to cd - afterwards.

5
  • 2
    That's what I usually do. May 26, 2011 at 17:48
  • Nice, I had forgotten altogether about sub-shells, very handy in this case. May 26, 2011 at 21:02
  • 9
    To avoid potential chaos, sub out the semicolon with two ampersands: $ (cd /var/log && cp *.log ~/Desktop). That way, if the directory doesn't exist, no further commands are executed. Apr 11, 2013 at 19:38
  • 8
    Note for anyone else as naive as me: the $ at start isn't part of the command, just everything after it.
    – Maltronic
    Apr 15, 2017 at 13:58
  • 1
    TNX, worked on arch/zsh.
    – aasmpro
    May 9, 2020 at 20:25
20

Not to undermine the value of answers given by other people, but I believe what you want is this:

(cd /path/to && ./executable [ARGS])

Note the parens to invoke cd in a sub-shell.

2
  • I don't think so. As I understand, he wants to end up in the initial directory. May 26, 2011 at 17:51
  • 1
    Oh, I totally forgot to wrap that into sub-shell. Corrected my answer, thank you.
    – alex
    May 26, 2011 at 18:48
16

Some programs have options with which you can tell them to chdir(2) themselves (e.g. GNU tar’s -C/--directory).

Outside of such programs though, something will have to chdir. You could write and use some sort of compiled “binary” program instead of having the shell do it, but it probably would not yield much benefit.

In a comment in another answer, you gave an example:

execindirectory -d /var/log "cp *.log ~/Desktop"

Since the *.log pattern is expanded by the shell itself (not cp), something will have to chdir to the directory before having a shell evaluate your command.

If you are just interesting in avoiding having to “cd back”, then you can use a subshell to isolate the effect of the cd from your working shell instance.

(cd /path/to/dir && some command)

You can package this up in a shell function. (I dropped the -d option from your example usage since there is little point to this command if the directory is actually optional.)

runindir() { (cd "$1" && shift && eval "$@"); }

runindir /var/log 'cp *.log ~/Desktop'  # your example
runindir /var/log cp \*.log \~/Desktop  # eval takes multiple args

runindir /var/log cp \*.log ~/Desktop   # it is okay to expand tilde first
1
  • 2
    I like this solution, except, personally I'd use pushd/popd
    – N J
    May 26, 2011 at 3:57
4

Here is something that should let you cd back where you were (using Bash), since not forgetting to do so seems to be the purpose of the question:

# Save where you are and cd to other dir
pushd /path/to/dir/that/needs/to/be/current/dir

run-your-command

# Get back where you were at the beginning.
popd

(EDIT: slightly shorter version, thanks to @Random832)

2
  • 1
    you could just pushd other-dir, rather than pushd .; cd other-dir
    – Random832
    May 26, 2011 at 12:06
  • @Random832, good point!
    – Bruno
    May 26, 2011 at 12:09
3

Sadly, your example:

execindirectory -d /var/log "cp *.log ~/Desktop"

doesn't need a change to the dir, because

cp /var/log/*.log ~/Desktop

would do the same. Can't you get closer to your real problem? Because we might know a better solution for that too.

A complicated way to solve your problem, which is far away from the elegance of Michaels solution, is the usage of find, which has a switch '-execdir' to be performed in the dir, where a file is found. Badly adopted to your example:

find /var/log -maxdepth 1 -type f -name "*.log" -execdir echo cp {} ~/Desktop ";"

Maybe it is useful for your real problem. -okdir instead of -execdir will ask you to confirm every invocation.

-okdir and -execdir might need gnu-find to be installed, which is typically used on Linux.

2

How about ./your/path/command.sh?

2
  • No, I actually meant something like the following: execindirectory -d /var/log "cp *.log ~/Desktop". May 26, 2011 at 1:23
  • I'm perfectly aware that this isn't strictly necessary, by the way, I'm just interested in if it can actually be done. May 26, 2011 at 1:24
2

The env program from GNU coreutils-8.28 (released 2017-09-01) and newer has --chdir:

env: add --chdir option

This is useful when chaining with other commands that run commands in a different context, while avoiding using the shell to cd, and thus having to consider shell quoting the chained command.

$ env --help | grep chdir
  -C, --chdir=DIR      change working directory to DIR

So on a modern system (RHEL 8+, Ubuntu LTS 20.04+) you can just:

env --chdir=/tmp pwd

This is especially useful if you need to use sudo and don't want to mess up quoting the arguments.

FILENAME="Some file maybe with spaces.txt"
FILEDIR="/some/directory maybe with spaces"
sudo env --chdir "$FILEDIR" \
  zip "/tmp/$FILENAME.zip" "$FILENAME"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.