仅当进程在后台运行时才隐藏其标准输出

仅当进程在后台运行时才隐藏其标准输出

当进程在后台运行时,如何隐藏其标准输出?

例如,

测试文件

#!/bin/bash

for i in $(seq 200); do
   echo $i
done

现在,我运行脚本。我停止它。我把它放回去测试它是否继续运行。然后我把它放在后台,但它一直在标准输出中打印,我无法继续工作。有没有办法仅在后台时重定向或隐藏进程输出?

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:~$ 

答案1

重定向正在运行的进程的输出是不容易。但如果你从一开始就努力,可能会有其他选择:

some_long_runnning_command | tail -n 1000 & pkill -STOP tail

使用此方法,由于tailSIGSTOP发送给它,因此它不会输出任何内容,直到您使用 (或类似方法)明确将其带到前台fg,并且直到脚本完成。调整1000以获得所需的行数。

更好的选择可能是grep

some_long_runnning_command | grep . --color=never & pkill -STOP sed

您可以根据需要发出信号SIGCONT来启动和SIGSTOP停止grep输出。

这可能可以与上面链接的 SO 问题中打开文件描述符的重新连接相结合(也许使用脚本这个答案),以便可以通过已经打开的流程来完成此操作。


如果缓冲确实是问题grep,因为沃尔克·西格尔指出,那么可以考虑使用更复杂的命令集。首先,请注意sort用途临时文件,因此缓冲对它来说应该不是问题。它将像tail和的组合一样工作grep- 等待命令完成,如tail,并保留整个输出,如grep。类似于:

some_long_runnning_command | awk '{$0=";"$0}1' | sort -k1.1,1.1 --stable | sed 's/^;//' & pkill -STOP sed

这里的损失是您不能随意启动和停止输出,并且在无用的 上耗费了大量的计算sort,以及在添加和删除 上耗费的工作;


进一步的测试表明Volker的说法确实正确:

对于类似这样的脚本(称之为test.sh):

#! /bin/bash
for i in $(seq 1000000); do echo $i; echo $i > /tmp/log; done

和命令:

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

中的数字/tmp/log停留在 12768( 中的数字类似。使用命令

./test.sh | awk '{$0=";"$0}1' | sort -k1.1,1.1 --stable | sed 's/^;//' & pkill -STOP sed

脚本在 没有 任何 声音 的 情况 下运行/tmp/log完成.1000000sed


我还没有设法将它与 GDB 方法集成,但它可以作为一个函数运行,类似于John1024的。问题是你没有很好的方法来知道进程是否已经结束,因为 shell 只在整个作业结束时才通知你。所以你必须使用检查函数:

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
}

您可以将 包装"$@"在另一个函数中,一旦作业完成,该函数将通知您,使用类似notify-send或 之类的方法write

这是一次很好的练习,但最终最简单的方法是重定向/dev/null并忘记输出:如何将后台应用程序的输出重定向到/dev/null

答案2

/dev/null仅当脚本在后台运行时,此脚本才会将标准输出重定向到:

#!/bin/sh
case "$(ps -o stat= -p $$)" in
   *+*) : ;;
   *)  exec 1>/dev/null ;;
esac
for i in $(seq 200); do
   echo $i
done

这里的关键是,当作业处于前台时,该字段中ps stat有一个。如果没有,则表示作业处于后台。++

如果您希望某些任务保留 stdout,而其他任务不保留,我们可以为任意输出创建文件描述符 3,并保持 stdout 处于活动状态以进行其他输出。下面的脚本实现了这一点。它将循环输出发送到文件描述符 3。如果作业在前台运行,则文件描述符 3 是 stdout,如果作业/dev/null在后台运行,则文件描述符 3 是 stdout:

#!/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

在后台静默运行任何命令的简单方法

创建此 bash 函数:

bkg() { "$@" >/dev/null & }

然后,任何时候您想要在后台运行嘈杂命令,例如seq 200,输入:

bkg seq 200

该命令将在后台运行,并且其标准输出将被处理。

为了使 的定义bkg永久化,请将定义放在 中~/.bashrc

相关内容