Is there a way to make bash job control quiet?

BashShellUnixParallel Processing

Bash Problem Overview


Bash is quite verbose when running jobs in the background:

$ echo toto&
toto
[1] 15922
[1]+  Done                    echo toto

Since I'm trying to run jobs in parallel and use the output, I'd like to find a way to silence bash. Is there a way to remove this superfluous output?

Bash Solutions


Solution 1 - Bash

You can use parentheses to run a background command in a subshell, and that will silence the job control messages. For example:

(sleep 10 & )

Solution 2 - Bash

Note: The following applies to interactive Bash sessions. In scripts, job-control messages are never printed.

There are 2 basic scenarios for silencing Bash's job-control messages:


Launch-and-forget:

CodeGnome's helpful answer answer suggests enclosing the background command in a simple subshell - e.g, (sleep 10 &) - which effectively silences job-control messages - both on job creation and on job termination.

This has an important side effect:

  • By using control operator & inside the subshell, you lose control of the background job - jobs won't list it, and neither %% (the spec. (ID) of the most recently launched job) nor $! (the PID of the (last) process launched (as part of) the most recent job) will reflect it.[1]

For launch-and-forget scenarios, this is not a problem:

  • You just fire off the background job,
  • and you let it finish on its own (and you trust that it runs correctly).

[1] Conceivably, you could go looking for the process yourself, by searching running processes for ones matching its command line, but that is cumbersome and not easy to make robust.


Launch-and-control-later:

If you want to remain in control of the job, so that you can later:

  • kill it, if need be.
  • synchronously wait (at some later point) for its completion,

a different approach is needed:

  • Silencing the creation job-control messages is handled below, but in order to silence the termination job-control messages categorically, you must turn the job-control shell option OFF:

    • set +m (set -m turns it back on)
    • Caveat: This is a global setting that has a number of important side effects, notably:
      • Stdin for background commands is then /dev/null rather than the current shell's.
      • The keyboard shortcuts for suspending (Ctrl-Z) and delay-suspending (Ctrl-Y) a foreground command are disabled.
      • For the full story, see man bash and (case-insensitively) search for occurrences of "job control".
  • To silence the creation job-control messages, enclose the background command in a group command and redirect the latter's stderr output to /dev/null

      { sleep 5 & } 2>/dev/null
    

The following example shows how to quietly launch a background job while retaining control of the job in principle.

$ set +m; { sleep 5 & } 2>/dev/null # turn job-control option off and launch quietly
$ jobs # shows the job just launched; it will complete quietly due to set +m 

If you do not want to turn off the job-control option (set +m), the only way to silence the termination job-control message is to either kill the job or wait for it:

Caveat: There are two edge cases where this technique still produces output:

  • If the background command tries to read from stdin right away.
  • If the background command terminates right away.

To launch the job quietly (as above, but without set +m):

$ { sleep 5 & } 2>/dev/null

To wait for it quietly:

$ wait %% 2>/dev/null    # use of %% is optional here

To kill it quietly:

{ kill %% && wait; } 2>/dev/null

The additional wait is necessary to make the termination job-control message that is normally displayed asynchronously by Bash (at the time of actual process termination, shortly after the kill) a synchronous output from wait, which then allows silencing.

But, as stated, if the job completes by itself, a job-control message will still be displayed.

Solution 3 - Bash

Wrap it in a dummy script:

quiet.sh:

#!/bin/bash
$@ &

then call it, passing your command to it as an argument:

./quiet.sh echo toto

You may need to play with quotes depending on your input.

Solution 4 - Bash

Interactively, no. It will always display job status. You can influence when the status is shown using set -b.

There's nothing preventing you from using the output of your commands (via pipes, or storing it variables, etc). The job status is sent to the controlling terminal by the shell and doesn't mix with other I/O. If you're doing something complex with jobs, the solution is to write a separate script.

The job messages are only really a problem if you have, say, functions in your bashrc which make use of job control which you want to have direct access to your interactive environment. Unfortunately there's nothing you can do about it.

Solution 5 - Bash

One solution (in bash anyway) is to route all the output to /dev/null

    echo 'hello world' > /dev/null &

The above will not give you any output other than the id for the bg process.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionstatic_rttiView Question on Stackoverflow
Solution 1 - BashTodd A. JacobsView Answer on Stackoverflow
Solution 2 - Bashmklement0View Answer on Stackoverflow
Solution 3 - BashjedwardsView Answer on Stackoverflow
Solution 4 - BashormaajView Answer on Stackoverflow
Solution 5 - BashACEView Answer on Stackoverflow