23

I have a .bash_profile and in that I have a set of aliases. Those aliases at the moment execute only a single command and it was fairly easy. I would however like to do two things with a new alias that I'm trying to create.

  1. CD into a directory
  2. Run a command from that directory
27

To execute a command with a specific working directory, one usually does

( cd directory && utility )

The parentheses around the cd ... means that the command(s) therein runs in a subshell. Changing the working directory in a subshell makes it so that the current working directory of the calling shell is not changed, i.e., after having called this command, you would still be located in the same directory where you started.

Example:

( cd / && echo "$PWD" )  # will output "/"
echo "$PWD"              # will output whatever directory you were in at the start

This can not be turned into a generic alias as an alias can not take any arguments.

For a specific directory and utility, one could do

alias cdrun='( cd "$HOME/somedir" && ./script.sh )'

but for the general case, you would have to use a shell function:

cdrun () {
    ( cd "$1" && shift && command "$@" )
}

or

cdrun () (
    cd "$1" && shift && command "$@"
)

Replacing the curly braces with parentheses around the body of the function makes the function execute in its own subshell.

This would be used as

$ cdrun "$HOME/somedir" ./script.sh

which would run the script script.sh located in the directory $HOME/somedir, with $HOME/somedir as the working directory, or

$ cdrun / ls -l

which would provide you with a directory listing in "long format" of the root directory.

The shell function takes its first argument and tries to change to that directory. If that works, it shifts the directory name off from the positional parameters (the command line argument list) and executes the command given by the rest of the arguments. command is a built-in command in the shell which simply executes its arguments as a command.


All of this is needed if you want to execute a command with a changed working directory. If you just want to execute a command located elsewhere, you could obviously use

alias thing='$HOME/somedir/script.sh'

but this would run script.sh located in $HOME/somedir with the current directory as the working directory.

Another way of executing a script located elsewhere without changing the working directory is to add the location of the script to your PATH environment variable, e.g.

PATH="$PATH:$HOME/somedir"

Now script.sh in $HOME/somedir will be able to be run from anywhere by just using

$ script.sh

Again, this does not change the working directory for the command.

| improve this answer | |
  • 2
    Excellent, comprehensive answer. I just thought I'd mention that the function definition only needs one method of grouping commands and the parentheses for the subshell are enough: cdrun () ( cd "$1" && shift && command "$@" ). – Anthony Geoghegan May 19 '17 at 8:19
  • 2
    @AnthonyGeoghegan Ah, that's true. I always use { ... } for consistency with longer functions though. – Kusalananda May 19 '17 at 8:21
  • This Q&A seems to speak of my situation, and may be an improvement on using a surrounding pushd and popd (also > /dev/null if you are trying to avoid the output messages as well), while also keeping the context clean! – Pysis May 19 '17 at 13:49
  • 1
    @Pysis Yes, (cd dir && thing) is also much more portable than using pushd and popd. – Kusalananda May 19 '17 at 18:34
  • @Kusalananda Really? I've had them work for at least several systems. – Pysis May 19 '17 at 18:50
6
alias <name-of-the-alias>='cd <the-directory> && <command>'

So, if you want to change directory (cd) into the folder /var/log/ and then list (ls) its files you could append in your .bash_profile file the following:

alias logs='cd /var/log/ && ls'
| improve this answer | |
1

I use this function to run a single command in a different directory:

cd1 () {
  if [ $# -eq 1 ]; then
    command cd -- "$1"
  else
    ( command cd -- "$1" && shift && "$@" )
  fi
}

A limitation of this function is that wildcards are completed relative to the original directory, not relative to the directory where the command runs. It's possible to do better in zsh.

This function acts like the ordinary cd if called with a single argument, so you may want to call it cd. (This function doesn't support any options, but options to cd are rarely used.)

| improve this answer | |
-1

Separate commands with a semicolon, e.g.:

alias do_something='cd /tmp; ls'
| improve this answer | |
  • 8
    If the cd fails, you would get the directory listing of the wrong directory. It's better to use && instead of ; here. – Kusalananda May 19 '17 at 7:02
  • @Kusalananda is right. You may find more in this answer – nikolas May 19 '17 at 7:04

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.