5

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;"
  • 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 '16 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 '16 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. – machineghost Feb 26 '16 at 18:23
  • 1
    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. – machineghost Feb 26 '16 at 18:26
  • You can remove the last ; after backend_stop – Thomas Feb 26 '16 at 18:26
9

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
}
  • 1
    Exhaustively good answer. – glenn jackman Feb 27 '16 at 0:55
  • Does the subshell variation need curly braces for the function definition? – Jeff Schaller Feb 27 '16 at 2:47
  • @JeffSchaller No, since it uses parentheses, precisely to make the body execute in a subshell. – Gilles Feb 27 '16 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 '18 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. – Gilles Mar 5 '18 at 18:27

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.