Bash – Why Can’t I Call Two Aliases With “;”

aliasbashshell

When I try to combine two normal commands using the ; character (eg. ls; cd) it works fine. However, I have two aliases that I've created (stopdev and startdev), and if I try to combine them:

stopdev; startdev

or even if I just try and add a semi-colon after one:

stopdev;

I get a syntax error:

bash: syntax error near unexpected token `;'

I also have the same problem if I use &&:

stopdev && startdev

bash: syntax error near unexpected token `&&'

I'm confused by this because I had thought that aliases were just like any other commands … but clearly they aren't.

So, two questions:

  1. Why is using ; or && with an alias call invalid?
  2. Is there any way (other than creating a stopstartdev alias) to easily run these two commands together?

Here's the definition of stopdev:

alias stopdev="cd $HOME/website; make website_stop; make backend_stop;"

Best Answer

An alias is expanded simply by replacing the alias by its definition (as a list of tokens, not a string, which is basically equivalent to taking the string and adding a space at the end). So stopdev; true is expanded to

cd $HOME/website; make website_stop; make backend_stop; ; true
                                                      ^^^

Since you can't have two consecutive semicolons in the shell syntax, that's a syntax error.

You can remove the ;, and that will make stopdev; startev work, but it isn't good, because any argument you pass to stopdev will be passed to make backend_stop, which is probably not desirable.

You should make this a function. Also, don't run the make commands if the cd command fails.

stopdev () {
  cd "$HOME/website" && {
    make website_stop
    make backend_stop
  }
}

An improvement would be to make the function return a failure code even if make website_stop fails but make backend_stop succeeds.

stopdev () {
  cd "$HOME/website" && {
    make website_stop
    ret=$?
    make backend_stop && return $ret
  }
}

Note that this leaves you in the ~/website directory. To avoid changing the directory of the shell process, run the function in a subshell.

stopdev () (
  cd "$HOME/website" && {
    make website_stop
    ret=$?
    make backend_stop && return $ret
  }
)

Alternatively, with GNU make, you can use its -C option.

stopdev () {
  make -C "$HOME/website" website_stop
  ret=$?
  make -C "$HOME/website" backend_stop && return $ret
}

If the targets never fail, just pass them both.

stopdev () (
  cd "$HOME/website" && make website_stop backend_stop
)

or

stopdev () {
  make -C "$HOME/website" website_stop backend_stop
}
Related Question