首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Bash函数产生错误输出,为什么?

Bash函数产生错误输出,为什么?
EN

Unix & Linux用户
提问于 2022-01-06 16:43:42
回答 3查看 216关注 0票数 1

我有一个脚本,我经常使用cron运行。当这些脚本失败时,我想通过电子邮件得到通知。我不希望每次它们运行并产生任何输出时都得到通知。

因此,我使用脚本克罗尼奇在cron中运行我的作业,这意味着只发送错误输出,而不仅仅是任何输出。

但是,在一个脚本中,我有一个bash函数来检测目录是否为空:

代码语言:javascript
复制
# 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语句中使用:

代码语言:javascript
复制
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

有时候我会收到克罗尼奇的这样一封电子邮件

代码语言:javascript
复制
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:


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_receiver=sysadmin@mydomain.tld
+ 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 root@ed-mh-x86001.mydomain.tld
+ remote_backup_machine_accessible=0
+ [ 0 -eq 0 ]
+ remote_backup_location=root@ed-mh-x86001.mydomain.tld:/mnt/storage-2/data_upload_backup_ed-mh-pi01
+ logger -t backup_uploads copying local backup up to root@ed-mh-x86001.mydomain.tld:/mnt/storage-2/data_upload_backup_ed-mh-pi01
+ rsync -aP --delete-after /mnt/storage-2/data_upload_backup/ root@ed-mh-x86001.mydomain.tld:/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

我并不是每次运行脚本时都会看到这种情况,只是偶尔,所以可能在目录实际上为空或类似于此的情况下。

因此,我的问题是,为什么从这个命令中得到错误输出,以及如何防止它(至少在我不希望它出现的明显情况下是这样)?

这个问题和我问自己这里的另一个问题很相似,但我向你保证这是不同的!

EN

回答 3

Unix & Linux用户

回答已采纳

发布于 2022-01-06 17:32:18

类似于相关问题中的问题。请参阅输出中的此部分:

代码语言:javascript
复制
+ + wc -c
ls -A /mnt/storage-2/data_upload_backup/upload

当cronic解析输出以获取strace行时,它会看到前面没有+的行,并认为这是常规错误输出的一部分。

那里的输出混乱了,它应该只是显示这两个命令的正常xtrace输出,即:

代码语言:javascript
复制
+ ls -A /mnt/storage-2/data_upload_backup/upload
+ wc -c

这里要做的是,在调用write()将其发送到stderr之前,Dash不需要对整个输出行进行缓冲,而是一片片地进行缓冲。因为有两个shell进程这样做,每个管道的每个部分都有一个,并且它们同时运行,所以输出可能会被混淆。我可以在没有太多麻烦的加载系统上重复这一点。

对于这个结果,它已经足够先编写+,然后编写命令,但更糟糕的是,我也得到了这样的输出:

代码语言:javascript
复制
+ is_dir_empty .
+ + lswc -A -c .

也就是说,即使是单个单词也会交织在一起。

我们可以看到这个人用strace写东西:

代码语言:javascript
复制
$ 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似乎没有这个问题,所以可能会转而使用它。

票数 1
EN

Unix & Linux用户

发布于 2022-01-06 17:28:20

这取决于cronic如何捕获命令的错误输出,以及如何尝试将xtrace输出与之分离。

它寻找从一个或多个+开始的行。

这是个粗略的启发。如果某些错误消息以+开头,则将其归类为跟踪输出。如果一个xtrace行与另一个并行启动的命令的错误消息行或另一个xtrace行交织在一起(就像在您的示例中那样),那么+可能不在行的开头,它将被归类为xtrace。如果脚本使用不同的PS4,这将不能正常工作,等等。

对于bash脚本,要将错误输出与xtrace输出分开,您应该使用BASH_XTRACEFD

代码语言:javascript
复制
BASH_XTRACEFD=7 bash -x /path/to/your-script > out 2> err 7> trace
票数 2
EN

Unix & Linux用户

发布于 2022-01-06 17:17:42

这可能是相关的,也可能是不相关的,但是从不解析ls输出;即使在这里也不是。

一个简单的

代码语言:javascript
复制
number_of_items () { echo $#; }
is_empty() {
 shopt -s dotglob
 shopt -s nullglob
 value=$(number_of_items $1/*)
 [[ "${value}" -eq 0 ]]
}

就可以了,所以你可以

代码语言:javascript
复制
if is_empty "${local_backup_location}/${folder_to_backup_basename}" ; then
  …

你真正想要的是contains_accessible_things

代码语言:javascript
复制
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
票数 0
EN
页面原文内容由Unix & Linux提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://unix.stackexchange.com/questions/685348

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档