6

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;"
6
  • 4
    They work fine on my system. Please edit your question and tell us exactly how the aliases are defined. Do you also have this with very simple aliases? Try running alias a="echo foo"; alias b="echo bar"; a; b. Does that work?
    – terdon
    Feb 26, 2016 at 18:18
  • what are those aliases ? I mean when you say stopdev, what does the alias do ? I created 2 aliases to 2 simple commands and it works fine with a semicolon in between them
    – MelBurslan
    Feb 26, 2016 at 18:18
  • One of them is alias stopdev="cd $HOME/website; make website_stop; make backend_stop;". I think we've found the problem. If either of you want to provide an answer to the effect of "it's because your alias has a semi-colon at the end of its definition, dummy" I'll be happy to accept it. Feb 26, 2016 at 18:23
  • 2
    P.S. Just to be clear for anyone reading this later, removing the semi-colon from the end of my alias's definition did in fact fix the problem. Feb 26, 2016 at 18:26
  • 1
    P.P.S. And for anyone reading the above P.S., note that removing the last semi-colon is a bad idea as it will take any parameters fed to the alias and feed them to the make backend_stop command, as noted in @Gilles' answer.
    – Wildcard
    Feb 27, 2016 at 1:59

1 Answer 1

11

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
}
5
  • 1
    Exhaustively good answer. Feb 27, 2016 at 0:55
  • Does the subshell variation need curly braces for the function definition?
    – Jeff Schaller
    Feb 27, 2016 at 2:47
  • @JeffSchaller No, since it uses parentheses, precisely to make the body execute in a subshell. Feb 27, 2016 at 11:41
  • I had this issue with alias CLONE='nohup konsole --workdir "$(pwd)" &>/dev/null &' and alias REFRESH='CLONE; exit'. This worked until recently on my bash. Removing the ; makes it work again but I don't like that too much and dont understand how a backgrounding & stops me from following with ;; is it actually the same issue, or just coincidentally the same solution?
    – Hashbrown
    Mar 5, 2018 at 7:19
  • 1
    @Hashbrown It's the same issue. The reason is that & and ; in fact have the same role in the shell grammar: they terminate a simple command, and which one you use determines whether the command is executed in the foreground or in the background. They only appear to have different roles because semicolons are used rarely: usually they're conveyed by a newline, which means “if a command terminator is expected, pretend there was a ;, else ignore the newline”. alias REFRESH=$'CLONE\n exit' should work in all bash versions except antique ones. Use an actual newline instead for portability. Mar 5, 2018 at 18:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .