我正在尝试编写一个(sh -bourne shell)脚本,该脚本在将行写入文件时对其进行处理。我试图通过将tail -f
的输出提供给while read
循环来实现此目的。根据我在谷歌的研究以及this question处理类似问题,但使用bash,这种策略似乎是正确的。
从我读过的内容来看,当被跟踪的文件不再存在时,我似乎应该能够摆脱循环。它没有。事实上,似乎我可以摆脱这种情况的唯一方法是在另一个会话中杀死进程。 tail
似乎确实工作正常,否则用这个测试:
touch file tail -f file | while read line do echo $line done
我在另一个会话中附加到file
的数据只显示在上面写的循环处理文件中。
这是在HP-UX B.11.23版上。
感谢您提供的任何帮助/见解!
答案 0 :(得分:1)
如果你想突破,当你的文件不再存在时,就这样做:
test -f file || break
将它放在你的循环中,应该会爆发。
剩下的问题是,如何打破读取线,因为这是阻塞。
这可以通过应用超时来完成,例如read -t 5行。然后每5秒读取一次,如果文件不再存在,循环将中断。注意:创建一个它可以处理大小写的循环,读取超时,但文件仍然存在。
编辑:似乎超时读取返回false,因此您可以将测试与超时结合起来,结果将是:
tail -f test.file | while read -t 3 line || test -f test.file; do
some stuff with $line
done
答案 1 :(得分:0)
我不知道HP-UX tail
但GNU tail
有--follow=name
选项,它将按名称跟踪文件(通过每隔几秒重新打开文件从相同的文件描述符读取,不会检测文件是否已取消链接),并在用于打开文件的文件名取消链接时退出:
tail --follow=name test.txt
答案 2 :(得分:0)
除非您使用GNU tail ,否则在跟踪文件时无法自行终止。 -f选项实际上仅用于交互式监视 - 实际上,我有一本书说-f“不太可能在shell脚本中使用”。
但是对于问题的解决方案,我并不完全确定这不是一种过度设计的方法,但我想你可以将 tail 发送到FIFO,然后有一个函数或脚本检查文件是否存在,并且如果它已被取消链接则终止它。
#!/bin/sh sentinel () { while true do if [ ! -e $1 ] then kill $2 rm /tmp/$1 break fi done } touch $1 mkfifo /tmp/$1 tail -f $1 >/tmp/$1 & sentinel $1 $! & cat /tmp/$1 | while read line do echo $line done
做了一些天真的测试,似乎可以正常工作,并且不会留下任何垃圾。
答案 3 :(得分:0)
我从来没有对这个答案感到满意,但我还没有找到替代方案:
kill $(ps -o pid,cmd --no-headers --ppid $$ | grep tail | awk '{print $1}')
获取当前进程的子进程,查找尾部,打印出第一列(尾部的pid)并将其终止。罪恶 - 确实很难看,就像生活一样。
答案 4 :(得分:0)
以下方法介绍tail -f file
命令,将其进程ID和自定义字符串前缀(此处为 tailpid:
)回显到while
循环,其中带有自定义字符串前缀的行触发另一个(后台)while
循环,每隔5秒检查file
是否仍然存在。如果没有,tail -f file
被杀死,包含背景while
循环的子shell退出。
# cf. "The Heirloom Bourne Shell",
# http://heirloom.sourceforge.net/sh.html,
# http://sourceforge.net/projects/heirloom/files/heirloom-sh/ and
# http://freecode.com/projects/bournesh
/usr/local/bin/bournesh -c '
touch file
(tail -f file & echo "tailpid: ${!}" ) | while IFS="" read -r line
do
case "$line" in
tailpid:*) while sleep 5; do
#echo hello;
if [ ! -f file ]; then
IFS=" "; set -- ${line}
kill -HUP "$2"
exit
fi
done &
continue ;;
esac
echo "$line"
done
echo exiting ...
'