Execute a command from another directory in bash

125

32

Say that I'm doing this:

cd subdir
git init
cd ../

Is there a way to do this with a single command, or perhaps two, rather than having to move in and out of a directory in order to run a command there?

(Not looking for a git-specific solution; that's just an example.)

Trevor Burnham

Posted 2011-04-17T16:05:57.320

Reputation: 1 661

Answers

214

This is often the best way:

( cd dir ; git init )

or

( cd dir && git init )

It's pretty short and easy to type. It does start a sub-shell, so you can't modify your environment from that, but that doesn't seem to be an issue here.

Mat

Posted 2011-04-17T16:05:57.320

Reputation: 6 193

1@CraigMcQueen: not really. $? will contain the exit code of the last command that ran in the subshell. If you use the && variant (which you usually should), then you'll get the exit code of the first command that failed (or 0 if all went ok). – Mat – 2015-07-20T05:12:02.440

I think the parentheses are optional – Francis.Beauchamp – 2016-05-05T13:10:39.373

11@Francis.Beauchamp: if you omit the parens, you'll be in the subdir after the command rather than back where you started. – Mat – 2016-05-05T13:19:46.743

How does one do this on Windows? – Karl Morrison – 2019-07-19T06:08:49.180

1And if you do need to set an environment variable or two, just add that as another command at the beginning. – Hippo – 2012-12-24T11:18:52.070

23

I was looking for a way to execute the git command from a path, and make changes to the repository in a different path. So I ended up in this question here.

But for my specific needs neither the accepted answer nor any of the other ones helped.

I needed to run git commands using sudo -u USER /usr/bin/git (another user running it). And as you may know, sudo doesn't allow me to run the cd command, so I can't be in the repository directory.

So, I went to git's man page. And among the options, I saw the --git-dir=<path>:

--git-dir=

Set the path to the repository. This can also be controlled by setting the GIT_DIR environment variable. It can be an absolute path or relative path to current working directory.

So, if it help someone, you can still use git from a path and make changes to a repository "far from you". Just use:

git --git-dir=/path/to/repository GIT_COMMAND

or, to run it as another user, do something like:

echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir=/path/to/repository GIT_COMMAND

Also from git-init's man page:

If the $GIT_DIR environment variable is set then it specifies a path to use instead of ./.git for the base of the repository.

So, if you want to init the repository under the usual .git folder, you will need to specify it together with the --git-dir option. e.g.:

echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir=/path/to/repository/.git init

After initializing the repository on /path/to/repo/.git, all further commands should have the option --work-tree=<path>, as described on git's man page:

--work-tree=

Set the path to the working tree. It can be an absolute path or a path relative to the current working directory. This can also be controlled by setting the GIT_WORK_TREE environment variable and the core.worktree configuration variable (see core.worktree in git-config(1) for a more detailed discussion).

So, the right command to run git as another user, and initialize a new repository is:

echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir=/path/to/repository/.git init
echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir='/path/to/repository/.git' --work-tree='/path/to/repository' add /path/to/repository/*
echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir='/path/to/repository/.git' --work-tree='/path/to/repository' commit -m 'MESSAGE'
echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir='/path/to/repository/.git' --work-tree='/path/to/repository' remote add origin user@domain.com:path
echo USER_PASSWORD | sudo -u USER_LOGIN -S /usr/bin/git --git-dir='/path/to/repository/.git' --work-tree='/path/to/repository' push -u origin master

dmmd

Posted 2011-04-17T16:05:57.320

Reputation: 331

If you're using sudo interactively and not through a script, you could simply do sudo -i or sudo su to get an interactive root shell. – Bugster – 2018-08-28T23:03:43.570

I can’t imagine why ( cd subdir && sudo -u USER /usr/bin/git init ) wouldn’t work. – Scott – 2019-06-23T17:30:04.613

What if I don't have permission to access subdir? – dmmd – 2019-06-24T12:40:35.773

13

Not exactly what you're asking (you have real answers above with the subshell) but look at pushd and popd

Rich Homolka

Posted 2011-04-17T16:05:57.320

Reputation: 27 121

I tried with cd && for maven and it didn't work, but pushd and popd does the job perfectly. Thanks – Radu Toader – 2015-06-25T17:47:47.407

6

You have a few options. You can either group the commands with && or ;. Like this:

cd subdir && git init && cd ..

or

cd subdir; git init; cd ..

The difference between these is that in the first example, if one of the commands fails, it will not execute the rest of them. In the second example, all of the commands will run no matter what.

Another option would be to define a function and use it, for instance:

function cdinit() {
    cd $1
    git init
    cd ..
}

Then you can run the command:

cdinit subdir

And it will automatically git init in that directory and move out of it.

You could also do a more complex solution using a function if you have a bunch of directories and want to git init them with one command.

function cdinit() {
    for arg in $@
    do
        cd $arg
        git init
        cd ..
    done
}

You can then run this with:

cdinit subdir1 subdir2 subdir3

And it will do git init in subdir1, subdir2, and subdir3.

Wuffers

Posted 2011-04-17T16:05:57.320

Reputation: 16 645

Do you think your cdinit function can be generalised for arbitrary commands? I tried using just the arguments, but that didn't work. – Chetan Bhasin – 2019-04-23T12:01:51.543

(1) Newline is equivalent to ;, so that three-line function is equivalent to cd $1; git init; cd ...  (2) You should quote your variables: "$1", "$@" and "$arg".  Or you can abbreviate for arg in "$@" to for arg. – Scott – 2019-06-23T17:29:47.100

Thanks. I was aware of && and ;, but hoped for something more elegant. Sounds like writing a script is my best option. – Trevor Burnham – 2011-04-17T16:21:54.523

Correction: This answer is fine, but Mat's answer is better for my particular needs. – Trevor Burnham – 2011-04-17T16:36:05.240

5

In case of git (at least in version 2.7.0), you can leverage the -C option which makes git behave as if it was started in the given directory. So your solution may look like:

> git -C subdir init
Initialized empty Git repository in /some/path/subdir/.git/

Quoting the documentation:

Run as if git was started in <path> instead of the current working directory. When multiple -C options are given, each subsequent non-absolute -C
<path> is interpreted relative to the preceding -C <path>.

This option affects options that expect path name like --git-dir and --work-tree in that their interpretations of the path names would be made
relative to the working directory caused by the -C option. 

Mifeet

Posted 2011-04-17T16:05:57.320

Reputation: 161

2

You can group the commands with &&, i.e.

cd subdir && git init && cd ../

If you don't want any dependency on the exit code of each command, you can use ; instead, i.e.:

cd subdir ; git init ; cd ../

Gaff

Posted 2011-04-17T16:05:57.320

Reputation: 16 863

(1) cd subdir && git init ; cd .. actually may make the most sense.  If the user wants to run the git init command in the subdir, then they probably don’t want to run the command in the current directory; i.e., they don’t want to run it if the (first) cd fails.  (Although it’s possible that the cd fails because we’re already in the subdir, but that’s a corner case.)  … (Cont’d) – Scott – 2019-06-23T17:29:12.327

(Cont’d) …  However, if the user wants to treat the block of three commands as a unit, they might want to cd back up to the starting directory even if the git init command fails.  (Or, they might want to stay in the subdirectory and diagnose the command failure.)  (2) You don’t need to include the / after ... – Scott – 2019-06-23T17:29:14.747

1Or with ; so that they don't depend on the return code of the previous one. – slhck – 2011-04-17T16:14:54.377

2

You have to hop into your target-directory if the command doesn't have a filename or directory name parameter.

But you can write a bash script that takes the target directory and the command as parameters. For this you could take a look at pushd and popd: http://ss64.com/bash/pushd.html

I would write that little script for you, but I haven't a Linux box here :)

wullxz

Posted 2011-04-17T16:05:57.320

Reputation: 2 400

Just saw the answer from Mark Szymanski. You could just implement a second parameter for a command (and rename the command) and you have what you want. – wullxz – 2011-04-17T16:25:30.080

1

Programs have different ways of handling arguments, so a few will have some equivalent of -folder=name option. Beyond that exception, the standard, even on MS DOS, is simply

$ program subdir

Sometimes you need

$ program subdir/

The program will open the folder, work with it the same way you work with a file, and once finished, return control to your shell, which is pointed at your original standard directory. Programs handled this way DO have the issue that error outputs (like core dumps) go to a file in your shell's current directory (rather than subdir.)

There is no workaround unless the program has command switches available to specify a different place. Some programmers take artistic license between "directory program was called from" and "directory program was told to work in ."

Vlueboy

Posted 2011-04-17T16:05:57.320

Reputation: 673