
Итак, скажем, у меня есть каталог с кучей файлов типа g.txt, где g.txt последний раз был изменен, скажем, 20 июня 2012 года.
Как мне переименовать все файлы (например, g.txt) и добавить в конец дату последнего изменения — 20 июня 2012 года?
решение1
Вот версия однострочной фразы Голдшрафе:
не использует статистику
работает с более ранними версиями GNU date
корректно обрабатывает любые пробелы в именах файлов
также справляется с именами файлов, начинающимися с тире
for f in *; do mv -- "$f" "$f-$(date -r "$f" +%Y%m%d)"; done
Обновление 2021-03-20
Мой ответ на этот вопрос беспокоил меня годами (ну, только когда я вспоминаю о нем - в такие дни, как сегодня, когда он получает еще один голос), поэтому я наконец-то обновляю его. Мой первоначальный ответ выше все еще работает, но обновленный ответ ниже лучше.
НравитьсялюбойОперация пакетного переименования файлов должна быть выполнена с помощьюперл rename
утилита, а не какая-то неуклюжая оболочка для цикла.
Утилита Perl rename по сути является специализированным языком сценариев, который позволяет использовать ЛЮБОЙ код Perl для переименования файлов, от простых s/search/replace/
операций регулярных выражений (достаточно для большинства задач переименования) до сложных многострочных сценариев.
например
rename -n 'BEGIN {use Date::Format};
die $! unless -f $_;
next if (m/-\d{8}$/);
my $ts=(stat($_))[9];
my $dt=time2str("%Y%m%d",$ts);
s/$/-$dt/;' *.txt
Для этого требуются только perl и Date::Format
модуль (модуль, который настолько полезен, что его следует установить на любой системе с perl. По моему мнению, он, вместе с модулем автора Грэма Барра Date::Parse
,долженбыть частью базовой библиотеки модулей Perl, но это не так, поэтому вам придется установить его с помощью cpan
пакета дистрибутива, например пакета Debian libtimedate-perl
).
Кстати, этот скрипт пропускает любой файл, который выглядит так, будто в конце имени файла уже есть дата (т. е. 8 цифр).
или, для более сложной версии, которая помещает дату перед суффиксом файла (если таковой имеется):
rename -n 'BEGIN {use Date::Format; use File::Basename};
die $! unless -f $_;
my ($filename,$dirs,$suffix) = fileparse($_,qr/\.[^.]*/);
next if (m/-\d{8}${suffix}$/);
my $ts=(stat($_))[9];
my $dt=time2str("%Y%m%d",$ts);
s/${suffix}$/-${dt}${suffix}/;' *.txt
Эта версия не имеет дополнительных требований, поскольку File::Basename
модуль был включен в качестве стандартного основного модуля Perl с тех пор, как я себя помню (как минимум десять лет, а возможно и больше).
Примечание:оба rename
скрипта выше используют опцию rename -n
(она же --nono
) "dry-run", чтобы результаты можно было протестировать/смоделировать перед применением. Удалите -n
(или замените на -v
для подробного вывода), когда вы будете уверены, что он делает то, что вам нужно.
Также обратите внимание:Как и любой другой rename
скрипт Perl, он может переименовывать имена файлов, указанные в командной строке и/или из стандартного ввода. Например, чтобы переименовать все файлы .txt в текущем каталоге и всех подкаталогах:
find . -type f -iname '*.txt' -print0 |
rename -0 --nofullpath -n '..........'
Кстати, я использовал здесь опцию rename
--nofullpath (она же -d
, --filenmame
, --nopath
), чтобы гарантировать, что она переименует только часть имени файла из всех найденных путей к файлам. В этом конкретном случае это не нужно (потому что примеры сценариев переименования изменяют только конец имени файла), но обычно это хорошая идея, когда вы не хотите переименовывать путь и имя файла (например, сценарий переименования, например, 's/ //g'
удаляющий пробелы из имен файлов, попытается удалить все пробелы в пути и в имени файла без --nofullpath
, что, вероятно, приведет к сбою с ошибкой).
Окончательно:не путайте скрипт perl rename (он же File::Rename
, или иногда называемый prename
в Fedora и RedHat, или perl-rename
) с любой другой программой под названием rename. ТолькоЭта утилита переименования на основе Perl может переименовывать файлы, используя произвольный код Perl, как показано выше. Любая другая утилита переименования будет иметь другие возможности и другие и несовместимые параметры командной строки.
Вы можете проверить, установлен ли у вас правильный rename:
$ rename -V
/usr/bin/rename using File::Rename version 1.13, File::Rename::Options version 1.10
Исполняемый файл может называться prename
или , perl-rename
или file-rename
в вашей системе, поэтому попробуйте -V
использовать эти имена и измените приведенные выше примеры, чтобы использовать правильное имя исполняемого файла.
решение2
Быстрый и грязный однострочный Bash-код для переименования всех (подстановочных) файлов в текущем каталоге из filename.txt
в filename.txt-20120620
:
for f in *; do mv -- "$f" "$f-$(stat -c %Y "$f" | date +%Y%m%d)"; done
Я уверен, что предприимчивый фанат Bash найдет какой-нибудь крайний случай, чтобы его сломать. :)
Очевидно, что это не делает желаемых вещей, таких как проверка того, есть ли в конце файла что-то похожее на дату.
решение3
Обязательный однострочник zsh (не считая единовременной загрузки дополнительных компонентов):
zmodload zsh/stat
autoload -U zmv
zmv -n '(*)' '$1-$(stat -F %Y%m%d +mtime -- $1)'
Мы используем stat
встроенный изzsh/stat
модуль, иzmv
функция переименования файлов. А вот дополнительная, которая помещает дату перед расширением, если оно есть.
zmv -n '(*)' '$1:r-$(stat -F %Y%m%d +mtime -- $1)${${1:e}:+.$1:e}'
решение4
Вот версия однострочника cas (основанная на однострочнике goldschrafe), расширеннаяидемпотентность,
т.е. расширено для добавления префикса к файлам с датой, временем исделать это только для тех, у кого еще нет префикса даты и времени.
Вариант использования: полезно, если вы добавляете новые файлы в каталог и хотите добавить префикс даты и времени к тем, у которых его еще нет.
for f in * ; do
if [[ "$f" != ????-??-??' '??:??:??' - '* ]]; then
mv -v -- "$f" "$(date -r "$f" +%Y-%m-%d' '%H:%M:%S) - $f" ;
fi;
done