cronジョブで呼び出されるスクリプトにシェルエラーを記録するにはどうすればよいですか?

cronジョブで呼び出されるスクリプトにシェルエラーを記録するにはどうすればよいですか?

今朝、次のメッセージが見つかりました。

編集する:メッセージの件名も追加して、そのメッセージがどこから来たのかを明確に知ることができました。

Return-Path: <root@REDACTED>
Received: from localhost (localhost [127.0.0.1])
  (uid 0)
  by REDACTED with local
  id 00000000005DC0DF.00000000633BA87E.000042C7; Tue, 04 Oct 2022 05:29:02 +0200
From: CronDaemon <root@REDACTED>
To: admlog@REDACTED
Subject: Cron <root@north> test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin>
X-Cron-Env: <MAILTO=REDACTED>
X-Cron-Env: <HOME=/root>
X-Cron-Env: <LOGNAME=root>
Message-ID: <courier.00000000633BA87E.000042C7@REDACTED>
Date: Tue, 04 Oct 2022 05:29:02 +0200
X-Mime-Autoconverted: from 8bit to 7bit by courier 1.1

/etc/cron.daily/syslogrotate:
parse error: Invalid numeric literal at line 2, column 0
parse error: Invalid numeric literal at line 2, column 0

syslogrotate回転したファイルを表示し、必要に応じて他のスクリプトを呼び出すためにシェルスクリプトを呼び出すように修正しました。 Cron は、stderr で作成されたすべてのエントリを最初の最初のプロセスに帰属します。実際のスクリプトファイルや行番号まで知っておくといいようです。これを行うためのツールはありませんか?

答え1

syslogによって呼び出されたスクリプトが呼び出す各サブタスクの終了コードを確認し、サブタスクがゼロ以外のコードで終了すると、スクリプトはスクリプトのパスと名前を示すメッセージをstderrに書き込みます。たとえば、/path/to/the/sub_task.sh returned exit code 2。メッセージはcronの電子メールに含まれ、必要なスクリプト名を受け取ります。

答え2

いいえ、そのようなツールはありません。

しかし、bashエラーを見つけるためのツールがあります。

#!/bin/bash -v

スクリプトは、実行する前に各コマンドを印刷します。これにより、正確にエラーが発生した場所が表示されます。

または使用できる-xキーですbash。これにより、スクリプトの実行に関する追加情報が提供されます。

エラーをスクリプトの小さな部分に限定し、内容全体を印刷したくない場合は、次のようにしますset -xv

#!/bin/bash
some-good-commands
set -xv  # debug mode on
some-suspicious-code
set +xv  # debug mode off
some-good-commands

スクリプトを手動で実行しても問題の原因が見つからない場合は、cronジョブで直接使用できます。

もちろん、いつでもデバッグを追加できますecho

#!/bin/bash
echo "Starting script A"
VAR=$1
echo "Executing `abc $VAR`"
abc $VAR
echo "abc ended with $?"

答え3

Sotto Voceは良いアイデアを持っています。戻りコードをテストする代わりに、書かれた内容があるかどうかをテストできます。これにより、問題が発生した場合に備えてcronスクリプトを計測していくつかのコンテキストを作成できます。

Bashで出力をテストする方法がわからなかったので、次の簡単なCユーティリティを作成しましたany_output

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    long cur_out = lseek(1, 0, SEEK_END);
    if (cur_out < 0) cur_out = 0;
    long cur_err = lseek(2, 0, SEEK_END);
    if (cur_err < 0) cur_err = 0;
    if (argc > 1)
    {
        char *slash, *end;
        long last_out = strtol(argv[1], &slash, 10);
        if (*slash == '/')
        {
            long last_err = strtol(slash + 1, &end, 10);
            if (*end != 0 && *end != '\n')
                last_err = -1;
            else
            {
                int rtc = cur_err > last_err || cur_out > last_out;
                if (rtc)
                    fprintf(stderr, "%s, %ld > %ld || %ld > %ld\n",
                        argv[1], cur_err, last_err, cur_out, last_out);
                return rtc;
            }
        }
        else
        {
            fputs("argument to any_output is its previous output\n", stderr);
            return 0;
        }
    }
    else
    {
        printf("%ld/%ld\n", cur_out, cur_err);
        return 0;
    }
}

次に、テストスクリプトを作成しました。jqこれが私が得るエラーであるため呼び出されます。

#! /bin/bash

output=$(/home/ale/tmp/any_output)
if [ -f /home/ale/tmp/test.data ]; then
    if [[ "$(jq .j < /home/ale/tmp/test.data)" != "1" ]]; then
        touch /home/ale/tmp/test.data
    fi
fi
/home/ale/tmp/any_output $output || echo ${BASH_SOURCE[*]}

パスは現在のディレクトリのパスです。 test.dataが含まれているので、{"j":1}スクリプトはほとんど機能しません。 2分ごとにテストスクリプトを呼び出すようにcrontabを設定しました。 test.dataに書き込むまでしばらく何も起こりませんhello。次の実行では、cronが私に次の電子メールを送信しました。

From: CronDaemon <REDACTED>
To: REDACTED
Subject: Cron <ale@pcale> /home/ale/tmp/test.sh
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin>
X-Cron-Env: <MAILTO=REDACTED>
X-Cron-Env: <HOME=/home/ale>
X-Cron-Env: <LOGNAME=ale>
Date: Thu, 06 Oct 2022 14:18:01 +0200
X-Mime-Autoconverted: from 8bit to 7bit by courier 1.0

parse error: Invalid numeric literal at line 2, column 0
0/0, 57 > 0 || 57 > 0
/home/ale/tmp/test.sh

テキストの2行目の無効な文字は、機器自体をデバッグするために使用されます。 falseでもlseek(1, 0, SEEK_END)-1が返されるため、一部のfalse positiveが発生しますisatty(1)。だから上記の呼び出しを削除しisattyてコードを設定しました。

関連情報