66

我正在尝试在 Bash 中实现一个简单的日志服务器。它应该将一个文件作为参数并使用 netcat 在端口上提供它。

( tail -f $1 & ) | nc -l -p 9977

但问题是当 netcat 终止时,tail 仍然在运行。(澄清:如果我不分叉尾部进程,即使 netcat 终止,它将继续永远运行。)

如果我以某种方式知道尾巴的 PID,那么我可以在之后杀死它。
显然,使用 $! 将返回 netcat 的 PID。

如何获取尾部进程的 PID?

4

14 回答 14

57

另一种选择:使用重定向到子shell。这改变了后台进程的启动顺序,所以 $! 给出tail进程的PID。

tail -f $1 > >(nc -l -p 9977) &
wait $!
于 2011-11-08T09:47:49.767 回答
50

将 tail 的 PID 写入文件描述符 3,然后从那里捕获它。

( tail -f $1 & echo $! >&3 ) 3>pid | nc -l -p 9977
kill $(<pid)
于 2010-09-24T12:15:38.720 回答
20

这个怎么样:

jobs -x echo %1

%1用于链中的第一个作业,%2用于第二个作业等。jobs -x用 PID 替换作业说明符。

于 2012-01-18T09:38:33.440 回答
13

这对我有用(SLES Linux):

tail -F xxxx | tee -a yyyy &
export TAIL_PID=`jobs -p`
# export TEE_PID="$!"

如果用户可以在同一台机器上为两个“实例”运行脚本,则该线程中提到的ps|grep|kill技巧将不起作用。

jobs -x echo %1对我不起作用(手册页没有-x标志)但给了我尝试的想法jobs -p

于 2012-08-20T16:19:54.087 回答
7

也许您可以使用fifo,以便您可以捕获第一个进程的 pid,例如:

FIFO=my_fifo

rm -f $FIFO
mkfifo $FIFO

tail -f $1 > $FIFO &
TAIL_PID=$!

cat $FIFO | nc -l -p 9977

kill $TAIL_PID

rm -f $FIFO
于 2009-10-30T22:59:15.040 回答
2

最后,我设法使用ps. 感谢 ennukiller 的想法。

我已经使用psgrep 从 args 中提取 tail 并杀死它。这是一种黑客攻击,但它确实有效。:)

如果你能找到更好的方法,请分享。

这是完整的脚本:(
最新版本可以在这里找到:http: //docs.karamatli.com/dotfiles/bin/logserver

if [ -z "$1" ]; then
    echo Usage: $0 LOGFILE [PORT]
    exit -1
fi
if [ -n "$2" ]; then
    PORT=$2
else
    PORT=9977
fi

TAIL_CMD="tail -f $1"

function kill_tail {
    # find and kill the tail process that is detached from the current process
    TAIL_PID=$(/bin/ps -eo pid,args | grep "$TAIL_CMD" | grep -v grep | awk '{ print $1 }')
    kill $TAIL_PID
}
trap "kill_tail; exit 0" SIGINT SIGTERM

while true; do
    ( $TAIL_CMD & ) | nc -l -p $PORT -vvv
    kill_tail
done
于 2009-10-31T12:08:25.690 回答
2

ncat退出时自动终止tail -f(在 Mac OS X 10.6.7 上)!

# simple log server in Bash using ncat
# cf. http://nmap.org/ncat/
touch file.log
ncat -l 9977 -c "tail -f file.log" </dev/null   # terminal window 1
ncat localhost 9977 </dev/null                  # terminal window 2
echo hello > file.log                           # terminal window 3
于 2011-07-07T08:59:10.297 回答
1

一种方法是使用脚本 ppid 简单地执行 ps -ef 和 grep for tail

于 2009-10-30T22:49:16.140 回答
1

你有没有尝试过:

nc -l -p 9977 -c "tail -f $1"

(未经测试)

或者-e如果您nc没有-c. 您可能必须有一个nc使用该GAPING_SECURITY_HOLE选项编译的。是的,您应该从该选项名称中推断出适当的警告。

于 2009-10-30T23:50:38.833 回答
1

tail您可以仅使用 Bash I/O 重定向将命令的 pid 存储在变量中(请参阅如何在管道中获取进程的 PID)。

# terminal window 1
# using nc on Mac OS X (FreeBSD nc)
: > /tmp/foo
PID=$( { { tail -f /tmp/foo 0<&4 & echo $! >&3 ; } 4<&0 | { nc -l 9977 ;} & } 3>&1 | head -1 )
kill $PID

# terminal window 2
nc localhost 9977

# terminal window 3
echo line > /tmp/foo
于 2011-09-18T17:44:14.897 回答
1

不是一个理想的答案,但我找到了我工作的记录器守护程序的解决方法:

#!/bin/sh
tail -f /etc/service/rt4/log/main/current --pid=$$ | grep error

来自 $info 尾巴:

--pid=PID
          with -f, terminate after process ID, PID dies
于 2012-07-24T01:48:41.520 回答
1

您可以使用该coproc命令两次。

给定的示例转换为:

coproc TAIL { tail -f $1; }; exec {TAIL[1]}<&-
coproc NC { nc -v -l -p 9977; } <&"${TAIL[0]}" >&1
wait $NC_PID; echo "nc exit code: $!"
kill $TAIL_PID; echo "done"

(我已经扔了-v一对夫妇echo在那里进行故障排除。)

使用coproc感觉很像在其他各种脚本语言中使用 Popen()。

于 2018-11-13T17:29:12.790 回答
1

bobbogo 答案有效,但需要一个中间pid 文件

您可以利用流程替换功能 >(),它的工作原理类似于|但无需等待tail完成

tail -f $1 > >(nc -l -p 9977) & pid=!
于 2020-05-13T11:53:19.023 回答
0

tail 的 --pid 选项是你最好的朋友。它将允许您完全控制在后台运行的管道。阅读 tail 命令选项以获得更大的弹性,以防您的文件被另一个进程主动轮换,这可能会让您拖尾一个非活动的 inode。下面的示例虽然不用于处理数据,但演示了对尾部的“强加”限制以及告诉它随时退出的能力。这用于测量 httpd 上的服务压力。

  # Set the tail to die in 100 second even if we die unexpectedlly.
sleep 100 & ;  ctlpid=$!
tail -q -n 0 --follow=name --retry --max-unchanged-stats=1 --pid=$ctlpid -f  /var/log/httpd/access_log 2>/dev/null | wc –l > /tmp/thisSampleRate &
…. Do some other work
….  Can kill the pipe at any time by killing $ctlpid 
…. Calculate preassure if /tmp/thisSampleRate is ready
于 2015-01-09T13:29:32.653 回答