Как я могу захватить первую подпапку в каталоге и удалить ее?
Я нашел скрипт для отслеживания свободного места на диске.
Он отправляет электронное письмо с предупреждением, если место заканчивается, но я также хочу удалить некоторые ненужные вещи.
У меня есть резервная папка, в которой я сохраняю ежедневные и ежемесячные резервные копии. Я хочу удалить первую папку, так как она всегда самая старая, но я не знаю имени самой старой резервной копии.
Мои папки без января-мая и декабря:
06-01
07-01
08-01
09-01
10-01
11-01
Friday
Monday
Saturday
Sunday
Thursday
Tuesday
Wednesday
Как я могу удалить первую папку «06-01», не зная ее имени?
Если вы хотите найти первая подпапка Что касается алфавитно-цифрового порядка (будьте осторожны, это зависит от ваших локальных настроек), вы можете использовать этот небольшой фрагмент Bash:
shopt -s nullglob
shopt -u dotglob
a=( */ )
echo "${a[0]}"
это будет просто эхом первая подпапка относительно буквенно-цифровой сортировки Баша.
Если вы хотите удалить его, добавив несколько охранников:
shopt -s nullglob
shopt -u dotglob
a=( */ )
[[ -d "${a[0]}" ]] && echo rm -rfv -- "${a[0]}"
Я не уверен, что лично буду использовать это на рабочем сервере. Смотря как конечно Я содержимое каталога (и насколько я уверен в резервных копиях).
В shopt -s nullglob
и shopt -u dotglob
здесь, чтобы избежать глупой ошибки, которая могла бы произойти, если бы они были установлены иначе. Также соблюдайте цитаты и не меняйте их вообще.
О, я оставил echo
напротив rm
. Удаляйте его только после нескольких проверок, если вы уверены, что он работает именно так, как вы ожидаете. В качестве однострочного:
shopt -s nullglob; shopt -u dotglob; a=( */ ); [[ -d "${a[0]}" ]] && echo rm -rfv -- "${a[0]}"
Если вы хотите указать путь к вложенным папкам, просто замените a=( */ )
заявление с:
a=( /path/to/folder/*/ )
убедитесь, что вы указали абсолютный путь и заканчиваете */
(с косой чертой в конце). Если на вашем пути есть пробелы, используйте кавычки:
a=( '/path with/spaces to/folder/'*/ )
(обратите внимание, как */
находится за пределами цитируемой части, и нет пробелов после закрывающей кавычки и */
).
Теперь, если ты действительно хотите определить самую старую подпапку (с определенной неопределенностью, если несколько из них имеют одинаковый возраст), вы можете (вздрогнув) проанализировать вывод ls
отсортировать по времени в обратном порядке и взять первое и удалить его, как в:
ls -1tr --group-directories-first /path/to/folder | head -1 | xargs echo rm -rfv
но это не удастся, если нет каталогов (вы удалите самый старый файл вместо этого) или завершиться ошибкой, если есть файлы, содержащие пробелы или забавные символы. Не делай этого.
Вместо этого вы могли бы попросить ls */
без перечисления содержимого каталогов с -d
вариант:
ls -1dtr path/to/folder/*/ | head -1 | xargs echo rm -rfv
но это с треском провалилось бы, если бы в имени были пробелы. Не делай этого.
Затем вы можете попытаться исправить это, используя -Q
возможность ls
(т.е. процитируйте вывод):
ls -1dtQr path/to/folder/*/ | head -1 | xargs echo rm -rfv
но это не удается, если в имени файла есть кавычки или новые строки. Не делай этого.
Тогда ты бы попробовал xargs
с -0
вариант:
ls -1dtQr path/to/folder/*/ | head -1 | xargs -0 echo rm -rfv
но это не удается, если в имени файла есть символы новой строки. Не делай этого.
Тогда ты мог ... СТОП!
Если вам действительно нужен 100% пуленепробиваемый метод, он может быть не самым эффективным, но он подойдет вам, если безопасность является вашим главным приоритетом, вот возможность (в Bash):
shopt -s nullglob
shopt -u dotglob
where="/path with spaces is ok/to/folder"
for i in "$where"/*/; do
if ! find "$where" -mindepth 1 -maxdepth 1 \! -name '.*' -type d \! -newer "$i" \! -samefile "$i" -print | read; then
echo rm -rfv -- "$i"
break
fi
done
Это не самый эффективный вариант, но работает очень хорошо: он перебирает все каталоги (первого уровня). i
в данном каталоге where
и запускает find
чтобы (не скрытые) каталоги были старше, чем каталог i
а если находит, то ничего не делает; если не находит, то ломаем echo rm
это и разорвать петлю. Побочным эффектом является то, что каталог, который будет удален, является самым старым и первым в алфавитно-цифровом порядке Bash.
Вы даже можете сделать из него классный сценарий (назовите его, например, rm_oldest_dir
):
#!/bin/bash
if [[ $1 = --serious ]]; then
serious=()
shift
else
serious=("echo")
fi
if [[ -z $1 ]]; then
echo >&2 "You must provide an argument"
exit 1
fi
where=$1
if [[ ! -d $where ]]; then
echo >&2 "\`$where' is not a directory"
exit 1
fi
shopt -s nullglob
shopt -u dotglob
for i in "$where"/*/; do
if ! find "$where" -mindepth 1 -maxdepth 1 \! -name '.*' -type d \! -newer "$i" \! -samefile "$i" -print | read; then
"${serious[@]}" rm -rfv -- "$i"
if ((${#serious[@]})); then
echo "*** Nothing has been removed. Call with option --serious to really remove. ***"
fi
break
fi
done
Затем используйте, например, как
$ mkdir Test
$ cd Test
$ mkdir oldest; sleep 1; mkdir lots_of_dirs{1..10}
$ rm_oldest_dir .
rm -rfv -- ./oldest/
*** Nothing has been removed. Call with option --serious to really remove. ***
$ ls
lots_of_dirs1 lots_of_dirs3 lots_of_dirs6 lots_of_dirs9
lots_of_dirs10 lots_of_dirs4 lots_of_dirs7 oldest
lots_of_dirs2 lots_of_dirs5 lots_of_dirs8
$ rm_oldest_dir --serious .
removed directory: `./oldest'
$ ls
lots_of_dirs1 lots_of_dirs2 lots_of_dirs4 lots_of_dirs6 lots_of_dirs8
lots_of_dirs10 lots_of_dirs3 lots_of_dirs5 lots_of_dirs7 lots_of_dirs9
$
Аккуратно.
Затем вы можете поместить его в свой crontab как
0 0 * * * rm_oldest_dir --serious "/path with spaces/to/where/you/want"
Единственная атака на этот скрипт, о которой я могу думать, - это слишком много папок: глобализация в Bash не очень эффективна, а слишком много папок может привести к очень высокой загрузке ЦП в течение длительного времени и, в конечном итоге, закончиться прерыванием. Более сложный метод - это быстрая сортировка на основе файлов, но поскольку этот пост уже слишком длинный, я закончу его здесь.
Ура!
Я не знаю, действительно ли вторая часть этого ответа имеет место в научной фантастике. Если нет, дайте мне знать в комментариях или проголосуйте против этого сообщения!