做一个脚本来观察文件内部
Do a script to watch inside a file
我目前正在编写一个脚本来监视文件中是否有任何添加的行。
我当前的脚本是:
#!/bin/bash
GRAY_R='3[1;30m'
RESET='3[0m'
refreshtime="0.5"
filetowatch="/root/upgrade.log"
while [ -n "" ]; do
case "" in
-r | --refresh)
isnumber='^[0-9]+([.][0-9]+)?$'
if ! [[ =~ $isnumber ]] ; then
echo "--refresh || -r need a number as argument (int or float)" >&2; exit 1
else
refreshtime=""
shift
fi;;
-h | --help)
echo "You can use -r or --refresh to set a refresh time (0.5s by default) or give a file as argument to watch this file (/root/upgrade.log by default)."
exit;;
*)
if [ -f "" ]; then
filetowatch=""
else
echo " is not a file, please use -h or --help to see help."
fi;;
esac
shift
done
tput smcup
while true
do
clear
cat ${filetowatch}
echo
echo -e "${GRAY_R}Press Q to quit - - - - - - - - - - - - - - - Refresh time ${refreshtime}s${RESET}"
read -t ${refreshtime} -N 1 input
if [[ $input = "q" ]] || [[ $input = "Q" ]]
then
echo
clear
break
fi
done
tput rmcup
这工作得很好,正如我想要的,但有 1 个问题。
当我观看一个很长的文件(可能超过 30 行)时,它的一部分会滚出视图,我无法使用滚动条来阅读它,因为脚本使用 clear
然后 cat
一次又一次,干扰滚动。
所以,我想知道如何做 tail -f
?
我想编写脚本,而不仅仅是使用 tail -f
来完成(我这样做是为了学习,否则我可以只使用 watch cat
而不是这个脚本)
对不起我的英语,谢谢你的帮助!
如果我理解正确,您正在尝试模拟命令tail -f
。
有一种快速而优雅的方法,使用 read
,如 所建议:
# parse command line and get filename and timeout
...
# watch the file until told to quit
clear
while true; do
# read line by line while there is something to read
while IFS='' read -r line; do
echo "$line"
done
# then wait for user input for the specified time
read -t ${refreshtime} -N 1 input
if [[ $input = "q" ]] || [[ $input = "Q" ]]; then
break
fi
# then loop back to trying to read the file. The script "remembers"
# where it left off in the file, because the file is opened by the
# outer while loop, which is still running
done <"${filetowatch}"
在您的原始脚本中,底部有一个提示(按 Q 退出...)。让我们在这里添加。我们将使用一个技巧,这样我们就不必每次都使用 clear
。只要您的提示字符串不超过一个终端行,这就会起作用:
...
# then wait for user input for the specified time
echo -ne "${GRAY_R}Press Q to quit - Refresh time ${refreshtime}s${RESET}" # -n means the cursor stays on this line
read -t ${refreshtime} -N 1 input
if [[ $input = "q" ]] || [[ $input = "Q" ]]; then
break
fi
echo -ne '\r3[K' # go back to the beginning of the line and erase it
# then loop back to trying to read the file
...
现在让我们添加一个边缘案例 - 如果您正在查看日志文件,它可能会不时被截断,因为日志文件通常会定期轮换。
read
解决方案将不起作用,因为它会继续尝试读取它停止的最后一个位置。
由于这个问题是出于学习目的,我假设您可以接受速度较慢且磁盘密集度更高的解决方案(并且不建议在生产中使用)。
执行此操作的一种方法是跟踪您在跟踪文件中已经看到的行数,并且仅打印新部分。当日志文件轮转时,您需要将“看到”的行数重置为零 - 您可能会丢失一些输出(在轮转之前添加到日志文件的行,但在您上次阅读文件)。
要打印文件的一部分,您可以使用 tail
本身 - 它可以选择从给定行 (-n +K
) 开始打印文件。
如果(出于学习目的)您根本不想使用 tail
,您也可以阅读每一行并打印您需要的内容(同样,速度较慢):
# parse command line and get filename and timeout
#...
# watch the file until told to quit
lines_seen=0
clear
while true; do
lines_total=$(wc -l "${filetowatch} | cut -f 1 -d ' '")
if ((lines_total < lines_seen)); then
# log file was truncated
lines_seen=0
fi
if ((lines_total > lines_seen)); then
for (( i=0; ; i++ )); do
# read the whole file
if IFS='' read -r line; then
# but only print newer lines
if ((lines_seen <= i)); then
echo "$line"
fi
else
break
fi
done <${filetowatch}
# update the number of lines printed (which may be different than lines_total meanwhile)
lines_seen=$i
fi
echo -ne "${GRAY_R}Press Q to quit - Refresh time ${refreshtime}s - ${lines_seen} lines${RESET}"
read -t ${refreshtime} -N 1 input
echo -ne '\r3[K'
if [[ $input = "q" ]] || [[ $input = "Q" ]]; then
break
fi
done
我目前正在编写一个脚本来监视文件中是否有任何添加的行。 我当前的脚本是:
#!/bin/bash
GRAY_R='3[1;30m'
RESET='3[0m'
refreshtime="0.5"
filetowatch="/root/upgrade.log"
while [ -n "" ]; do
case "" in
-r | --refresh)
isnumber='^[0-9]+([.][0-9]+)?$'
if ! [[ =~ $isnumber ]] ; then
echo "--refresh || -r need a number as argument (int or float)" >&2; exit 1
else
refreshtime=""
shift
fi;;
-h | --help)
echo "You can use -r or --refresh to set a refresh time (0.5s by default) or give a file as argument to watch this file (/root/upgrade.log by default)."
exit;;
*)
if [ -f "" ]; then
filetowatch=""
else
echo " is not a file, please use -h or --help to see help."
fi;;
esac
shift
done
tput smcup
while true
do
clear
cat ${filetowatch}
echo
echo -e "${GRAY_R}Press Q to quit - - - - - - - - - - - - - - - Refresh time ${refreshtime}s${RESET}"
read -t ${refreshtime} -N 1 input
if [[ $input = "q" ]] || [[ $input = "Q" ]]
then
echo
clear
break
fi
done
tput rmcup
这工作得很好,正如我想要的,但有 1 个问题。
当我观看一个很长的文件(可能超过 30 行)时,它的一部分会滚出视图,我无法使用滚动条来阅读它,因为脚本使用 clear
然后 cat
一次又一次,干扰滚动。
所以,我想知道如何做 tail -f
?
我想编写脚本,而不仅仅是使用 tail -f
来完成(我这样做是为了学习,否则我可以只使用 watch cat
而不是这个脚本)
对不起我的英语,谢谢你的帮助!
如果我理解正确,您正在尝试模拟命令tail -f
。
有一种快速而优雅的方法,使用 read
,如
# parse command line and get filename and timeout
...
# watch the file until told to quit
clear
while true; do
# read line by line while there is something to read
while IFS='' read -r line; do
echo "$line"
done
# then wait for user input for the specified time
read -t ${refreshtime} -N 1 input
if [[ $input = "q" ]] || [[ $input = "Q" ]]; then
break
fi
# then loop back to trying to read the file. The script "remembers"
# where it left off in the file, because the file is opened by the
# outer while loop, which is still running
done <"${filetowatch}"
在您的原始脚本中,底部有一个提示(按 Q 退出...)。让我们在这里添加。我们将使用一个技巧,这样我们就不必每次都使用 clear
。只要您的提示字符串不超过一个终端行,这就会起作用:
...
# then wait for user input for the specified time
echo -ne "${GRAY_R}Press Q to quit - Refresh time ${refreshtime}s${RESET}" # -n means the cursor stays on this line
read -t ${refreshtime} -N 1 input
if [[ $input = "q" ]] || [[ $input = "Q" ]]; then
break
fi
echo -ne '\r3[K' # go back to the beginning of the line and erase it
# then loop back to trying to read the file
...
现在让我们添加一个边缘案例 - 如果您正在查看日志文件,它可能会不时被截断,因为日志文件通常会定期轮换。
read
解决方案将不起作用,因为它会继续尝试读取它停止的最后一个位置。
由于这个问题是出于学习目的,我假设您可以接受速度较慢且磁盘密集度更高的解决方案(并且不建议在生产中使用)。
执行此操作的一种方法是跟踪您在跟踪文件中已经看到的行数,并且仅打印新部分。当日志文件轮转时,您需要将“看到”的行数重置为零 - 您可能会丢失一些输出(在轮转之前添加到日志文件的行,但在您上次阅读文件)。
要打印文件的一部分,您可以使用 tail
本身 - 它可以选择从给定行 (-n +K
) 开始打印文件。
如果(出于学习目的)您根本不想使用 tail
,您也可以阅读每一行并打印您需要的内容(同样,速度较慢):
# parse command line and get filename and timeout
#...
# watch the file until told to quit
lines_seen=0
clear
while true; do
lines_total=$(wc -l "${filetowatch} | cut -f 1 -d ' '")
if ((lines_total < lines_seen)); then
# log file was truncated
lines_seen=0
fi
if ((lines_total > lines_seen)); then
for (( i=0; ; i++ )); do
# read the whole file
if IFS='' read -r line; then
# but only print newer lines
if ((lines_seen <= i)); then
echo "$line"
fi
else
break
fi
done <${filetowatch}
# update the number of lines printed (which may be different than lines_total meanwhile)
lines_seen=$i
fi
echo -ne "${GRAY_R}Press Q to quit - Refresh time ${refreshtime}s - ${lines_seen} lines${RESET}"
read -t ${refreshtime} -N 1 input
echo -ne '\r3[K'
if [[ $input = "q" ]] || [[ $input = "Q" ]]; then
break
fi
done