为什么这个脚本运行缓慢?

为什么这个脚本运行缓慢?

我有以下 bash 脚本作为 OpenMediaVault 上的 cronjob 运行:

BACKUP_DIR='/srv/dev-disk-by-uuid-9EE055CFE055ADF1/Backup dir/'
BACKUP_FILE_PATH="/srv/dev-disk-by-uuid-9EE055CFE055ADF1/Backup dir/Backup [ashen] ($(date +%d-%m-%Y)).tar.gz"
SERVER_DIR=/var/lib/docker/volumes/49c9e5c53ea5b9c893c0a80117860da9b493484395c0$
MAX_BACKUPS_COUNT=4

tar -zcf "$BACKUP_FILE_PATH" $SERVER_DIR

cd "$BACKUP_DIR"

[[ $( ls | wc -l ) -gt $MAX_BACKUPS_COUNT ]] && rm "$(ls -t | tail -1)"

该脚本的重点是在给定位置创建 .tar.gz 备份,如果备份目录中有超过 4 个文件,则删除最旧的文件(重点是仅保留 4 个最近的备份)。最后一行/命令并不总是有效。在终端中手动运行它可以按预期工作,有时脚本会执行它,但有时它会停止,直到我手动尝试运行脚本/行,然后它似乎会神奇地自行修复一段时间。

有谁知道为什么它偶尔停止执行最后一行?即使我看到正在创建备份。

答案1

如果任何文件名包含换行符,您的脚本将失败。解析ls是一个非常糟糕的主意并且很可能会失败。此外,您的脚本只会删除最新的文件。因此,如果有 100 个文件,您将剩下 99 个。您似乎期望它会删除除最近的 4 个文件之外的所有内容,但脚本的逻辑并非如此。

这是一种替代方法,可以处理任意文件名,并且实际上删除除最近 4 个文件之外的所有文件:

#!/bin/bash

## avoid using CAPS for local variable names in shell scripts
backup_dir='/srv/dev-disk-by-uuid-9EE055CFE055ADF1/Backup dir/'
backup_file_path="/srv/dev-disk-by-uuid-9EE055CFE055ADF1/Backup dir/Backup [ashen] ($(date +%d-%m-%Y)).tar.gz"
server_dir='/var/lib/docker/volumes/49c9e5c53ea5b9c893c0a80117860da9b493484395c0$'
## This needs to be set to the number of files you want to keep plus one,
## so that we can use tail -n $max_backups below.
max_backups=5

tar -zcf "$BACKUP_FILE_PATH" "$SERVER_DIR" 

## delete all but the newest 4 tar.gz files in the
## backup directory
stat --printf '%Y %n\0' "$backup_dir"/*tar.gz |
  sort -rznk1,1 | tail -z -n +"$max_backups" |
  sed -z 's/^[0-9]* //' | xargs -0 rm -v

这里的工作是由该stat命令和各种下游管道完成的。以下是该命令正在执行的操作的详细说明:

  • stat --printf '%Y %n\0' "$backup_dir"/*tar.gz.tar.gz:这将打印自备份目录中所有文件的纪元以来的文件名和文件期限(以秒为单位) 。为了能够处理带有换行符 ( \n) 的文件名,我们需要以 NULL ( \0) 结束每个条目。输出如下所示:

    $ stat --printf '%Y %n\0' * | tr '\0' '\n'
    1616867929 ./afile 5  tar.gz
    1616868565 ./file 10  tar.gz
    1616868560 ./file 1  tar.gz
    1616868561 ./file 2  tar.gz
    1616867927 ./file 3  tar.gz
    1616867928 ./file 4  tar.gz
    1616867930 ./file 6  tar.gz
    1616868562 ./file 7  tar.gz
    1616868563 ./file 8  tar.gz
    1616868564 ./file 9  tar.gz
    

对于这个例子,我通过管道传输输出,tr '\0' '\n'以便它清晰可辨,但在实际输出中,每个记录的末尾都有一个\0

  • sort -rznk1,1:上面的输出stat通过管道传输到sort它将按数字 ( -n) 排序,按相反顺序 ( -r) 排序,\0用作记录分隔符 ( -z) 并仅考虑第一个字段 ( -k1,1),即文件的年龄。

    输出看起来像:

      $ stat --printf '%Y %n\0' "$backup_dir"/*tar.gz | 
          sort -rznk1,1 | tr '\0' '\n'
      1616868565 ./file 10  tar.gz
      1616868564 ./file 9  tar.gz
      1616868563 ./file 8  tar.gz
      1616868562 ./file 7  tar.gz
      1616868561 ./file 2  tar.gz
      1616868560 ./file 1  tar.gz
      1616867930 ./file 6  tar.gz
      1616867929 ./afile 5  tar.gz
      1616867928 ./file 4  tar.gz
      1616867927 ./file 3  tar.gz
    
  • tail -z -n +"$max_backups":该命令tail -n +X将打印您从 record 开始给出的最后一条记录X。这里,X$max_backups变量,这就是为什么需要将该变量设置为要保留的文件数加一。 let-z处理tail以 null 结尾的记录。

    此时,我们有了要删除的文件列表,但它们也有其年龄,我们需要将其删除:

       $ stat --printf '%Y %n\0' "$backup_dir"/*tar.gz  | sort -rznk1,1       
          | tail -z -n +5 | tr '\0' '\n'
      1616868561 ./file 2  tar.gz
      1616868560 ./file 1  tar.gz
      1616867930 ./file 6  tar.gz
      1616867929 ./afile 5  tar.gz
      1616867928 ./file 4  tar.gz
      1616867927 ./file 3  tar.gz
    
  • sed -z 's/^[0-9]* //':删除文件的年龄,仅保留名称。再次,-z处理空终止记录:

      $ stat --printf '%Y %n\0' "$backup_dir"/*tar.gz  | 
              sort -rznk1,1 | tail -z -n +5 | 
                  sed -z 's/^[0-9]* //' | tr '\0' '\n' 
      ./file 2  tar.gz
      ./file 1  tar.gz
      ./file 6  tar.gz
      ./afile 5  tar.gz
      ./file 4  tar.gz
      ./file 3  tar.gz
    
  • xargs -0 rm -v: 最后一步。这将删除文件,并且再次删除文件,-z以便它可以处理以空结尾的记录。

重要的:该脚本假定您正在使用 GNU 工具。打开媒体库声称是 Linux 并运行 Debian,所以它应该适合你,但我从未使用过该系统,所以我不能确定。

相关内容