我为我大学的一个实验室编写了下面的 shell 脚本。它必须查看从另一个进程频繁升级的日志文件,并在调用时创建多个副本。以下是代码(日志旋转命令):
#!/bin/bash
# Usage:
# logrotate.sh [-n int] [-s signal] logfile
# where:
# int is an optional integer used to make int number of copies of logfile
# signal is the name of signal which shell command fuser must send to the process managing logfile
# this script lacks of a strong parameters checking
NCOPIES=4
LOGSIGNAL=USR1
#use of getopts to parse the arguments
while getopts "n:s:" OPTION ; do
case $OPTION in
n) NCOPIES="$OPTARG"
;;
s) LOGSIGNAL="$OPTARG"
;;
?) printf "Usage: %s [-n copies to keep] [-s signal to send] filename\n" $(basename $0) >&2
exit 1
;;
esac
done
#shift to read the last parameter (logfile)
shift $(($OPTIND - 1))
LOGFILE=$1
#create logfile.2 logfile.3 ... logfile.NCOPIES
for i in `seq $NCOPIES -1 1` ; do
test -f $LOGFILE.$i && mv $LOGFILE.$i $LOGFILE.$[ $i + 1 ]
done
mv $LOGFILE $LOGFILE.1
#sending signal to process which is writing to logfile to keep on writing to $LOGFILE(original name, without any extensions)
fuser -k -"$LOGSIGNAL" $LOGFILE.1
所以我写了两个脚本,每秒写入文件日志:
-C 程序(日志测试.c):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("log", O_WRONLY | O_APPEND);
if(fd < 0 ){
printf("Impossible to open file %s.\n", "log");
return -1;
}
for(;;){
if(write(fd, "Ciao bello mio\n", 15) != 15){
write(2, "Error in writing data.\n", 23);
}
sleep(1);
}
close(fd);
exit(0);
}
- 和 shell 脚本(日志测试工具):
#! /bin/bash
while true
do
echo $(date) >> log
sleep 1
done
当我启动时
./logtest.sh &
./logrotate.sh 日志
剧本日志旋转命令移动所有具有正确名称的文件(日志变成日志1)并将信号发送给拥有该文件的进程日志那一刻(所以 shell 脚本日志测试工具)然后继续在文件上写入日志。此外,似乎我用定影器发送的信号没有区别:它总是以相同的方式做出反应。
但是,如果我启动
./logtest &
./logrotate.sh 日志
碰巧的是 C 程序日志测试接收来自命令的信号定影器然后终止。
我的问题是:为什么这两个日志程序对 fuser 发送的信号有不同的反应?我的意思是,为什么 schell 脚本继续工作,而 C 程序终止了?
在 fuser 的手册页中的 RESTRICTIONS 部分中,它说
-k 选项仅适用于进程。
难道 shell 脚本不被视为 shell 中的真正进程?这对我来说是新鲜事……我在互联网上搜索过,但没有找到关于定影器深入信号部分。
答案1
您的脚本logtest.sh
仅写入log
并立即关闭文件描述符。因此,当您调用时,fuser
没有log.1
进程具有此文件的活动文件描述符。
您可以通过while
在list
(while true; do echo $(date); sleep 1; done) >> log
并且无论你发送哪个,logtest.sh
和都将终止,因为你不处理信号。这可以通过(查看)来实现。但我不知道如何在 C 中做到这一点(从未学过 C)。logtest.c
SIGNAL
bash
trap '<COMMAND>' USR1
man bash-builtins
答案2
问题是,它只fuser
对当前使用内核中为其打开了文件描述符的文件的进程起作用。
虽然这对于您的C
程序来说是正确的,但对于您的脚本来说却不正确bash
:
echo $(date) >> log
只需打开文件,向其中添加内容并立即关闭即可。因此,在检查stdout
时,内核永远不会认为该文件已打开。fuser
一个简单的解决方案是更改您的bash
脚本,以便文件保持打开直到while
循环结束:
#! /bin/bash
while true
do
echo $(date) >> log
sleep 1
done < log
这样,log
在循环开始时就会创建一个文件描述符while
,并且它会保持打开直到while
循环结束。