Bash 함수가 오류 출력을 생성하는 이유는 무엇입니까?

Bash 함수가 오류 출력을 생성하는 이유는 무엇입니까?

cron을 사용하여 주기적으로 실행되는 스크립트가 있습니다. 이 스크립트가 실패할 경우 이메일 알림을 받고 싶습니다. 실행되고 출력이 생성될 때마다 알림을 받고 싶지 않습니다.

그래서 스크립트를 사용하고 있어요크로니크cron 내에서 작업을 실행한다는 것은 출력이 아닌 오류 출력만 보내는 것을 의미합니다.

그러나 스크립트에는 디렉토리가 비어 있는지 감지하는 bash 함수가 있습니다.

# function to check if directory is empty outputs 0 if empty, or
# some positive integer if it does not
is_dir_empty () 
{
  ls -A "$1" | wc -c
}

이 함수는 아래와 같이 if 문에서 사용됩니다.

if [ "$(is_dir_empty "${local_backup_location}/${folder_to_backup_basename}")" -eq 0 ]; then
    # save space by removing diffs older than 1 month
    rdiff-backup --remove-older-than 1M --force ${local_backup_location}/${folder_to_backup_basename} || logger -t ${logger_name}  "No existing data backup"
fi

가끔 cronic으로부터 이런 이메일을 받습니다.

Cronic detected failure or error output for the command:
/usr/local/sbin/backup_uploads

RESULT CODE: 0

ERROR OUTPUT:
ls -A /mnt/storage-2/data_upload_backup/upload

STANDARD OUTPUT:
<snip>

TRACE-ERROR OUTPUT:
+ folder_to_backup=/mnt/storage-1/sftp_data/sftpuser1/upload
+ backup_server=ed-mh-x86001.mydomain.tld
+ backup_folder=data_upload_backup
+ remote_backup_storage_root=/mnt/storage-2
+ local_backup_storage_root=/mnt/storage-2
+ logger_name=backup_uploads
+ error_report_dir=/root/rdiff-backup-errors
+ [email protected]
+ problems=0
+ lockfilename=/root/.backup_uploads_lockfile.pid
+ test -e /root/.backup_uploads_lockfile.pid
+ echo
+ basename /mnt/storage-1/sftp_data/sftpuser1/upload
+ folder_to_backup_basename=upload
+ hostname
+ remote_server_backup_folder=/mnt/storage-2/data_upload_backup_ed-mh-pi01
+ mkdir -p /root/rdiff-backup-errors
+ logger -t backup_uploads Starting backup for folder /mnt/storage-1/sftp_data/sftpuser1/upload
+ test -d /mnt/storage-2
+ local_backup_location=/mnt/storage-2/data_upload_backup
+ mkdir -p /mnt/storage-2/data_upload_backup
+ logger -t backup_uploads Backing up to /mnt/storage-2/data_upload_backup
+ mkdir -p /root/rdiff-backup-errors
+ test -d /mnt/storage-2/data_upload_backup/upload
+ is_dir_empty /mnt/storage-2/data_upload_backup/upload
+ + wc -c
ls -A /mnt/storage-2/data_upload_backup/upload
+ [ 26 -eq 0 ]
+ logger -t backup_uploads Starting backup for /mnt/storage-1/sftp_data/sftpuser1/upload
+ hostname
+ rdiff-backup --ssh-no-compression /mnt/storage-1/sftp_data/sftpuser1/upload /mnt/storage-2/data_upload_backup/upload/
+ testssh [email protected]
+ remote_backup_machine_accessible=0
+ [ 0 -eq 0 ]
+ [email protected]:/mnt/storage-2/data_upload_backup_ed-mh-pi01
+ logger -t backup_uploads copying local backup up to [email protected]:/mnt/storage-2/data_upload_backup_ed-mh-pi01
+ rsync -aP --delete-after /mnt/storage-2/data_upload_backup/ [email protected]:/mnt/storage-2/data_upload_backup_ed-mh-pi01
+ rm -f /root/.backup_uploads_lockfile.pid
+ logger -t backup_uploads Lock file /root/.backup_uploads_lockfile.pid deleted, end of script

스크립트를 실행할 때마다 이 메시지가 표시되는 것은 아니며 가끔씩만 표시됩니다. 따라서 디렉토리가 실제로 비어 있거나 이와 유사한 경우일 수 있습니다.

