
Сегодня во время обеденного перерыва я написал bash-скрипт, который находит в каталоге файлы без расширений и добавляет к ним расширение.
Скрипт получился довольно длинным, так как я добавил кучу флагов и прочего, вроде выбора каталога и указания, копировать или перезаписывать файл, но основную его функциональность можно воспроизвести просто так:
#recursively find files in current directory that have no extension
for i in $(find . -type f ! -name "*.*"); do
#guess that extension using file
extfile=$(file --extension --brief $i)
#select the first extension in the event file spits something weird (e.g. jpeg/jpe/jfif)
extawk=$(echo $extfile | awk -F/ '{print $1}')
#copy the file to a file appended with the extension guessed from the former commands
cp -av $i $i.$extawk
done
В моем реальном сценарии все немного аккуратнее — я просто хотел разделить команды, чтобы иметь возможность комментировать, почему я что-то делаю.
Мой вопрос: использование find
в сочетании с file
тем способом, который я выбрал, вероятно, не самый надежный способ сделать это — в чемлучшийспособ рекурсивного угадывания и добавления расширений для большой группы различных типов файлов в нескольких каталогах?
решение1
for x in $(find …)
терпит неудачу симена файлов, содержащие пробелы (обычно) или подстановочные знаки (нечасто). Никогда не анализируйте вывод find
. Используйте -exec
.
Зш'szmv
удобен для массового переименования.
Давайте создадим команду zmv, которая делает то, что вам нужно. Сначала давайте создадим шаблон поиска:
autoload zmv
zmv -C -o -a -n -Q '(*/)#^*.*(.)' …
-C
приводит к копированию файлов вместо их перемещения.-o -a
переходит-a
кcp
.-n
означает не действовать, просто печатать то, что будет сделано. Удалите его, как только будете довольны. Замените его на ,-v
если хотите действовать, но также печатать то, что делается.-Q
позволяетквалификаторы globв узоре.(*/)#
соответствует нулю или более каталогов. Он использует#
оператор глоб(extended_glob
всегда включено в zmv).^*.*
использует^
оператор glob для сопоставления файлов,.
в имени которых нет символа .(.)
— это квалификатор glob, ограничивающий совпадения обычными файлами.…
будет заменен текстом замены. Это может использоваться$f
для ссылки на исходное имя.
zmv
вычисляет все заменяющие имена перед выполнением любой замены и выдает сообщение, если заменяющее имя уже существует или если есть конфликты. Файлы, для которых заменяющее имя идентично исходному, пропускаются.
Теперь давайте создадим текст замены. Мы будем использовать многорасширение параметрафункции.
- Запросите
file
продление:$(file --extension --brief -- $f)
- Добавьте a
.
в качестве подготовки к замене:$(echo -n .; file --extension --brief -- $f)
(Это можно также сделать с помощью расширения параметра:${:-.$(…)}
.) - Если предложено несколько расширений (разделенных косой чертой), оставьте только первое:
${$(echo -n .; file --extension --brief -- $f)%%/*}
- Если предложенное расширение пустое или
???
, откажитесь (замените.
или.???
на пустую строку):${${$(echo -n .; file --extension --brief -- $f)%%/*}:#.(|\?\?\?)}
- Добавить добавленное расширение к
$f
(исходному имени). Если то, что мы добавляем, пусто, файл останется нетронутым.
Получившаяся команда:
zmv -C -o -a -n -Q '(*/)#^*.*(.)' '$f${${$(echo -n .; file --extension --brief -- $f)%%/*}:#.(|\?\?\?)}'
Это немного загадочно, и вы можете предпочесть поместить код для генерации замены в функцию и использовать zmv … '$(add_extension $f)'
.
решение2
Я думаю, что наиболее эффективным способом является сравнение MIME-типов файла с базой данных, расположенной по адресу /usr/share/mime/globs
.
- шарикив Linux естьрасширение файла. Приведен пример, вывод изфайл globs
application/x-mswinurl:*.url
text/x-mrml:*.mrl
text/x-erlang:*.erl
audio/x-pn-audibleaudio:*.aa
application/x-bzip-compressed-tar:*.tbz2
application/x-netshow-channel:*.nsc
application/x-hdf:*.h4
application/pgp-keys:*.key
text/x-idl:*.idl
text/x-chdr:*.h
application/vnd.ms-powerpoint.presentation.macroEnabled.12:*.pptm
application/vnd.ms-powerpoint.presentation.macroEnabled.12:*.pptm
application/vnd.visio:*.vsd
application/x-hdf:*.h5
video/vnd.mpegurl:*.m4u
- после описания примера типа -->
text/x-erlang
, он сообщает Linux, что все файлы следует идентифицировать*.
какЭрлангс расширением.erl
[glob], поэтому -->*.erl
- вы можете добавить свои собственные расширения, которые будут учтены в
/etc/magic
файле
поэтому выполним команду:
mimetype -bM file
b
аргумент в пользу того, чтобы просто показать вамtype-app/extension
(кратко)M
аргумент означаетМагияэто способ, которым Linux проверяет файл в байт-коде, шестнадцатеричном и двоичном формате, чтобы убедиться, что файлы действительно являются тем, чем они себя выдают.mimetypeне возвращает,
/jpg/png/webp
возвращает только один тип, и он короче, чемfile --mime-type file
Возврат:
image/webp
последние мысли
mimetype
лучше всего работает сдвоичные файлыкак PDF, изображения, видео. Это потому, что он может проверить двоичный файл, вместо этого, text plain
это просто то, что вам нужно идентифицировать с чем-то, и это более сложно, поэтому текстовые редакторы могут распознавать различные языки программирования, ему нужна помощь от пользователя и серверного языка для каждого языка программирования.
для рекурсии, я думаюдеревоЭто хорошо:
tree -FIi '*.*' | grep -v /$
- аргумент
F
— добавить/
[слэш] к каталогам, например,folder
→folder/
- аргумент
I
— выбрать противоположность шаблону*.*
[это означает выбрать все файлы с расширением], поэтому противоположность — это не расширение - аргумент
i
— удалить пробелы из вывода дерева grep -v
чтобы выбрать обратный порядок, поэтому вы добавляете/
аргумент -F кдеревокоманду в начале, чтобы вы могли удалить каталоги и получить только файлы, с расширением/$
.
Подробнее здесьтипы пантомимы