我看到 bash 脚本中使用了以下函数:
LOG_FILE="/root/collect.log";\
date_info() {
local cmd; cmd=$(basename $0)
echo "$(date --rfc-3339=seconds) $cmd.info: $*" >>"$LOG_FILE"
>&2 echo "$cmd.info: $*"
}
有人可以帮我理解这个功能吗?我知道所有内容都被收集在 log_file 中,该文件位于 path 中/root/collect.log
,但除了日期和时间之外,我无法理解其详细信息。
问候, 泰托
答案1
它本质上只是一个日志功能。
local cmd; cmd=$(basename $0)
这里cmd
被声明为局部变量,这意味着它只能在该函数内部使用。然后它被分配的值$(basename $0)
将扩展为 shell 或 shell 脚本的名称。
echo "$(date --rfc-3339=seconds) $cmd.info: $*" >>"$LOG_FILE"
在这里你回显日期rfc-3339 格式(请注意,这可能只是 GNU date 的一个可用选项,因此该命令可能不太可移植),如果您从中运行它,则cmd
附加的值是,然后.info:
script.sh
script.sh.info:
$*
表示提供给由 中的第一个字符扩展的函数的所有参数IFS
。
因此,如果您要date_info unable to do the thing
在您的日志文件中运行script.sh
,您会在日志文件中看到如下一行:
2022-04-20 06:35:00-06:00 script.sh.info: unable to do the thing
最后一行:
>&2 echo "$cmd.info: $*"
此行将 stdout 重定向到 stderr,然后将基本相同的内容(减去日期)回显到终端。
另外,;\
最后的部分LOG_FILE="/root/collect.log";\
有点无意义。 ;
是一个命令分隔符,在 shell 脚本中在换行符末尾是不必要的,然后它\
会转义后面的换行符...它们有点相互抵消并且可以删除。
答案2
长话短说
它是一个日志记录功能(可能试图创建类似系统日志的条目),看起来像是通过大量剪切和粘贴构建的。下面提供了更深入的分析和重构。
最初发布的代码是做什么的
注意:原始代码依赖于 GNU 日期(BSD 日期不起作用),并且很可能依赖于 Bash,尽管根据制止主义公用事业。
以下是一个基于 Bourne 的脚本,由从其他来源进行了大量复制和粘贴的人编写。它似乎打算将严重性“info”的日志消息附加到日志文件,并将消息的副本附加到标准错误。它写得不好(定义为“它让 shellcheck 抱怨”),并且可以通过多种方式进行改进。发布的原始代码是:
LOG_FILE="/root/collect.log";\
date_info() {
local cmd; cmd=$(basename "$0)
echo "$(date --rfc-3339=seconds) $cmd.info: $*" >>"$LOG_FILE"
>&2 echo "$cmd.info: $*"
}
由于全大写的变量通常意味着导出环境变量,并且您似乎并没有刻意避免 bashism,因此可以使用更标准的日志记录格式、一些内置的 Bash 扩展以及通常更清晰的语义来重写代码意图使用任何最新版本的 Bash 和 GNU 版本的date
和tr
实用程序,例如gdate
在gtr
安装了 GNU 实用程序的 Darwin 系统上。
重构的原始代码
基于对代码及其意图的许多假设 - 您将必须验证其中一些内容,因为它们不能仅通过查看原始来源来收集 - 这就是我认为更灵活和可读的重构,尽管您可能需要根据您的特定用例调整格式和严重性级别。
#!/usr/bin/env bash
# Set a default logfile if one isn't already
# present in the environment.
: "${LOG_FILE:=/root/collect.log}"
export LOG_FILE
# Set the default log level to INFO if the
# environment variable isn't already set.
: "${DEFAULT_LOG_LEVEL:=INFO}"
export DEFAULT_LOG_LEVEL
# Log a message to a file and to standared error.
#
# NB: You may want to change the possible values
# for log levels (a.k.a. severity) based on your
# target logging system. For example, syslog
# also accepts `CRIT`, `ALERT`, and `EMERG`.
log_message () {
local date filename log_level log_msg
filename=$(basename "${BASH_SOURCE[0]}" ".sh")
date=$(date --rfc-3339=seconds | tr -d '\r\n')
log_level="${1:-$DEFAULT_LOG_LEVEL}"
log_level="${log_level^^}"
# Remove the first positional parameter if it's a
# defined log severity level.
[[ "${1@U}" =~ DEBUG|INFO|WARN|ERROR|FATAL ]] && shift
log_msg="[${date}] $log_level -- ${filename}: $*"
echo "$log_msg" >> "$LOG_FILE"
echo "$log_msg" > /dev/stderr
}
注意:上面的重构脚本没有报告任何 Bashisms,尽管它显然使用了它们。然而,它通过 shellcheck 运行干净。如果可移植性胜过便利性,您可以重写它以使用标准 *nix 实用程序,而不是 Bash 扩展或其他特定于 shell 的功能,但 shell 可移植性超出了原始问题和此答案的范围。
在程序中,您可以调用log_message
具有或不具有非默认严重性级别的函数。例如,如果代码位于示例_app.sh然后调用:
log_message WARN "This is just an example."
脚本内会记录:
[2022-04-20 11:10:01-04:00] WARN -- example_app: This is just an example.
二者皆是收集日志和标准误。