我需要编写一个脚本,将该脚本已执行的次数写入文件。
我怎样才能做到这一点?
答案1
我假设您想要一个countfile
仅包含一个代表执行计数器的数字的文件。
$counter
你可以使用下列其中一行将该计数器读入到 shell 变量中:
read counter < countfile
counter=$(cat countfile)
可以使用语法在 Bash 中执行简单的整数加法$(( EXPRESSION ))
。然后只需将结果写回到我们的countfile
:
echo "$(( counter + 1 ))" > countfile
您可能还应该保护您的脚本以防尚不存在的情况countfile
,然后创建一个用值 1 初始化的脚本。
整个事情可能看起来像这样:
#!/bin/bash
if [[ -f countfile ]] ; then
read counter < countfile
else
counter=0
fi
echo "$(( counter + 1 ))" > countfile
答案2
只需让脚本创建一个日志文件,例如在脚本末尾添加一行:
echo "Script has been executed at $(date +\%Y-\%m-\%d) $(date +\%H-\%M-\%S)" >> ~/script.log
通过这种方式,您可以自己格式化显示日期和时间的方式,但如果您只想使用完整的日期和时间(并且是HH:MM:SS
您可以接受的格式),您也可以简单地使用:
echo "Script has been executed at $(date +\%F-\%T)" >> ~/script.log
然后你可以这样做:
wc -l ~/script.log
它计算换行符并估计日志文件中有多少行。在此之前,您可以在日志文件中看到它何时被执行。为了满足您的需要,您可以更改用于记录的路径和名称。我在这里做了一个将日志文件保存在 中的示例~
。
例如,您希望脚本将此计数添加到您在脚本末尾添加的行中,您可以在脚本开始时执行如下操作:
count=$(( $(wc -l ~/script.log | awk '{print $1}') + 1 ))
# the next line can be simply skipped if you not want an output to std_out
echo "Script execution number: $count"
并将脚本末尾的行更改为包含该信息的内容:
echo "Script has been executed $count times at $(date +\%F-\%T)" >> ~/script.log
答案3
此解决方案使用与字节指挥官的回答但它不依赖于 shell 算术或其他 Bashism。
exec 2>&3 2>/dev/null
read counter < counter.txt || counter=0
exec 3>&2 3>&-
expr "$counter" + 1 > counter.txt
流重定向
- 将标准错误流(2)复制到不同的文件描述符(3),
- 将其 (2) 替换为重定向到(如果计数器文件预计丢失,则
/dev/null
在命令输入的后续重定向中抑制错误消息),read
- 稍后将原始标准错误流(现在为 3)复制回原位(2),然后
- 关闭标准错误流的副本(3)。
答案4
不同的方法
单独的计数器文件有缺点:
- 每个计数器文件占用 4096 字节(或任何块大小)。
- 您必须在 bash 脚本中查找文件的名称,然后打开该文件以查看计数。
- 没有文件锁定(在其他答案中),因此两个人可能同时更新计数器(在 Byte Commander 的答案下的评论中称为竞争条件)。
所以这个答案取消了单独的计数器文件,并将计数放在 bash 脚本本身中!
- 将计数器放在 Bash 脚本本身中,您就可以在脚本本身中看到它运行了多少次。
- 使用
flock
保证在短时间内两个用户不可能同时运行该脚本。 - 因为计数器文件名不是硬编码的,所以您不需要更改不同脚本的代码,您可以简单地从存根/样板文件中获取它或复制并粘贴它。
代码
#!/bin/bash
# NAME: run-count.sh
# PATH: $HOME/bin
# DESC: Written for AU Q&A: https://askubuntu.com/questions/988032/how-can-i-cause-a-script-to-log-in-a-separate-file-the-number-of-times-it-has-be
# DATE: Mar 16, 2018.
# This script run count: 0
# ======== FROM HERE DOWN CAN GO INTO FILE INCLUDED WITH SOURCE COMMAND =======
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
# This is useful boilerplate code for shell scripts. Put it at the top of
# the shell script you want to lock and it'll automatically lock itself on
# the first run. If the env var $FLOCKER is not set to the shell script
# that is being run, then execute flock and grab an exclusive non-blocking
# lock (using the script itself as the lock file) before re-execing itself
# with the right arguments. It also sets the FLOCKER env var to the right
# value so it doesn't run again.
# Read this script with entries separated newline " " into array
mapfile -t ScriptArr < "$0"
# Build search string that cannot be named
SearchStr="This script"
SearchStr=$SearchStr" run count: "
# Find our search string in array and increment count
for i in ${!ScriptArr[@]}; do
if [[ ${ScriptArr[i]} = *"$SearchStr"* ]]; then
OldCnt=$( echo ${ScriptArr[i]} | cut -d':' -f2 )
NewCnt=$(( $OldCnt + 1 ))
ScriptArr[i]=$SearchStr$NewCnt
break
fi
done
# Rewrite our script to disk with new run count
# BONUS: Date of script after writing will be last run time
printf "%s\n" "${ScriptArr[@]}" > "$0"
# ========= FROM HERE UP CAN GO INTO FILE INCLUDED WITH SOURCE COMMAND ========
# Now we return you to your original programming....
exit 0
使用日志文件的另一种方法
与 Videonauth 的回答类似,我在这里写了一个日志文件答案:Bash 脚本用于维护访问文件的审计跟踪/日志记录每次使用根幂与gedit
或 时的情况nautilus
。
不过,问题在于,不是使用gksu
脚本来命名gsu
,而是调用pkexec
在 GUI 中使用 sudo 的“现代”方式,我是这么认为的。
另一个优点是它不仅会显示每次使用 root 权限的时间gedit
,还会记录编辑的文件名。以下是代码。
~/bin/gsu
:
#!/bin/bash
# Usage: gsu gedit file1 file2...
# -OR- gsu natuilus /dirname
# & is used to spawn process and get prompt back ASAP
# > /dev/null is used to send gtk warnings into dumpster
COMMAND="$1" # extract gedit or nautilus
pkexec "$COMMAND" "${@:2}"
log-file "${@:2}" gsu-log-file-for-"$COMMAND"
/usr/local/bin/log-file
:
#! /bin/bash
# NAME: log-file
# PATH: /usr/local/bin
# DESC: Update audit trail/log file with passed parameters.
# CALL: log-file FileName LogFileName
# DATE: Created Nov 18, 2016.
# NOTE: Primarily called from ~/bin/gsu
ABSOLUTE_NAME=$(realpath "$1")
TIME_STAMP=$(date +"%D - %T")
LOG_FILE="$2"
# Does log file need to be created?
if [ ! -f "$LOG_FILE" ]; then
touch "$LOG_FILE"
echo "__Date__ - __Time__ - ______File Name______" >> "$LOG_FILE"
# MM/DD/YY - hh:mm:ss - "a/b/c/FileName"
fi
echo "$TIME_STAMP" - '"'"$ABSOLUTE_NAME"'"' >> "$LOG_FILE"
exit 0
gsu-log-file-for-gedit
经过几次编辑后的日志文件内容:
__Date__ - __Time__ - ______File Name______
11/18/16 - 19:07:54 - "/etc/default/grub"
11/18/16 - 19:08:34 - "/home/rick/bin/gsu"
11/18/16 - 19:09:26 - "/home/rick/bin/gsu"