У n
меня в каталоге есть несколько файлов ../in/
. Из этих файлов я хочу обработать самый старый файл и отправить его в ../complete/
каталог. Остальные файлы отправляются в каталог ../error/
. Может кто-нибудь помочь?
решение1
Чтобы скопировать самый старый файл в ../complete
:
cp -v "$(find ../in -maxdepth 1 -type f -printf '%T@ %p\0' | sort -zn | \
sed -zn '1s/[0-9,\.]\+ //p')" ../complete
Чтобы скопировать все, кроме самых старых, в ../error
:
find ../in -maxdepth 1 -type f -printf '%T@ %p\0' | sort -zn | \
sed -zn '2,$s/[0-9,\.]\+ //p' | xargs -0 cp -vt ../error
Объяснение:
find
-maxdepth 1
спускается только на 1 уровень; поэтому он не будет выполнять рекурсивный поиск в каталоге.-type f
только соответствующие файлы.-printf '...'
пользовательский формат вывода.%T@
выдает время последнего изменения файла в виде временной метки,%p
— имя файла. Обратите внимание на\0
в конце. Файлы печатаются с разделителями в виде нулевого байта, а не с разделителями в виде новой строки, поскольку символ новой строки, вероятно, может быть символом в имени файла, что приведет к сбою команды.
sort -zn
это прочитает вывод, разделенный нулевым байтом (-z
), и отсортирует его по числовому значению (-n
).sed -zn
считывает вывод, разделенный нулевым байтом.1
будет соответствовать только первой строке (первая команда) и2,$
всем остальным (вторая команда)s/[0-9,\.]\+ //p
удалит начальную временную метку из выходных данных.
- Только из второй команды:
xargs -0 cp -vt ../error
обрабатывает выходные данные, разделенные нулевым байтом (-0
), и вызываетcp -vt ../error
все входные данные, которые копируют файлы.
Пример вывода:
$ ls -lt ../in/
total 0
-rw-r--r-- 1 user user 0 Jun 24 08:18 file2
-rw-r--r-- 1 user user 0 Jun 24 08:18 file3
-rw-r--r-- 1 user user 0 Jun 24 08:18 file4
-rw-r--r-- 1 user user 0 Jun 24 08:18 file5
-rw-r--r-- 1 user user 0 Jun 24 08:17 file_oldest
$ cp -v "$(...)" ../complete
'../in/file_oldest' -> '../complete/file_oldest'
$
$ find ... | xargs ... ../error
'../in/file2' -> '../error/file2'
'../in/file3' -> '../error/file3'
'../in/file4' -> '../error/file4'
'../in/file5' -> '../error/file5'
решение2
Чтобы найти самый старый файл и переместить его в нужный каталог, используйте:
# cd to ../in/
$ ls -lt | grep -v '^d' | tail -1 | awk '{print $NF}' | xargs -I '{}' mv '{}' ../complete/
Теперь, когда ваш самый старый файл перемещен, вы можете переместить все файлы в../error/
$ mv -- * ../error/
Последующее необходимо, если у вас есть имена файлов/каталогов, начинающиеся --
с .mv
-
решение3
Опираясь наОтвет Рахула:
$ ls -ltr | grep -v '^d' | awk 'NR==2 {print $NF; exit}' | xargs -I '{}' mv -- '{}' ../complete/
Примечания:
- Вы говорите, что хотите что-то сделать с «самым старым файлом». Все ответы предполагают, что вы имеете в виду наименее недавно измененный. Если вы имеете в виду наименее недавноизмененный, так и скажите; ответы будут немного отличаться. Если вы имеете в виду наименее недавносозданный, то, что вы хотите, практически невозможно в Unix.
ls -lt
ls -ltr
( то , что использовал Рахул) выводит список текущего каталога в длинном формате в порядке даты/времени изменения, начиная с самого нового и заканчивая самым старым. рнаоборот: сначала самые старые, а последние самые новые.- Если у вас есть файлы, имена которых начинаются с точки (
.
), добавьтеA
параметр кls
; например,ls -ltrA
. (Порядок параметров не имеет значения; вы можете использоватьls -ltAr
,ls -lAtr
, или дажеls -ltr -A
, если хотите.) - Я солгал. Длинный
ls
список каталога всегда имеетtotal
строку в начале. Вls -ltr
, самая старая запись находится навторойлиния. grep -v '^d'
отфильтровывает каталоги (если таковые имеются).awk 'NR==2 { print $NF; exit }'
печатает последнее поле (имя файла*) из второй строки (самый старый файл) и затем выходит.- Это на один процесс меньше, чем
tail -1 | awk '{print $NF}'
. - Это может работать быстрее, поскольку при
awk
выходеls
может также выйти и вам не придется создавать список для всего каталога. Вы можете устранить это,
grep
выполнив следующие действия:ls -ltr | awk '! /^d/ { count++ } count==2 { print $NF; exit }'
но я бы не советовал. Слишком сложные скрипты трудно поддерживать.
- Это на один процесс меньше, чем
- После
--
mv
вxargs
команденеобходимо, если у вас есть имена файлов, начинающиеся с-
. Вам на самом деле не нужно
xargs
. Вы можете упростить команду выше до$ mv -- $(ls -ltr | grep -v '^d' | awk 'NR==2 { print $NF; exit }') ../complete/
________
* Как и в ответе Рахула, это не сработает, если имя файла содержит пробелы или табуляции, и еще хуже, если оно содержит переводы строк.
Если у вас есть файлы, имена которых начинаются с точки (.
), сделайте shopt -s dotglob
перед выполнением
$ mv -- * ../error/