Проблемы с существующими ответами:
- невозможность обрабатывать имена файлов со встроенными пробелами или переводами строки.
- в случае решений, которые вызываются
rm
непосредственно в подстановке команд без кавычек ( rm `...`
), существует дополнительный риск непреднамеренного сглаживания.
- невозможность различать файлы и каталоги (т. е. если каталоги оказались в числе 5 самых последних измененных элементов файловой системы, вы фактически сохраните менее 5 файлов, и применение
rm
к каталогам не удастся).
Ответ wnoise решает эти проблемы, но решение является специфичным для GNU (и довольно сложным).
Вот прагматичное, POSIX-совместимое решение, которое поставляется только с одной оговоркой : оно не может обрабатывать имена файлов со встроенными символами новой строки - но я не считаю это реальной проблемой для большинства людей.
Для справки, вот объяснение того, почему вообще не очень хорошая идея разбирать ls
вывод: http://mywiki.wooledge.org/ParsingLs
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
Вышеупомянутое неэффективно , потому что xargs
должен вызывать rm
один раз для каждого имени файла.
Ваша платформа xargs
может позволить вам решить эту проблему:
Если у вас есть GNU xargs
, используйте -d '\n'
, что делает xargs
каждую входную строку отдельным аргументом, но передает столько аргументов, сколько поместится в командной строке одновременно :
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
-r
( --no-run-if-empty
) гарантирует, что rm
не вызывается, если нет ввода.
Если у вас есть BSD xargs
(в том числе в macOS ), вы можете использовать -0
для обработки NUL
ввода, разделенного разделителями, после первой трансляции символов новой строки в NUL
( 0x0
) chars., Который также передает (обычно) все имена файлов одновременно (также будет работать с GNU xargs
):
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
Объяснение:
ls -tp
печатает имена элементов файловой системы, отсортированные по тому, как недавно они были изменены, в порядке убывания (сначала самые последние измененные элементы) ( -t
), с каталогами, напечатанными с последующим, /
чтобы пометить их как таковые ( -p
).
grep -v '/$'
затем отсеивает каталоги из результирующего списка, пропуская -v
строки ( ), которые имеют конечный /
( /$
).
- Предостережение : поскольку символическая ссылка, которая указывает на каталог , технически сама по себе не является каталогом, такие символические ссылки не будут исключены.
tail -n +6
пропускает первые 5 записей в списке, фактически возвращая все, кроме 5 самых последних измененных файлов, если таковые имеются.
Обратите внимание, что для исключения N
файлов N+1
необходимо передать tail -n +
.
xargs -I {} rm -- {}
(и его вариации) затем вызывает rm
все эти файлы; если совпадений xargs
нет вообще, ничего не сделаю.
xargs -I {} rm -- {}
определяет местозаполнитель, {}
который представляет каждую строку ввода в целом , поэтому rm
он вызывается один раз для каждой строки ввода, но с именами файлов со встроенными пробелами, которые обрабатываются правильно.
--
во всех случаях гарантирует , что любые имена файлов , которые происходят , чтобы начать с -
не ошибаемся для опций по rm
.
Вариации на исходной задаче, в случае , если соответствующие файлы должны быть обработаны по отдельности или собран в массиве оболочки :
# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done
# One by one, but using a Bash process substitution (<(...),
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)
# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements
ls
не в текущем каталоге, пути к файлам будут содержать '/', что означает, чтоgrep -v '/'
ничего не будет совпадать. Я считаюgrep -v '/$'
, что вы хотите исключить только каталоги.grep
концептуальную команду более понятной. Однако обратите внимание, что описанная вами проблема не возникла бы с одним путем к каталогу; например,ls -p /private/var
все равно будет печатать только простые имена файлов. Только если вы передадите несколько аргументов файла (обычно через глобус), вы увидите фактические пути в выводе; Например,ls -p /private/var/*
(и вы также увидите содержимое соответствующих подкаталогов, если вы также не включили-d
).Удалите все кроме 5 (или любого другого числа) самых последних файлов в каталоге.
источник
ls -t
наls -td *.bz2
ls -t | awk 'NR>1'
(я хотел только самый последний). Спасибо!ls -t | awk 'NR>5' | xargs rm -f
если вы предпочитаете каналы, и вам нужно подавить ошибку, если нечего удалять.touch 'hello * world'
, это приведет к удалению абсолютно всего в текущем каталоге .Эта версия поддерживает имена с пробелами:
источник
(ls -t|head -n 5;ls)
это командная группа . Он печатает 5 самых последних файлов дважды.sort
соединяет одинаковые строкиuniq -u
удаляет дубликаты, поэтому остаются все файлы, кроме 5 самых последних.xargs rm
призываетrm
на каждом из них.--no-run-if-empty
вxargs
как в(ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rm
пожалуйста , обновите ответ.touch 'foo " bar'
скинет всю оставшуюся часть команды.xargs -d $'\n'
чем инъекционные кавычки в содержание, хотя NUL задающего входной поток (что требует использовать что - то другое , чемls
на самом деле делать справа) является вариантом идеально.Более простой вариант ответа thelsdj:
ls -tr отображает все файлы, сначала самые старые (сначала сначала -t, либо наоборот -r).
head -n -5 отображает все, кроме 5 последних строк (т.е. 5 новейших файлов).
xargs rm вызывает rm для каждого выбранного файла.
источник
-1
используется по умолчанию, когда вывод выполняется в конвейер, поэтому здесь это не обязательно. Это имеет гораздо более серьезные проблемы, связанные с поведением по умолчаниюxargs
при разборе имен с пробелами, кавычками и т. Д.--no-run-if-empty
не распознается в моей оболочке. Я использую Cmder на Windows.-0
Возможно, потребуется использовать эту опцию, если имена файлов могут содержать пробелы. Пока не проверял это. источникТребует GNU find для -printf, GNU sort для -z, GNU awk для "\ 0" и GNU xargs для -0, но обрабатывает файлы со встроенными символами новой строки или пробелами.
источник
awk
логики. Я пропускаю некоторые требования в вопросе ОП, которые делают это необходимым?while read -r -d ' '; IFS= -r -d ''; do ...
цикл оболочки - первое чтение завершается в пространстве, а второе переходит в NUL.sed -z -e 's/[^ ]* //; 1,5d'
- самое ясное. (или, может бытьsed -n -z -e 's/[^ ]* //; 6,$p'
.Все эти ответы терпят неудачу, когда есть каталоги в текущем каталоге. Вот что работает:
Это:
работает, когда есть каталоги в текущем каталоге
пытается удалить каждый файл, даже если предыдущий не может быть удален (из-за разрешений и т. д.)
терпит неудачу в безопасности , когда количество файлов в текущем каталоге избыточно и
xargs
будет нормально ввернуть Вас более года (-x
)не учитывает пробелы в именах файлов (возможно, вы используете не ту ОС?)
источник
find
вернет больше имен файлов, чем может быть передано в одной командной строкеls -t
? (Совет: вы получаете несколько прогоновls -t
, каждый из которых отсортирован только по отдельности, вместо того, чтобы иметь глобально правильный порядок сортировки; таким образом, этот ответ сильно нарушается при работе с достаточно большими каталогами).Список имен файлов по времени модификации, цитируя каждое имя файла. Исключить первые 3 (3 самых последних). Удалить оставшиеся.
РЕДАКТИРОВАТЬ после полезного комментария от mklement0 (спасибо!): Исправлен аргумент -n + 3, и обратите внимание, что это не будет работать должным образом, если имена файлов содержат символы новой строки и / или каталог содержит подкаталоги.
источник
-Q
Вариант , кажется, не существует на моей машине.-Q
. Да,-Q
это расширение GNU (вот спецификация POSIXls
). Небольшое предостережение (на практике редко является проблемой):-Q
кодирует встроенные символы новой строки в именах файлов как буквальные\n
, которыеrm
не распознаются. Чтобы исключить первые 3 ,xargs
аргумент должен+4
. И, наконец, предостережение, которое применимо и к большинству других ответов: ваша команда будет работать, как и предполагалось, только если в текущем каталоге нет подкаталогов .--no-run-if-empty
опцией:ls -tQ | tail -n+4 | xargs --no-run-if-empty rm
Игнорирование новых строк игнорирует безопасность и хорошее кодирование. У wnoise был единственный хороший ответ. Вот вариант его, который помещает имена файлов в массив $ x
источник
IFS
- в противном случае вы рискуете потерять конечный пробел из имен файлов. Можно включить это в команду чтения:while IFS= read -rd ''; do
"${REPLY#* }"
?Если в именах файлов нет пробелов, это будет работать:
Если имена файлов имеют пробелы, что-то вроде
Основная логика:
источник
while read
хитрость при работе с пробелами:ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
while IFS= read -r d
было бы немного лучше --r
предотвращает поглощение литералами обратной косой чертыread
иIFS=
предотвращает автоматическое усечение завершающего пробела.touch $'hello \'$(rm -rf ~)\' world'
; литеральные кавычки внутри имени файла будут противостоять литеральным кавычкам, которые вы добавляетеsed
, в результате чего выполняется код внутри имени файла.| sh
форме, которая имеет уязвимость инъекции оболочки).С зш
Предполагая, что вы не заботитесь о существующих каталогах, и у вас будет не более 999 файлов (выберите большее число, если хотите, или создайте цикл while).
В
*(.om[6,999])
, в.
файлах значит,o
средство порядок сортировки вверх,m
средства по дате модификации (положитьa
на время доступа илиc
для изменения инода), то[6,999]
выбирает диапазон файла, поэтому не Р.М. 5 первых.источник
om
работать классификатор glob сортировки ( ) (любая попытка сортировки, которую я пробовал, не дала эффекта - ни на OSX 10.11.2 (пробовал с zsh 5.0.8 и 5.1.1) ни на Ubuntu 14.04 (zsh 5.0.2)) - чего мне не хватает ?. Что касается диапазона конечной точки: нет необходимости в жесткий код, просто использовать-1
для обозначения последней записи и , таким образом , включают в себя все остальные файлы:[6,-1]
.Я понимаю, что это старая ветка, но, возможно, кому-то это поможет. Эта команда найдет файлы в текущем каталоге:
Это немного более надежно, чем некоторые из предыдущих ответов, поскольку позволяет ограничить область поиска файлами, соответствующими выражениям. Сначала найдите файлы, соответствующие любым условиям, которые вы хотите. Распечатайте эти файлы с отметками времени рядом с ними.
Затем отсортируйте их по временным меткам:
Затем уберите 4 самых последних файла из списка:
Возьмите 2-й столбец (имя файла, а не метку времени):
А затем оберните все это в утверждение for:
Это может быть более многословная команда, но мне повезло больше, когда я смог нацелиться на условные файлы и выполнить с ними более сложные команды.
источник
нашел интересный cmd в Sed-Onliners - удалите последние 3 строки - и он идеально подходит для другого способа облысения кошки (хорошо, нет), но идея:
источник
Удаляет все, кроме 10 последних (большинство последних) файлов
Если менее 10 файлов, ни один файл не будет удален, и у вас будет: error head: недопустимое количество строк - 0
Считать файлы с помощью bash
источник
Мне нужно было элегантное решение для busybox (маршрутизатора), все решения xargs или array были для меня бесполезны - такой команды там не было. find и mtime не правильный ответ, так как речь идет о 10 пунктах и не обязательно 10 днях. Ответ Эспо был самым коротким и чистым и, вероятно, самым неожиданным.
Ошибка с пробелами и когда файлы не должны быть удалены, просто решаются стандартным способом:
Немного больше образовательной версии: мы можем сделать все это, если будем использовать awk по-другому. Обычно я использую этот метод для передачи (возврата) переменных из awk в sh. Поскольку мы все время читаем, что не может быть сделано, я позволю себе не согласиться: вот метод.
Пример для файлов .tar без проблем с пробелами в имени файла. Чтобы проверить, замените «rm» на «ls».
Объяснение:
ls -td *.tar
перечисляет все файлы .tar, отсортированные по времени. Чтобы применить ко всем файлам в текущей папке, удалите часть "d * .tar"awk 'NR>7...
пропускает первые 7 строкprint "rm \"" $0 "\""
конструирует строку: rm "имя файла"eval
выполняет этоПоскольку мы используем
rm
, я бы не использовал вышеуказанную команду в сценарии! Более разумное использование:В случае использования
ls -t
команды не навредит такие глупые примеры как:touch 'foo " bar'
иtouch 'hello * world'
. Не то чтобы мы когда-либо создавали файлы с такими именами в реальной жизни!Примечание. Если бы мы хотели передать переменную в sh таким образом, мы бы просто изменили print (простая форма, без пробелов):
установить переменную
VarName
в значение$1
. Несколько переменных могут быть созданы за один раз. ЭтоVarName
становится обычной переменной sh и впоследствии может быть использовано в скрипте или оболочке. Итак, чтобы создать переменные с помощью awk и вернуть их обратно в оболочку:источник
источник
xargs
без-0
или по минимуму-d $'\n'
ненадежен; посмотрите, как это происходит с файлом с пробелами или кавычками в имени.Я сделал это в скрипт оболочки bash. Использование:
keep NUM DIR
где NUM - это количество файлов для хранения, а DIR - каталог для очистки.источник