0

I am running a process in a sub-shell which may or may not hang, then killing that process if it is still running after 10 seconds.

In doing so, I pipe stdout and stderr to /dev/null with > /dev/null 2>&1, which seems to work. However, the output of the kill command then prints to stdout after my next command, no matter what command I use.

[me@test] (Command_That_May_Hang.sh)&
[me@test] X=$!
[me@test] (sleep 10 && kill -9 $! > /dev/null 2>&1)&
[me@test] ps
>   PID TTY          TIME CMD
> 22741 pts/1    00:00:00 bash
> 27585 pts/1    00:00:00 ps
> [1]+  Killed                  Command_That_May_Hang.sh > /dev/null 2>&1

I have tried moving /dev/null 2>&1 to the outside of () and even after &, with no change in results. For reference, those iterations look like:

(sleep 10 && kill -9 $!) > /dev/null 2>&1 &
(sleep 10 && kill -9 $!)& > /dev/null 2>&1

Executing the process and then killing it both work successfully, it just seems that I can only temporarily suppress the output, which is strange behavior. Any help on correctly sending output to /dev/null would be appreciated.

Jodie Evan
  • 11
  • 3
  • I don't see any output from `kill`. What are you referring to? – melpomene Jan 23 '19 at 20:38
  • Your redirections only apply to the individual commands they're run against. If you want to suppress output from `Command_That_May_Hang`, then you need to run `Command_That_May_Hang >/dev/null 2>&1`. Suppressing output from the `kill` has no impact on whether the separate program that is the *target* of that `kill` writes output. – Charles Duffy Jan 23 '19 at 20:39
  • nit: "I pipe stdout and stderr" is incorrect. You are redirecting stdout and stderr to /dev/null. No pipe is involved. – William Pursell Jan 23 '19 at 22:11

2 Answers2

2

The message is printed by bash itself, not by the kill command. So you can't suppress it by redirecting any output in your script.

What you can do is to disable the monitor mode of job control by executing

set +m

Quote from the bash man page:

-m Monitor mode. Job control is enabled. This option is on by default for interactive shells on systems that support it (see JOB CONTROL above). All processes run in a separate process group. When a background job completes, the shell prints a line containing its exit status.

With a +m the monitor mode is disabled.

Ralf
  • 1,773
  • 8
  • 17
1

Speaking to actual subprocess output, not shell diagnostic status

As I understand it, you want to suppress output from your program only after it's received a kill signal.

You can't do that by redirecting the output from kill itself; you can only do that by changing how the program itself has its output directed at startup time.

In the following example, we redirect stdout and stderr of your command to a separate shell process, and tell that shell process to stop printing output when it receives a SIGUSR1.

#!/usr/bin/env bash
case $BASH_VERSION in ''|[123].*|4.[012]) echo "ERROR: Bash 4.3 required" >&2; exit 1;; esac

process_output_until_signal() {
  running=1
  trap 'running=0' USR1
  while IFS= read -r line; do
    (( running )) && printf '%s\n' "$line"
  done
}
exec 3> >(process_output_until_signal); output_loop_pid=$!
Command_That_May_Hang >&3 2>&3 & command_pid=$!
exec 3>&-

sleep 10
kill -USR1 "$output_loop_pid"
sleep 0.1
kill "$command_pid"

Speaking to shell diagnostic status

See the answer by Ralf: This is output written by the shell, not by the kill command, and also not by the command that was killed. (It's also written only when the command is running in a configuration that's enabled by default only for interactive shells and not for scripts).

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441