이 스크립트가 느리게 실행되는 이유는 무엇입니까?

이 스크립트가 느리게 실행되는 이유는 무엇입니까?

OpenMediaVault에서 cronjob으로 실행되는 다음 bash 스크립트가 있습니다.

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: 백업 디렉터리에 있는 모든 파일의 epoch 이후 파일 이름과 파일 수명(초)을 인쇄합니다. 줄 바꿈( )으로 파일 이름을 처리하려면 각 항목을 NULL( )로 종료 \n해야 합니다 . \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 +XRecord 에서 시작하여 마지막으로 제공한 레코드를 인쇄합니다 X. 여기에 변수가 X있으므로 $max_backups보관하려는 파일 수에 1을 더한 값으로 변수를 설정해야 합니다. null로 끝나는 레코드를 -z처리 합니다 .tail

    이제 삭제할 파일 목록이 있지만 파일의 수명도 있으므로 삭제해야 합니다.

       $ 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]* //': 파일의 나이를 제거하고 이름만 유지합니다. 이번에도 -znull로 끝나는 레코드를 처리합니다.

      $ 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: 마지막 단계. 그러면 파일이 삭제되고 -zNull 종료 레코드를 처리할 수 있도록 다시 삭제됩니다.

중요한: 이 스크립트는 GNU 도구를 사용하고 있다고 가정합니다.미디어 라이브러리 열기Linux라고 주장하고 Debian을 실행하므로 여러분에게 도움이 될 것입니다. 하지만 저는 해당 시스템을 사용해 본 적이 없으므로 확신할 수 없습니다.

관련 정보