Удалите самый старый файл в каталоге, когда будет больше чем 7 файлов?

Я должен создать резервный сценарий (удар) базы данных MySQL. Когда я выполню сценарий, sql файл будет создан в "/home/user/Backup". Проблема, я также должен сделать сценарий, который удаляет самый старый файл, если существует больше чем 7 файлов в ".../резервное копирование". Кто-то знает, как сделать это? Я попробовал все, но этому не удалось каждый раз считать файлы в каталоге и обнаружить самый старый...

6
задан 6 March 2017 в 13:00

2 ответа

Введение

Позволяет, рассматривают проблему: задача состоит в том, чтобы проверить, ли количество файлов в особенности каталог по определенному числу, и удалите самый старый файл среди тех. Сначала может казаться, что мы должны пересечь дерево каталогов однажды подсчет файлов, затем пересечь его снова, чтобы найти время последнего изменения всех файлов, отсортировать их и извлечь самое старое для удаления. Но полагая, что в данном случае OP упомянула, что удалила файлы, если и только если количество файлов выше 7, оно предполагает, что мы можем просто получить список всех файлов с их метками времени однажды и сохранить их в переменную.

проблемой с этим подходом является опасность, связанная с именами файлов. Как был упомянут в комментариях, никогда не рекомендуется проанализировать ls команда, так как вывод может содержать специальные символы и повредить сценарий. Но, как некоторые из Вас могут знать в подобных Unix системах (и Ubuntu также), каждый файл имеет inode число, связанное с нею. Таким образом создание списка записей с метками времени (в секундах для легкой числовой сортировки) плюс inode число, разделенное новой строкой, гарантирует, что мы безопасно анализируем имена файлов. Удаление самого старого имени файла может также быть сделано тот путь.

сценарий, представленный ниже, делает точно, как описано выше.

Сценарий

, Важный : прочитайте комментарии, особенно в delete_oldest функция.

#!/bin/bash
# Uncomment line below for debugging
#set -xv
delete_oldest(){
     # reads a line from stdin, extracts file inode number
     # and deletes file to which inode belongs
     # !!! VERY IMPORTANT !!!
     # The actual command to delete file is commented out.
     # Once you verify correct execution, feel free to remove
     # leading # to uncomment it
     read timestamp file_inode
     find "$directory" -type f -inum "$file_inode"  -printf "Deleted %f\n" 
     # find "$directory" -type f -inum "$file_inode"  -printf "Deleted %f\n"  -delete
}

get_files(){
    # Wrapper function around get files. Ensures we're working
    # with files and only on one specific level of directory tree
    find "$directory" -maxdepth 1 -type f  -printf "%Ts\t%i\n" 
}

filecount_above_limit(){
    # This function counts number of files obtained
    # by get_files function. Returns true if file
    # count is greater than what user specified as max
    # value 
    num_files=$(wc -l <<< "$file_inodes"  )
    if [ $num_files -gt "$max_files" ];
    then
        return 0
    else
        return 1
    fi
}

exit_error(){
    # Print error string and quit
    printf ">>> Error: %s\n"  "$1" > /dev/stderr 
    exit 1
}

main(){
    # Entry point of the program. 
    local directory=$2
    local max_files=$1

    # If directory is not given
    if [ "x$directory" == "x"  ]; then
        directory="."
    fi

    # check arguments for errors
    [ $# -lt 1  ] && exit_error "Must at least have max number of files"
    printf "%d" $max_files &>/dev/null || exit_error "Argument 1 not numeric"
    readlink -e "$directory" || exit_error "Argument 2, path doesn't exist"

    # This is where actual work is being done
    # We traverse directory once, store files into variable.
    # If number of lines (representing file count) in that variable
    # is above max value, we sort numerically the inodes and pass them
    # to delete_oldest, which removes topmost entry from the sorted list
    # of lines.
    local file_inodes=$(get_files)
    if filecount_above_limit 
    then
        printf  "@@@ File count in %s is above %d." "$directory" $max_files
        printf "Will delete oldest\n"
        sort -k1 -n <<< "$file_inodes" | delete_oldest
    else
        printf "@@@ File count in %s is below %d."  "$directory" $max_files
        printf "Exiting normally"
    fi
}

main "$@"

примеры Использования

$ ./delete_oldest.sh 7 ~/bin/testdir                                                                                     
/home/xieerqi/bin/testdir
@@@ File count in /home/xieerqi/bin/testdir is below 7.Exiting normally
$ ./delete_oldest.sh 7 ~/bin                                                                                             
/home/xieerqi/bin
@@@ File count in /home/xieerqi/bin is above 7.Will delete oldest
Deleted typescript

Дополнительное обсуждение

Это, вероятно, страшно.. длинный .and.. .and похож на него, делает слишком много. И это могло бы быть. На самом деле все можно пихнуть на одну строку командной строки (очень измененная версия предложения muru, отправленного в чат , который имеет дело с именами файлов. echo используется вместо rm в демонстрационных целях):

find /home/xieerqi/bin/testdir/ -maxdepth 1 -type f -printf "%T@ %p\0" | sort -nz | { f=$(awk  'BEGIN{RS=" "}NR==2{print;next}'  ); echo "$f" ; }

Однако у меня есть несколько вещей, мне не нравится приблизительно это:

  • это удаляет самый старый файл безусловно, не проверяя количество файлов в каталоге
  • , это имеет дело с именами файлов непосредственно (который потребовал, чтобы я использовал неловкий awk команда, которая, вероятно, порвет с именами файлов, которые имеют пробелы)
  • слишком много инфраструктуры (слишком много каналов)

Так, в то время как мой сценарий выглядит ужасно гигантским для простой задачи, это делает всех больше проверок и имеет целью решать проблему со сложными именами файлов. Это, вероятно, было бы короче и более идиоматичным для реализации в Perl или Python (который я абсолютно могу сделать, я просто, оказалось, выбрал bash для этого вопроса).

3
ответ дан 23 November 2019 в 07:59

Я думаю, что ответ @Serg хорош, и я учусь от него и от @muru. Я сделал этот ответ, потому что я хотел исследовать и изучить, как создать файл сценария оболочки на основе вывода от find с 'действием' -print для сортировки файлов согласно времени, они были созданы/изменены. Предложите улучшения и bugfixes (при необходимости).

Как Вы заметите, стиль программирования очень отличается. Мы можем сделать вещи во многих отношениях в Linux :-)

