70

I know in bash I can run one command after another by separating them by semicolons, like

$ command1; command2

Or if I only want command2 to run only if command1 succeeds, using &&:

$ command1 && command2

This works, but if I suspend command1 using Ctrl-z, in the first case, it runs command2 immediately, and in the second case, it doesn't run it at all. How can I run commands in sequence, but still be able to suspend the first command, but not have the second run until I have restarted it (with fg) and it finishes? I'd prefer something as simple to type as possible, as I would like to do this interactively. Or maybe I just need to set an option somewhere.

By the way, what is the proper term for what Ctrl-z does?

codeforester
  • 39,467
  • 16
  • 112
  • 140
asmeurer
  • 86,894
  • 26
  • 169
  • 240

2 Answers2

95

The following should do it:

(command1; command2)

Note the added parentheses.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • 5
    Awesome! Now, do you or anyone care to give an explanation as to why it behaves the way it does for these three cases (`command1; command2`, `command1 && command 2`, and `(command1; command2)`)? – asmeurer Nov 28 '12 at 08:18
  • 2
    Actually, I am not sure. Perhaps someone could enlighten both of us. – NPE Nov 28 '12 at 08:25
  • 44
    Technically, you don't suspend processes with Control-z, you suspend *jobs*. With `command1; command2`, the active job is just the process `command1` when you press Control-z, so it is suspended and the next job (`command2`) begins. With `(command1; command2)`, the active job consists of the subshell that runs the two commands, so the entire subshell is suspended by Control-z. You can confirm this by running the `jobs` command after suspending each. – chepner Nov 28 '12 at 13:50
  • Thanks @chepner, and what about the `command1 && command2` option? That is what I frequently encounter. Even though the _job_ consists of both commands together, I have also noticed that the second command won't execute after backgrounding. `(command1 && command2)` works, of course, but I often do not know that I want to background a process ahead of time. Otherwise I would have executed it as a background job from the start. – mattgately Aug 29 '14 at 18:04
  • 1
    `command1 && command2` is actually two jobs (a job consists of a pipeline, and the `&&` list consists of two (single-process) pipelines, `command1` and `command2`). The second command doesn't run until `command1` exits (with zero status), and `command1` doesn't exit when it is stopped. – chepner Aug 29 '14 at 18:13
11

In Bash, when you place a job into the background (using CTRL+Z or &) it does not wait for the job to finish, and gives an exit code of zero (success). That much you have observed, and it is documented in the man pages.

The behaviour of logical "AND", &&, is that it tests from left-to right. Each part must be successful, so if the first is unsuccessful then the second will not run. So with && it runs commands from left to right until one of them fails. The definition of success is an exitcode ($?) of zero.

Contrast this with logical "OR", ||, which runs commands from left to right until one of them works.

Explaination of the subshell solution give by @NPE can also be found in the man pages:

Compound commands and command sequences of the form ‘a ; b ; c’ are not handled gracefully when process suspension is attempted. When a process is stopped, the shell immediately executes the next command in the sequence. It suffices to place the sequence of commands between parentheses to force it into a subshell, which may be stopped as a unit.

The proper term for CTRL+Z is the suspend character, again from the man pages:

Typing the suspend character (typically ^Z, Control-Z) while a process is running causes that process to be stopped and returns control to bash.

(Sorry to quote the man pages so much, but they really are your friends and worth reading)

If you look at stty -a you will see something like this:

susp = ^Z; 

So you can alter it, hence the phrase "typically". Don't do that though, it will confuse the heck out of everyone. The terminal driver raises a SIGTSTP signal which is trapped by Bash.

cdarke
  • 42,728
  • 8
  • 80
  • 84
  • I still don't understand the behavior with &&. Does suspending the process cause it to return nonzero? – asmeurer Nov 28 '12 at 15:32
  • What man pages are these from? I figured the answer might be there, but I had no idea where to even start. – asmeurer Nov 28 '12 at 15:34
  • `&&` joins two *pipelines*, and each pipeline is a separate job. (Note that pipeline is one or more commands joined by a `|`, which means `command1` is technically a single one-command pipeline). – chepner Aug 29 '14 at 18:17