How could I hide the standard output of a process only when it is running in background?
For example,
test.sh
#!/bin/bash
for i in $(seq 200); do echo $i
doneNow, I run the script. I stop it. I put it back to test it keeps running. Then I put it on background but it keeps printing in the standard output and I can't keep working. Is there a way to redirect or hide the process output ONLY when it is on background?
dione@saturno:~$ ./test.sh
1
2
3
^Z
[1]+ Detenido ./test.sh
dione@saturno:~$ fg %1
./test.sh
4
5
6
^Z
[1]+ Detenido ./test.sh
dione@saturno:~$ %1 &
[1]+ ./test.sh &
dione@saturno:~$ 7
8
9
10
11
12
13
14
15
fg
./test.sh
16
17
^C
dione@saturno:~$ 3 2 Answers
Redirecting a running process's output is not easy. But there might be alternatives if you work from the start:
some_long_runnning_command | tail -n 1000 & pkill -STOP tailWith this method, since the tail has SIGSTOP sent to it, it won't output anything until you explicitly bring it to the foreground using fg (or something similar) and until the script has finished. Adjust the 1000 to get as many lines as you want.
A better alternative might be grep:
some_long_runnning_command | grep . --color=never & pkill -STOP sedYou can signal SIGCONT and SIGSTOP to grep to start and stop the output as you want.
This can probably be combined with the re-attachment of open file descriptors from the SO question linked above (maybe with the script in this answer) so that this can be done with processes already opened.
If buffering is indeed a problem with grep, as Volker Siegel points out, then one might consider using a more complex set of commands. First, note that sort uses temporary files, so buffering should not be a problem for it. It will act like a combination of tail and grep - waiting until the command is finished, like tail, and keeping the entire output, like grep. Something like:
some_long_runnning_command | awk '{$0=";"$0}1' | sort -k1.1,1.1 --stable | sed 's/^;//' & pkill -STOP sedThe loss here is that you can't start and stop the output at will, and there's the huge computation expended in an useless sort, as well as the work expended in adding and removing the ;.
Further tests reveal that Volker is indeed correct:
For a script like (call it test.sh):
#! /bin/bash
for i in $(seq 1000000); do echo $i; echo $i > /tmp/log; doneand the command:
./test.sh | grep . --color=never & pkill -STOP grepthe number in /tmp/log was stuck at 12768 (with a similar figure for .
And with the command
./test.sh | awk '{$0=";"$0}1' | sort -k1.1,1.1 --stable | sed 's/^;//' & pkill -STOP sedThe script ran to completion (/tmp/log had 1000000) without a peep from sed.
I still haven't managed to integrate this with the GDB method, but this as it is can be made to function as a function, similar to John1024's. The problem is that you have no good way of knowing if the process has ended, since the shell only notifies you if the entire job has ended. So you'll have to use a checking function:
function check ()
{ local PROC=${1:-$(jobs -p %%)} kill -0 $PROC 2>/dev/null && echo "The process is still running." || echo "The process is not running."
}
function bg ()
{ "$@" | awk '{$0=";"$0}1' | sort -k1.1,1.1 --stable | sed 's/^;//' & local PID=$! sleep 0.1s kill -STOP $PID cat <<EOF
To get the output, do: kill -CONT $PID
To check if the process has finished, do: check $(jobs -p %%)
EOF
}You could wrap the "$@" in another function which will notify you once the job is done, using something like notify-send or write.
It's been a nice exercise, but ultimately the simplest way is to redirect to /dev/null and forget the output: How to redirect the output of an application in background to /dev/null.
This script redirects stdout to /dev/null only if the script is running in the background:
#!/bin/sh
case "$(ps -o stat= -p $$)" in *+*) : ;; *) exec 1>/dev/null ;;
esac
for i in $(seq 200); do echo $i
doneThe key here is that the ps stat field has a + in it when the job is foreground. If the + is missing, we are in background.
If you want to keep stdout around for some things but not others, we can create a file descriptor 3 for the discretionary output and keep stdout active for other output.
The script below implements this. It sends the loop output to file descriptor 3. File description 3 is either stdout if the job is running in the foreground or /dev/null if it is running in the background:
#!/bin/sh
case "$(ps -o stat= -p $$)" in *+*) exec 3<&1 ;; *) exec 3>/dev/null ;;
esac
for i in $(seq 200); do echo $i
done >&3A Simple Way to Run Any Command in Background and Silently
Create this bash function:
bkg() { "$@" >/dev/null & }Then, anytime that you want to a noisy command, say seq 200, in background, type:
bkg seq 200The command will be run in background and its stdout will be disposed of.
To make the definition of bkg permanent, place the definition in ~/.bashrc.