関数の理解(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に収集されることを知っていますが、/root/collect.log日付と時刻を除いて詳細を理解することはできません。

ありがとう、台東

答え1

本質的には単にロギング機能です。

local cmd; cmd=$(basename $0)

これはcmdローカル変数として宣言されているため、この関数内でのみ使用できます。割り当てられた値は、$(basename $0)シェルまたはシェルスクリプトの名前に展開されます。

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";\ちょっと無意味ですね。 ;はシェルスクリプトの改行の末尾に不要なコマンド区切り文字で、\次の改行をエスケープします。お互いをキャンセルして削除できます。

答え2

長い話を短く

多くの切り取りと貼り付けによって構築されたように見えるロギング機能(おそらくsyslogに似た項目を作成しようとしました)。より詳細な分析および再構成を以下に提供する。

もともと公開されたコードは何をしますか?


注:ソースコードはGNU日付(BSD日付は機能しません)に依存し、Bashに依存する可能性が高いです。中断注意便利です。


以下は、他のソースから多くのコピーと貼り付けを行った人が作成したBourneベースのスクリプトです。重大度が「info」のログメッセージをログファイルに追加し、メッセージのコピーを標準エラーに追加しようとしているようです。これは正しく書かれておらず(「シェルチェックを文句にする」と定義されています)、いくつかの方法で改善することができます。公開されたソースコードは次のとおりです。

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の最新バージョンとdateユーティリティのGNUバージョンtr(たとえば、gdateGNUgtrユーティリティがインストールされている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
}

注:上記のリファクタリングスクリプトはBashismを明確に使用しますが、どのBashismも報告しません。しかし、shellcheckを介してきれいに実行されます。移植性が便利さを上回る場合は、Bash拡張機能やその他のシェル関連機能の代わりに標準の* nixユーティリティを使用するように書き直すことができますが、シェル移植性は元の質問とこの回答の範囲外です。


プログラムでは、デフォルト以外のlog_message重大度レベルを使用または使用せずに関数を呼び出すことができます。たとえば、コードがある場合example_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.

両方ログ収集そして標準エラー。

また、見ることができます

関連情報