Я сделал сценарий оболочки удара для соответствия требованиям OP, @beginner27 _, но не слишком трудно изменить его в других но подобных целях.

Следующий снимок экрана показывает, как он был протестирован: Одиннадцать файлов были созданы, и сценарий (который находится в ~ / мусорное ведро и имеет, выполняются, полномочия) выполняется. Я удалил # символ из строки

# bash "$cmd"

сделать его

bash "$cmd"

В первый раз сценарий обнаруживает и печатает эти одиннадцать файлов, семь новейших файлов с синим фоном и четыре самых старых файла с красным фоном. Четыре самых старых файла удалены. Скрипт запущен во второй раз (только для демонстрации). Это обнаруживает и печатает оставление семью файлами и удовлетворено, 'Нет никакого файла резервной копии для удаления'.

enter image description here

Решающее find команда, которая сортирует файлы согласно времени, похожа на это,

find "$bupdir" -type f -printf "%T+ %p\0"|sort -nrz > "$srtlst"

Вот файл сценария. Я сохранил его в ~/bin с именем rm_old_backups, но можно дать ему любое имя, пока это уже не вмешивается в некоторых существующее название исполняемой программы.

#!/bin/bash

keep=7  # set the number of files to keep

# variables and temporary files

inversvid="\0033[7m"
resetvid="\0033[0m"
redback="\0033[1;37;41m"
greenback="\0033[1;37;42m"
blueback="\0033[1;37;44m"

bupdir="$HOME/Backup"
cmd=$(mktemp)
srtlst=$(mktemp)
rmlist=$(mktemp)

# output to the screen

echo -e "$inversvid$0:
keep $keep backup files, remove the oldest files, if more than $keep are found $resetvid"

echo "Security fix: You must edit this script and remove the # character from
a line near the end of the script '# bash \"\$cmd\"' --> 'bash \"\$cmd\"'
otherwise the script will only show what it can do. Please test that it
works correctly before you remove that # character!"

# the crucial find command, that sorts the files according to time

find "$bupdir" -type f -printf "%T+ %p\0"|sort -nrz > "$srtlst"

# more output

echo -e "${inversvid}time-stamp                     file-name                               $resetvid"
echo -en "$blueback"
sed -nz -e 1,"$keep"p "$srtlst" | tr '\0' '\n'
echo -en "$resetvid"

echo -en "$redback"
sed -z -e 1,"$keep"d "$srtlst" | tr '\0' '\n' | tee "$rmlist"
echo -en "$resetvid"

# remove oldest files if more files than specified are found

if test -s "$rmlist"
then
 echo rm '"'$(sed -z -e 1,"$keep"d -e 's/[^ ]* //' -e 's/$/" "/' "$srtlst")'"'\
 | sed 's/" ""/"/' > "$cmd"
 cat "$cmd"

# uncomment the following line to really remove files 
# bash "$cmd"

 echo "The oldest backup files are removed"
else
 echo "There is no old backup file to remove"
fi

# remove temporary files

rm $cmd $srtlst $rmlist
1
ответ дан 23 November 2019 в 07:59

Другие вопросы по тегам:

Похожие вопросы: