ls -A 的错误输出导致错误输出,为什么?

ls -A 的错误输出导致错误输出,为什么?

我有一个使用 cron 定期运行的脚本。当这些脚本失败时,我希望收到电子邮件通知。我不希望每次运行并产生任何输出时都收到通知。

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

但是,在一个脚本中我有这样的命令:

if [ "$(ls -A ${local_backup_location}/nextcloud-data/)" ]; then
  # save space by removing diffs older than 6 months
  rdiff-backup --remove-older-than 6M --force ${local_backup_location}/nextcloud-data/ || echo "[$(date "+%Y-%m-%d %T")] No existing nextcloud data backup"
fi

旨在ls -A ${local_backup_location}/nextcloud-data/测试目录是否为空。我的问题是这个命令似乎会产生被识别为错误输出 cronic 的输出。 Cronic 将错误定义为任何非跟踪错误输出或非零结果代码。例如:

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

RESULT CODE: 0

ERROR OUTPUT:
appdata_ocgcv9nemegb
files_external
flow.log
flow.log.1
__groupfolders
.htaccess
index.html
nextcloudadmin
nextcloud-db.bak
nextcloud.log
nextcloud.log.1
.ocdata
rdiff-backup-data
Test_User
updater.log
updater-ocgcv9nemegb ]
custom
gitea-db.sql
log ]
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100   365    0     0  100   365      0    302  0:00:01  0:00:01 --:--:--   303
100   365    0     0  100   365      0    165  0:00:02  0:00:02 --:--:--   165
100   365    0     0  100   365      0    113  0:00:03  0:00:03 --:--:--   113
100   365    0     0  100   365      0     86  0:00:04  0:00:04 --:--:--    86
100   365    0     0  100   365      0     70  0:00:05  0:00:05 --:--:--    70
100   365    0     0  100   365      0     58  0:00:06  0:00:06 --:--:--     0
100   365    0     0  100   365      0     50  0:00:07  0:00:07 --:--:--     0
100   365    0     0  100   365      0     44  0:00:08  0:00:08 --:--:--     0
100   365    0     0  100   365      0     39  0:00:09  0:00:09 --:--:--     0
100   365    0     0  100   365      0     37  0:00:09  0:00:09 --:--:--     0
100 10.4M    0 10.4M  100   365  1016k     34  0:00:10  0:00:10 --:--:-- 2493k
100 11.6M    0 11.6M  100   365  1128k     34  0:
 00:10  0:00:10 --:--:-- 3547k

STANDARD OUTPUT:
Maintenance mode enabled
Deleting increment at time:
<snip>

那么为什么该命令ls -A ${local_backup_location}/nextcloud-data/在这种情况下会产生错误输出,以及如何防止这种情况发生?另一种可靠的方法来测试目录是否为空是可以接受的,但我还想解释为什么该命令似乎会产生错误输出。

编辑:添加 Cronic 标准输出set -ex

一些评论者要求提供非常长的实际整个脚本,但 Cronic 报告了脚本的实际标准输出,我set -ex在脚本的顶部使用。错误输出在调用后立即发生,ls -A /mnt/reos-storage-2/backups/nextcloud-data/这就是为什么我相信错误输出是此命令的结果。

+ rdiff-backup --ssh-no-compression /var/www/nextcloud /mnt/reos-storage-2/backups/nextcloud/
+ ls -A /mnt/reos-storage-2/backups/nextcloud-data/
+ [ 67cf481e-62a3-1039-8bf2-05805d214bca
<removed>
appdata_ocgcv9nemegb
<removed>
<removed>
<removed>
<removed>
files_external
flow.log
flow.log.1
__groupfolders
.htaccess
index.html
<removed>
<removed>
nextcloudadmin
nextcloud-db.bak
nextcloud.log
nextcloud.log.1
.ocdata
<removed>
<removed>
rdiff-backup-data
<removed>
Test_User
<removed>
updater.log
updater-ocgcv9nemegb ]
+ rdiff-backup --remove-older-than 6M --force /mnt/reos-storage-2/backups/nextcloud-data/
+ date +%Y-%m-%d %T
+ echo [2021-04-21 03:23:38] Starting nextcloud data backup

答案1

+ [ 67cf481e-62a3-1039-8bf2-05805d214bca
<已删除>
[...]
更新程序日志
更新程序-ocgcv9nemegb]

这里,这是一个命令。它在set -x/输出中分成多行xtrace,因为 / 输出ls包含换行符。 (Bash 会打印出带有一些引号的内容,而 Dash 则不会。)

默认情况下,xtrace输出会发送到 stderr,与常规错误输出相同,Cronic 尝试通过查看+行开头的标记来将它们分开xtrace。这里失败了,它认为文件名是常规的错误输出。

cronic 的作用基本上是这样的:

PATTERN="^${PS4:0:1}\\+${PS4:1}"
if grep -aq "$PATTERN" $TRACE
then
    ! grep -av "$PATTERN" $TRACE > $ERR

禁用xtrace是解决这个问题的一种方法,但这样做会很遗憾,因为 cronic 很好地支持它。

相反,最好使用其他方法来检查目录是否为空。

与 保持一致ls -A,您可以通过管道输出来wc计算那里的字符:

if [ "$(ls -A "${local_backup_location}/nextcloud-data/" | wc -c)" -gt 0 ]; then
    echo "directory not empty"
fi

或者grep

if ls -A "${local_backup_location}/nextcloud-data/" | grep -q .; then
    echo "directory not empty";
fi

检查目录是否为空可以在 shell 本身内以其他方式完成,但处理所有极端情况可能会很麻烦。参见例如 便携式检查空目录

答案2

如果$gitea_backup_dir不存在,或者ls以任何方式让人不高兴,ls则会向 写入一条错误消息STDERR

2>/dev/null您可以通过附加到命令来丢弃整个错误流。

恕我直言,更好的方法是:

if ([[ -d "$gitea_backup_dir" ]] && \
    [[ $(stat --format="%h" "$gitea_backup_dir") -gt 2 ]] ) ; then
    echo "Nonempty"
else
    echo "Empty"
fi
  

首先检查目录是否存在,然后查看目录中的硬链接数量(目录中文件和子目录条目的数量)是否大于2(每个目录至少有2个条目:...)。

相关内容