17

I know that you can use this to start a process in the background, without getting the notification when the process is put in the background and when it is done like so:

(command &) &> /dev/null

However this removes the option to trap when the process finishes (trap child_done CHLD).

How can I have both?

7 Answers 7

7

task-spooler might help you.

As in freshmeat.net:

task spooler is a Unix batch system where the tasks spooled run one after the other. The amount of jobs to run at once can be set at any time. Each user in each system has his own job queue. The tasks are run in the correct context (that of enqueue) from any shell/process, and its output/results can be easily watched. It is very useful when you know that your commands depend on a lot of RAM, a lot of disk use, give a lot of output, or for whatever reason it's better not to run them all at the same time, while you want to keep your resources busy for maximum benfit. Its interface allows using it easily in scripts.

Task-spooler is available in Debian, Ubuntu and other distros.

It lets you have a queue of tasks to run, and access their outputs just at wish.

For example:

$ tsp sleep 5
0 
$ tsp sleep 5 
1
$ tsp
ID   State      Output               E-Level  Times(r/u/s)   Command [run=1/1]
2    running    /tmp/ts-out.ZWn6Le                           sleep 5
1    finished   /tmp/ts-out.Fhlcle   0        5.00/0.00/0.01 sleep 5

If you run a lot of jobs in background, it will definitely help you.

You can either run them sequentially or in a preset amount of slots. You can also establish dependencies among the tasks (run task1 only if task0 completed successfully).

0
6

Here's how to do it in both {ba,z}sh.

Using a function helps not needing to save and restore the state of zsh's monitor variable by use of setopt local_options.

# Run the command given by "$@" in the background
silent_background() {
  if [[ -n $ZSH_VERSION ]]; then  # zsh:  https://superuser.com/a/1285272/365890
    setopt local_options no_notify no_monitor
    "$@" &
  elif [[ -n $BASH_VERSION ]]; then  # bash: https://stackoverflow.com/a/27340076/5353461
    { 2>&3 "$@"& } 3>&2 2>/dev/null
  else  # Unknownness - just background it
    "$@" &
  fi
}

Using this as follows:

trap 'echo child done' CHLD
silent_background sleep 1

Will produce the expected child done message.

1
5

The redirection of the command output seems to be irrelevant because the notification is send by the shell when a job is started asynchronously. More precisely, it is a shell feature (fonctionality) related to job control.

Here, a quote that comes from the "Bash Reference Manual", chapter "Job Control", section one.

The shell associates a JOB with each pipeline. It keeps a table of currently executing jobs, which may be listed with the jobs command. When Bash starts a job asynchronously, it prints a line that looks like:

[1] 25647

indicating that this job is job number 1 and that the process ID of the last process in the pipeline associated with this job is 25647. All of the processes in a single pipeline are members of the same job. Bash uses the JOB abstraction as the basis for job control.

Note that a shell script does not display this notification.

$ cat test
#!/bin/bash

true & echo true
$ ./test
true

In facts

Zsh

The Zsh documentation provides similar indications about these notifications, see man 1 zshmisc, section "JOBS". These notifications are not displayed when job control is disabled.

MONITOR (-m, ksh: -m)

    Allow job control. Set by default in interactive shell.

zsh_prompt % setopt no_monitor
zsh_prompt % true & echo true
true
zsh_prompt %
zsh_prompt % 

Bash

It seems that Bash always displays e.g. [1] 25647. The "final notification" e.g. [1]+ Done true is not displayed when job control is disabled.

bash_prompt $ true & echo true
[1] 25647
true
bash_prompt $ 
[1]+  Done                    true

Job control disabled

bash_prompt $ set +m       # disable job control
bash_prompt $ true & echo true
[1] 25685
bash_prompt $
bash_prompt $

Conclusion

I do not know if disable job control to hide the notifications is a good thing?

Resources

2

You may invoke zsh as a non-interactive shell to disable job notifications.

zsh -c '
   trap "echo trapped child_done" CHLD
   sleep 5 &
   #wait
   sleep 10 # simulate other running cmds
   echo script done
'
1
  • brilliantly simple! The only downside I can see is that your trap line needs to be a part of the argument to -c, which is not necessary in Tom Hale's answer (unix.stackexchange.com/a/452568/3358). Commented Mar 7, 2022 at 15:53
1

See the following example :

trap 'echo "DONE"' 0
{ find /usr & } &> /dev/null
wait

That works well. No need ( ) : that makes a not needed subshell. { } are just grouping commands.

2
  • 2
    That prints [2] 49591 and [2] + 49591 exit 1 find /usr, when it should print neither of these! Commented Oct 7, 2012 at 0:28
  • Sorry, misunderstood your question so. Commented Oct 7, 2012 at 0:46
1

This is an old question, but a possible answer (in bash, not tested with zsh) is to use the job control in the subshell: (command & wait)

Note: bash suppresses the start/end background job messages in the subshell, and there no need for redirection.

2
  • I think you missed the part with the trap. As far as I do understand the question the subprocess should be started, something will be done while the supprocess executes and then when the subprocess finished something else should be executed. With your solution you start the subprocess wait for it in the subshell and the main shell waits for the subshell. This gets rid of the messages, but you can't do something in parallel. Commented Apr 4, 2017 at 8:04
  • Nope, (sleep 5 & wait) delays 5 seconds. Commented Jun 26, 2018 at 18:11
-1

2 Possible Answers:

- Subshell with redirects.
     or
- Push to a Screen ( a multiplexer / virtual terminal ) session
  • The 'Screen' option maybe a bit convoluted, but it also gives you the ability to attach the 'Named' screen session and see what's happening.
1. You can run in a subshell, and redirect all output to /dev/null.
$( { sleep 10; touch a_file } >/dev/null 2>&1 & ) >/dev/null 2>&1;
2. A detached screen session.
  • Create Screen with a name Detached.
screen -dmS "${SCREEN_NAME}";
  • Run (stuff) the command(s) into the named screen session.
screen -S "${SCREEN_NAME}" -X stuff "${CMD_LINES}\nsleep ${SECS}\ntouch a_file\nexit\n";
I like adding a touched file to watch and wait for the backgrounded process to complete before Exiting the main script

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.