了解函数 (Bash)

了解函数 (Bash)

我看到 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.shscript.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 版本的datetr实用程序,例如gdategtr安装了 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.

二者皆是收集日志和标准误。

也可以看看

相关内容