Синтаксический вывод ls
является не надежным .
Вместо этого используйте, find
чтобы найти файлы и sort
упорядочить их по метке времени. Например:
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Что все это делает?
Сначала find
команды находят все файлы и каталоги в текущем каталоге ( .
), но не в подкаталогах текущего каталога ( -maxdepth 1
), затем распечатывают:
- Метка времени
- Пространство
- Относительный путь к файлу
- NULL символ
Отметка времени важна. Спецификатор %T@
формата для -printf
разбивки на T
, который указывает «Время последнего изменения» файла (mtime) и @
, который указывает «Секунды с 1970 года», включая доли секунды.
Пространство - это просто произвольный разделитель. Полный путь к файлу таков, что мы можем обратиться к нему позже, и символ NULL является терминатором, поскольку он является недопустимым символом в имени файла и, таким образом, позволяет нам точно знать, что мы достигли конца пути к файл.
Я включил 2>/dev/null
так, что файлы, к которым у пользователя нет прав доступа, исключаются, но сообщения об ошибках об их исключении исключаются.
Результатом find
команды является список всех каталогов в текущем каталоге. Список передается по трубопроводу, sort
которому поручено:
-z
Обрабатывайте NULL как символ конца строки вместо новой строки.
-n
Сортировать численно
Так как секунды с 1970 года всегда идут вверх, мы хотим файл, метка времени которого была наименьшим числом. Первым результатом sort
будет строка, содержащая метку времени с наименьшим номером. Осталось только извлечь имя файла.
Результаты find
, sort
трубопровод проходит через подмены процесса в while
котором он читается , как если бы это был файл на стандартный ввод. while
в свою очередь вызывает read
для обработки ввода.
В контексте read
мы устанавливаем IFS
переменную в ничего, что означает, что пробел не будет неправильно интерпретироваться как разделитель. read
сказано -r
, что блокирует расширение побега, и -d $'\0'
, что делает конец-строки разделителя NULL, соответствующий вывод из нашего find
, sort
трубопровода.
Первый фрагмент данных, представляющий самый старый путь к файлу, которому предшествуют его временная метка и пробел, считывается в переменную line
. Далее, подстановка параметров используется с выражением #*
, которое просто заменяет все символы от начала строки до первого пробела, включая пробел, ничем. Это удаляет метку времени модификации, оставляя только полный путь к файлу.
На этом этапе имя файла сохраняется, $file
и вы можете делать с ним все что угодно. Когда вы закончили делать что - то с $file
в while
цикл будет заявление и read
команда будет выполнена снова, извлекая следующий фрагмент и следующее имя файла.
Есть ли более простой способ?
Нет. Более простые способы глючат.
Если вы используете ls -t
и передаете по трубопроводу head
или tail
(или что-нибудь еще ), вы сломаете файлы с символами новой строки в именах файлов. Если у вас есть mv $(anything)
файлы с пробелами в имени, это приведет к поломке. Если вы mv "$(anything)"
затем файлы с завершающими символами новой строки в имени, это приведет к поломке. Если вы этого read
не -d $'\0'
сделаете, вы будете использовать файлы с пробелами в именах.
Возможно, в определенных случаях вы точно знаете, что более простой способ достаточен, но вы никогда не должны записывать подобные предположения в сценарии, если вы можете избежать этого.
Решение
#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Звоните как:
move-oldest /mnt/backup/ /var/log/foo/ 20
Чтобы переместить самые старые 20 файлов из /var/log/foo/
в /mnt/backup/
.
Обратите внимание, что я включаю файлы и каталоги. Для файлов только добавить -type f
к find
вызову.
Благодарность
Спасибо энзотибу и Павлу Танкову за улучшения этого ответа.