Удалите все файлы, кроме самого нового 3 в скрипте bash
Вопрос: Как вы удаляете все файлы в каталоге, кроме самого нового 3?
Найти новейшие 3 файла очень просто:
ls -t | head -3
Но мне нужно найти все файлы, кроме 3 новейших файлов. Как мне это сделать, и как я могу удалить эти файлы в одной строке, не делая ненужного цикла for для этого?
Для этого я использую Debian Wheezy и скрипты bash.
7 ответов
Это будет список всех файлов, кроме самых новых трех:
ls -t | tail -n +4
Это удалит эти файлы:
ls -t | tail -n +4 | xargs rm --
Это также перечислит точечные файлы:
ls -At | tail -n +4
и удалите с помощью файлов точек:
ls -At | tail -n +4 | xargs rm --
Но будьте осторожны: разбор ls
может быть опасно, когда имена файлов содержат забавные символы, такие как переводы строки или пробелы. Если вы уверены, что ваши имена файлов не содержат забавных символов, то анализируйте ls
вполне безопасно, даже более того, если это единовременный сценарий.
Если вы разрабатываете сценарий для повторного использования, то вам, безусловно, не следует анализировать вывод ls
и используйте методы, описанные здесь: http://mywiki.wooledge.org/ParsingLs
Это сочетание ответа Кевинга и Анубхавы. Оба решения не работают для меня. Поскольку я искал сценарий, который должен запускаться каждый день для резервного копирования файлов в архиве, я хотел избежать проблем с ls
(кто-то мог сохранить какой-нибудь забавный файл с именем в моей папке для сохранения резервных копий). Поэтому я изменил упомянутые решения в соответствии со своими потребностями. Решение Ceving удаляет три новейших файла - не то, что мне было нужно, и меня об этом спросили.
Мое решение удаляет все файлы, кроме трех новейших файлов.
find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g |
head -n -3 |
cut -d $'\t' -f 2- |
xargs rm
Некоторое объяснение:
find
перечисляет все файлы (не каталоги) в текущей папке. Они распечатываются с отметками времени.sort
сортирует строки по отметке времени (самая старая сверху).head
печатает верхние строки, до последних 3 строк.cut
удаляет временные метки.xargs
работает rm
для каждого выбранного файла.
Для проверки моего решения:
(
touch -d "6 days ago" test_6_days_old
touch -d "7 days ago" test_7_days_old
touch -d "8 days ago" test_8_days_old
touch -d "9 days ago" test_9_days_old
touch -d "10 days ago" test_10_days_old
)
Это создает 5 файлов с разными временными метками в текущей папке. Сначала запустите это и код для удаления, чтобы проверить код.
Следующее выглядит немного сложным, но очень осторожно, чтобы быть правильным, даже с необычными или намеренно злонамеренными именами файлов. К сожалению, это требует инструментов GNU:
count=0
while IFS= read -r -d ' ' && IFS= read -r -d '' filename; do
(( ++count > 3 )) && printf '%s\0' "$filename"
done < <(find . -maxdepth 1 -type f -printf '%T@ %P\0' | sort -g -z) \
| xargs -0 rm -f --
Объясняя, как это работает:
- Найти выбрасывает
<mtime> <filename><NUL>
для каждого файла в текущем каталоге. sort -g -z
выполняет общую (с плавающей точкой, в отличие от целочисленного) числовую сортировку на основе первого столбца (времен) со строками, разделенными NUL.- Первый
read
вwhile
цикл удаляет mtime (больше не требуется послеsort
готово). - Второй
read
вwhile
Цикл читает имя файла (работает до NUL). - Цикл увеличивает, а затем проверяет счетчик; если состояние счетчика указывает на то, что мы пропустили первоначальный пропуск, то мы печатаем имя файла, разделенное NUL.
xargs -0
затем добавляет это имя файла в список argv, который он собирает для вызоваrm
с.
ls -t | tail -n +4 | xargs -I {} rm {}
Если вы хотите 1 вкладыш
Не использовать ls -t
поскольку это небезопасно для имен файлов, которые могут содержать пробелы или специальные символы глобуса.
Вы можете сделать это, используя все gnu
На основе утилиты для удаления всех, кроме 3-х новейших файлов в текущем каталоге:
find . -maxdepth 1 -type f -printf '%T@\t%p\0' |
sort -z -nrk1 |
tail -z -n +4 |
cut -z -f2- |
xargs -0 rm -f --
В зш:
rm /files/to/delete/*(Om[1,-4])
Если вы хотите включить точечные файлы, замените часть в скобках на (Om[1,-4]D)
,
Я думаю, что это работает правильно с произвольными символами в именах файлов (только что проверено с новой строкой).
Пояснение: в скобках указаны Glob Qualifiers. O
означает "упорядочить по убыванию", m
означает mtime (см. man zshexpn
для других ключей сортировки - большая справочная страница; поиск "быть отсортированным"). [1,-4]
возвращает только совпадения с единичным индексом 1 - (последний + 1 - 4) (обратите внимание на -4
для удаления всех, кроме 3).
ls -t | tail -n +4 | xargs -I {} rm {}
Ответ Майкла Баллента лучше всего работает как
ls -t | tail -n +4 | xargs rm --
выведите мне ошибку, если у меня меньше 3 файлов
Рекурсивный скрипт с произвольным количеством файлов для каждого каталога
Также обрабатывает файлы / каталоги с пробелами, новой строкой и другими нечетными символами
#!/bin/bash
if (( $# != 2 )); then
echo "Usage: $0 </path/to/top-level/dir> <num files to keep per dir>"
exit
fi
while IFS= read -r -d $'\0' dir; do
# Find the nth oldest file
nthOldest=$(find "$dir" -maxdepth 1 -type f -printf '%T@\0%p\n' | sort -t '\0' -rg \
| awk -F '\0' -v num="$2" 'NR==num+1{print $2}')
if [[ -f "$nthOldest" ]]; then
find "$dir" -maxdepth 1 -type f ! -newer "$nthOldest" -exec rm {} +
fi
done < <(find "$1" -type d -print0)
Доказательство концепции
$ tree test/
test/
├── sub1
│ ├── sub1_0_days_old.txt
│ ├── sub1_1_days_old.txt
│ ├── sub1_2_days_old.txt
│ ├── sub1_3_days_old.txt
│ └── sub1\ 4\ days\ old\ with\ spaces.txt
├── sub2\ with\ spaces
│ ├── sub2_0_days_old.txt
│ ├── sub2_1_days_old.txt
│ ├── sub2_2_days_old.txt
│ └── sub2\ 3\ days\ old\ with\ spaces.txt
└── tld_0_days_old.txt
2 directories, 10 files
$ ./keepNewest.sh test/ 2
$ tree test/
test/
├── sub1
│ ├── sub1_0_days_old.txt
│ └── sub1_1_days_old.txt
├── sub2\ with\ spaces
│ ├── sub2_0_days_old.txt
│ └── sub2_1_days_old.txt
└── tld_0_days_old.txt
2 directories, 5 files
Как продолжение ответа flohail. Если вы хотите удалить все папки, кроме трех новейших папок, используйте следующее:
find . -maxdepth 1 -mindepth 1 -type d -printf '%T@\t%p\n' |
sort -t $'\t' -g |
head -n -3 |
cut -d $'\t' -f 2- |
xargs rm -rf
В -mindepth 1
игнорирует родительскую папку и -maxdepth 1
вложенные папки.
Это использует find
вместо ls
с преобразованием Шварца.
find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g |
tail -3 |
cut -d $'\t' -f 2-
find
ищет файлы и украшает их отметкой времени и использует табулятор для разделения двух значений. sort
разбивает ввод с помощью табулятора и выполняет обычную числовую сортировку, которая правильно сортирует числа с плавающей запятой. tail
должно быть очевидным и cut
undecorates.
Проблема с украшениями в целом заключается в том, чтобы найти подходящий разделитель, который не является частью ввода, имен файлов. В этом ответе используется символ NULL.
Ниже сработало для меня: (Ура)
rm -rf $(ll -t | tail +5 | awk '{ print $9}')