1

Я делаю сценарий bash для резервного копирования своей базы данных ежедневно (в Mac OS X) и хочу сохранить 7 последних каталогов резервных копий. Вот проблемный фрагмент моего скрипта (для перечисления всего, кроме самых новых каталогов):

# Assigning a value to a variable named NUMBEROFFOLDERS. 
NUMBEROFFOLDERS=7

# Command that leaves the newest NUMBEROFFOLDERS and removes everything else. 
TOBEDELETED=`(ls -t|head -n $(($NUMBEROFFOLDERS));ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm -rfv`

# Executing the TOBEDELETED command. 
eval "$TOBEDELETED"

PS Команда, назначенная в TOBEDELETED, работает, когда я вхожу в окно терминала, но когда я запускаю скрипт, ничего не происходит.


РЕДАКТИРОВАТЬ: i) Сценарий должен перечислять все каталоги в определенном месте назначения дважды по-разному; sort должен их проанализировать, и только самые новые каталоги будут удалены из списка; sed должен обрабатывать все пробелы правильно, а rm -rfv удалит все каталоги из списков (каталоги, которые нужно удалить) со всеми их файлами.

ii) Скрипт должен оставлять новейшие папки $ NUMBEROFFOLDERS.

iii) я не получил ошибку. Я считаю, что проблема заключается в том, когда я ссылаюсь на переменную назначенной команды.

3 ответа3

4

Вы делаете не то, что думаете

# Command that leaves the newest NUMBEROFFOLDERS and removes everything else. 
TOBEDELETED=`(ls -t|head -n $(($NUMBEROFFOLDERS));ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm -rfv`

Обратные пометки означают, что конвейер фактически выполняется, а выходные данные команды rm в переменную. Этот вывод не содержит команд, это просто вывод rm -v .

Я бы упростил эту команду немного:

stat -f "%m %N" * | sort -nr | sed -e "1,${numfolders}d" | cut -d " " -f 2 | xargs rm -rfv

У меня нет системы BSD для игры, поэтому я не уверен, что строка формата stat верна.

1

Не очень элегантное рабочее решение (проверьте вашу среду:
Давайте начнем с подхода dividi и impera, потому что он более понятен:

ListToSave=$(ls -dt */ |head -n $NUMBEROFFOLDERS| cut -f1 -d'/';)

В этой строке хранится список папок, которые будут сохранены в переменной ListToSave , возможно, для последующей переработки в скрипте ...

echo "$ListToSave"| awk 'BEGIN{printf("find . -maxdepth 1 -not -path \".\" ")}\
 {printf ("-not -path \"./"$0"\" ") } \
 END{print " -exec /bin/rm -rvf \"{}\" \\; "}' | /bin/sh

С помощью этой трехстрочной команды вы создаете команду find чтобы удалить все каталоги, кроме сохраняемых, и выполняете ее, передавая ее в оболочку (в данном случае /bin/sh).
Вы можете проверить конструкцию команды, вытирая трубу в оболочку | /bin/sh .
По соображениям безопасности всегда безопаснее запускать команду с полным путем. (лучше /bin/rm чем rm , просто чтобы избежать какого-либо трояна)

Комментарий: анализировать вывод ls небезопасно, когда вы планируете создать скрипт. Часто бывает, что вы начинаете с ограниченной и контролируемой ситуации; через некоторое время, когда вы уже забыли об этом, вы должны попытаться использовать его в более сложном случае и менее контролируемом. Когда, наконец, вы убедитесь, что все работает хорошо ... что-то происходит, и вы понимаете это слишком поздно.

Заметки:

  1. Не безопасно анализировать вывод ls ... проверьте здесь, т.е.
  2. ls -dt */ | cut -f1 -d'/' должен содержать список только каталогов и даже не файлов.
  3. xargs немедленно запускает командную строку, как вы можете прочитать с его страницы руководства

    xargs - собирать и выполнять командные строки из стандартного ввода


Я настоятельно рекомендую избегать делать то, что следует.
Однако, если вы хотите разобрать ls вы можете написать как минимальную модификацию из вашей предыдущей попытки что-то похожее на это

TOBEDELETED=`(ls -dt */ |head -n $NUMBEROFFOLDERS | cut -f1 -d'/'; ls)| sort | uniq -u | xargs echo "rm -rfv"`

таким образом, вы будете хранить выходные данные в TOBEDELETED которые вы сможете выполнить позже в сценарии по мере написания.

Примечание: он не работает с не добрыми именами файлов как пробелы, \t , \n ... и может создавать непредсказуемые побочные эффекты. Представьте, что вам нужно удалить каталог "1 бис", но не каталог "1", который тоже существует. Вы удалите каталог "1", и у вас возникнет ошибка, когда он попытается удалить каталог "bis", если его не существует.

0

Я добавляю еще один ответ с помощью конструктора тестовой среды . Я создал общий исполняемый проект здесь .
Перейдите по ссылке написать в оболочке bash main.sh Оно работает. После этого скопируйте скрипт на вашу систему и проверьте, работает ли он. Если не опубликовать ошибку, которую вы получите.

#!/bin/bash
printf "\n\n\n# Let's we create your test directory\n" 
TESTDIR="tmptmp"
    rm -rf $TESTDIR
    mkdir $TESTDIR
    cd $TESTDIR
      printf "\n### Now we are in \n"
        pwd 
        mkdir -p 1 2 3 4 5 6 7 8 9 "1 bis";
      printf "\n### Here the list in the creation order\n"
         ls -dt */
         sleep 1.5s             # Take your time ...
         touch 1 2 3 4 5 6 8;   # Make those the last modified
      printf "\n### Here the list with modified order\n"
         ls -dt */

printf "\n\n# Let's go with your script \n\n"

#
# It follows your script. You can put it where you prefer but not in the 
# directory that you want to delete else you can delete it too...
# If you put in the upper directory you can run it with: 
# /bin/bash ../myscript.sh
#
# If you do one time `chmod u+x myscript.sh` you can execute it with
# ../myscript 
# Remember to put as a 1st line "#!/bin/bash"
# Note: check the path of all the executable... 
#

#!/bin/bash
NUMBEROFFOLDERS=7
ListToSave=$(ls -dt */ |head -n $NUMBEROFFOLDERS| cut -f1 -d'/';)
printf "This is the list of directories to be saved:\n%s\n" "$ListToSave"

printf "\n# That's the command that you'll execute piping it into a shell\n"
echo "$ListToSave" \
     | awk 'BEGIN{printf("find . -maxdepth 1 -not -path \".\" ")}\
     {printf ("-not -path \"./"$0"\" ") } \
     END{print " -exec /bin/rm -rvf \"{}\" \\; "}' 
  sleep 1 ; 

printf "\n# NOW we execute those commands with \"the power of piping\" \n\n"
 echo "$ListToSave" \
      | awk 'BEGIN{printf("find . -maxdepth 1 -not -path \".\" ")}\
 {printf ("-not -path \"./"$0"\" ") } \
 END{print " -exec /bin/rm -rvf \"{}\" \\; "}' | /bin/sh


printf "\n# That's the list of the survived as 'ls -d *' says to us\n\n"
ls -d *
printf "\n\n ### It worked like a charm ###\n\n"

exit 0

Примечание: если вы выполните его с помощью bash -f main.sh он не будет работать, потому что вы попросите bash избежать расширения пути. Ищите об этом в man bash .

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .