2

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
done

Now, 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:~$ 
  • If it's possible, I don't think it will be easy: stackoverflow.com/q/593724/2072269 – muru Sep 17 '14 at 1:46
  • thanks muru for helping. it's not so easy I see! I run away from that post – whitenoisedb Sep 17 '14 at 1:53
  • I'll have to decide to redirect output to /dev/null from the start. – whitenoisedb Sep 17 '14 at 2:02
2

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 tail

With 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 sed

You 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 sed

The 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; done

and the command:

./test.sh | grep . --color=never & pkill -STOP grep

the 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 sed

The 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.

  • 1
    I would think that, when grep . is stopped, the command will fill the buffer of the pipe and will be stopped too? – Volker Siegel Sep 17 '14 at 3:02
  • @VolkerSiegel perhaps. But for the small outputs of my test script (for i in {1..1000}; do echo $i; sleep 0.1s; done) the command finished and only grep was waiting around. – muru Sep 17 '14 at 3:05
  • Just tested with seq 1000000 | grep . & pkill -STOP grep, seq got stopped at 998. – Volker Siegel Sep 17 '14 at 3:06
  • 1
    @VolkerSiegel although they are 166 minutes but sleep is set to 0.01s, that's why he could :D – whitenoisedb Sep 17 '14 at 13:35
  • 1
    @VolkerSiegel you were indeed right about the buffer problem. – muru Sep 19 '14 at 0:05
1

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
done

The 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 >&3

A 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 200

The 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.

  • clever way too! considering that way, it would only work with my scripts I suppose. actually, it's a pity that there's no included command on bash to do that with any process. – whitenoisedb Sep 17 '14 at 13:40
  • @redraw I updated the answer with a simple function that saves typing and handles putting a command, any command, in background and silencing its stdout. – John1024 Sep 17 '14 at 20:01
  • John, do you mean a bash script? I'm not sure I can define a bash function for shell usage directly, or yes? by the way, it would be better to send the pid as argument, considering it is already running – whitenoisedb Sep 17 '14 at 20:29
  • 1
    Yes, bash functions are good for shell usage directly. In a comment, you wrote "I'll have to decide to redirect output to /dev/null from the start." This shell function was intended to make that easy to do. – John1024 Sep 17 '14 at 20:41
  • Well, you're right. I was quiet disappointed when I said that, but that's a quick one to do that! – whitenoisedb Sep 17 '14 at 20:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.