그래서 내 질문은 이 명령에서 오류 출력이 나타나는 이유와 이를 방지할 수 있는 방법입니다(적어도 오류 발생을 원하지 않는 명백한 컨텍스트에서는).

이 질문은 제가 직접 물어본 다른 질문과 매우 유사합니다.여기, 그러나 나는 그것이 다르다고 확신합니다!

답변1

이는 명령의 오류 출력을 캡처하는 방법과 출력을 분리 cronic하는 방법 에 따라 다릅니다 .xtrace

+하나 이상으로 시작하는 줄을 찾습니다 .

이것은 대략적인 경험적 방법입니다. 일부 오류 메시지는 +이것으로 시작하는 경우 추적 출력으로 분류됩니다. xtrace 줄이 오류 메시지 줄이나 병렬로 시작된 다른 명령의 다른 xtrace 줄과 인터리브되면(귀하의 경우처럼) +줄의 시작 부분에 있지 않을 수 있으며 xtrace 출력으로 분류됩니다. 스크립트가 다른 스크립트를 사용하면 PS4제대로 작동하지 않습니다.

스크립트 의 경우 bashxtrace 출력에서 ​​오류 출력을 분리하려면 다음을 사용해야 합니다 BASH_XTRACEFD.

BASH_XTRACEFD=7 bash -x /path/to/your-script > out 2> err 7> trace

답변2

연결된 질문의 질문과 유사합니다. 출력에서 이 부분을 확인하세요.

+ + wc -c
ls -A /mnt/storage-2/data_upload_backup/upload

cronic이 행을 얻기 위해 출력을 구문 분석할 때 strace선행하는 행이 없는 행을 보고 이를 일반 오류 출력의 일부로 간주합니다.+

출력은 혼란스럽습니다. 다음 두 명령의 일반적인 xtrace 출력만 표시되어야 합니다.

+ ls -A /mnt/storage-2/data_upload_backup/upload
+ wc -c

여기서 발생해야 하는 일은 Dash가 stderr로 보내기 위해 호출하기 전에 전체 출력 라인을 버퍼링하는 것을 귀찮게 하지 않고 write()오히려 하나씩 수행한다는 것입니다. 이를 수행하는 두 개의 셸 프로세스가 파이프라인의 각 부분에 하나씩 있고 동시에 실행되기 때문에 출력이 혼동될 수 있습니다. 로드된 시스템에서 큰 문제 없이 이 작업을 반복할 수 있습니다.

+이 결과를 얻으려면 먼저 작성한 다음 명령을 작성하면 충분 하지만 더 나쁜 것 같습니다. 또한 다음과 같은 출력을 얻습니다.

+ is_dir_empty .
+ + lswc -A -c .

즉, 단일 단어라도 인터리브됩니다.

개인이 strace를 사용하여 작성하는 것을 볼 수 있습니다.

$ strace -etrace=write -f dash -xc 'echo aa bb cc'
write(2, "+ ", 2+ )                       = 2
write(2, "echo", 4echo)                     = 4
write(2, " aa", 3 aa)                      = 3
write(2, " bb", 3 bb)                      = 3
write(2, " cc", 3 cc)                      = 3
write(2, "\n", 1
)
...

이 문제를 쉘에서 수정하는 것 외에는 다른 방법이 생각나지 않습니다.

Bash에는 이 문제가 없는 것 같으니 대신 사용해보세요.

답변3

이는 관련이 있을 수도 있고 관련이 없을 수도 있지만,ls출력을 구문 분석하지 않음;여기에도 없습니다.

간단한

number_of_items () { echo $#; }
is_empty() {
 shopt -s dotglob
 shopt -s nullglob
 value=$(number_of_items $1/*)
 [[ "${value}" -eq 0 ]]
}

당신이 할 수 있도록 그렇게 할 것입니다

if is_empty "${local_backup_location}/${folder_to_backup_basename}" ; then

당신이 정말로 원하는 것은contains_accessible_things

number_of_items () { echo $#; }
contains_accessible_things() {
 shopt -s dotglob
 shopt -s nullglob
 # check for directory and readability 
 [[ -d "$1" && -r "$1" && ( "$(number_of_items $1/*)" -gt 0 ) ]]
}
if contains_accessible_things "${local_backup_location}/${folder_to_backup_basename}" ; then
  do_stuff
fi

관련 정보