如何使脚本执行一个单独的文件登录次数?


Answers:


15

我假设您想要一个countfile只包含一个代表执行计数器的数字的文件。

您可以将此计数器读取到shell变量中,$counter例如,使用以下几行之一:

  • read counter < countfile
  • counter=$(cat countfile)

简单的整数加法可以使用$(( EXPRESSION ))语法在Bash本身中完成。然后只需将结果写回我们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 $(( $(cat countfile 2>/dev/null || echo 0) + 1 )) > countfile
PerlDuck

1
很好,@ PerlDuck似乎也可以。我担心它可能会在读取文件之前打开文件以进行覆盖,但是显然,进程替换语法似乎阻止了这种情况。
字节指挥官

好点子。我不确定是否可以确保$(…)在执行其他任何操作之前(特别是在之前>)执行该命令。但是我经常使用该$(cat countfile 2>/dev/null || echo 0)部分来获得合理的默认值,以防文件不存在。为了sponge安全起见,我们可以添加:-)。
PerlDuck

2
通过将计数代码包装在flock命令中以防止出现竞争状况,可以改善此答案。参见unix.stackexchange.com/a/409276
rrauenza

5

只需让脚本创建一个日志文件,然后在脚本的末尾添加一行即可:

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

5

该解决方案使用与Byte Commander的答案相同的方法,但是它不依赖于Shell算术或其他Bashisms。

exec 2>&3 2>/dev/null
read counter < counter.txt || counter=0
exec 3>&2 3>&-
expr "$counter" + 1 > counter.txt

流重定向

  1. 将标准错误流(2)复制到其他文件描述符(3),
  2. 将其替换为(2)的重定向至/dev/null(以防止read在计数器文件预期丢失的情况下在命令输入的后续重定向中抑制错误消息),
  3. 稍后将原始标准错误流(现在为3)复制到位置(2),并
  4. 关闭标准错误流的副本(3)。

1

不同的方法

单独的计数器文件有以下缺点:

  • 每个计数器文件需要4096字节(或块大小)。
  • 您必须在bash脚本中查找文件的名称,然后打开文件以查看计数。
  • 没有文件锁定(在其他答案中),因此两个人有可能在同一时间更新计数器(在Byte Commander的答案下的注释中称为“竞争条件”)。

因此,此答案消除了一个单独的计数器文件,并将计数放入bash脚本本身!

  • 将计数器放在bash脚本本身中,使您可以在脚本本身中查看已运行了多少次。
  • 使用flock保证,在短时间内,两个用户不可能同时运行脚本。
  • 由于计数器文件名不是硬编码的,因此您无需更改其他脚本的代码,您可以简单地从存根/样板文件中获取或复制和粘贴它。

编码

#!/bin/bash

# NAME: run-count.sh
# PATH: $HOME/bin
# DESC: Written for AU Q&A: /ubuntu/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脚本,用于维护每次使用root gedit或root进行访问都被记录为日志的文件的审计跟踪/日志nautilus

尽管捕获而不是使用gksu脚本而不是命名gsu,但是它pkexec在GUI中调用了使用sudo的“现代”方式,所以有人告诉我。

另一个优点是,它不仅说每次使用根电源,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"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.