我正在尝试在 Bash 中实现一个简单的日志服务器。它应该将一个文件作为参数并使用 netcat 在端口上提供它。
( tail -f $1 & ) | nc -l -p 9977
但问题是当 netcat 终止时,tail 仍然在运行。(澄清:如果我不分叉尾部进程,即使 netcat 终止,它将继续永远运行。)
如果我以某种方式知道尾巴的 PID,那么我可以在之后杀死它。
显然,使用 $! 将返回 netcat 的 PID。
如何获取尾部进程的 PID?
另一种选择:使用重定向到子shell。这改变了后台进程的启动顺序,所以 $! 给出tail
进程的PID。
tail -f $1 > >(nc -l -p 9977) &
wait $!
将 tail 的 PID 写入文件描述符 3,然后从那里捕获它。
( tail -f $1 & echo $! >&3 ) 3>pid | nc -l -p 9977
kill $(<pid)
这个怎么样:
jobs -x echo %1
%1
用于链中的第一个作业,%2
用于第二个作业等。jobs -x
用 PID 替换作业说明符。
这对我有用(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
。
也许您可以使用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
最后,我设法使用ps
. 感谢 ennukiller 的想法。
我已经使用ps
grep 从 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
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
一种方法是使用脚本 ppid 简单地执行 ps -ef 和 grep for tail
你有没有尝试过:
nc -l -p 9977 -c "tail -f $1"
(未经测试)
或者-e
如果您nc
没有-c
. 您可能必须有一个nc
使用该GAPING_SECURITY_HOLE
选项编译的。是的,您应该从该选项名称中推断出适当的警告。
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
不是一个理想的答案,但我找到了我工作的记录器守护程序的解决方法:
#!/bin/sh
tail -f /etc/service/rt4/log/main/current --pid=$$ | grep error
来自 $info 尾巴:
--pid=PID
with -f, terminate after process ID, PID dies
您可以使用该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()。
bobbogo 答案有效,但需要一个中间pid 文件。
您可以利用流程替换功能 >()
,它的工作原理类似于|
但无需等待tail
完成
tail -f $1 > >(nc -l -p 9977) & pid=!
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