오늘 아침에 다음과 같은 메시지를 발견했습니다.
편집하다: 메시지 제목도 추가하여 해당 메시지가 어디에서 왔는지 명확하게 알 수 있도록 했습니다.
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에 의해 호출된 스크립트가 호출하는 각 하위 작업의 종료 코드를 확인하도록 하고, 하위 작업이 0이 아닌 코드로 종료되면 스크립트는 스크립트의 경로와 이름을 나타내는 메시지를 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
텍스트의 두 번째 줄에 있는 잘못된 문자는 계측기 자체를 디버깅하는 데 사용됩니다. false라도 lseek(1, 0, SEEK_END)
-1이 반환되기 때문에 일부 false positive가 발생합니다 isatty(1)
. 그래서 위의 호출을 제거 isatty
하고 코드를 설정했습니다.