Переименование файлов и каталогов.

Переименование файлов с помощью команды iconv

Часто бывает, что файлы упакованы в архив не в той кодировке. Сами файлы можно с лёгкостью перекодировать в нормальный (для нас) вид, но что делать с именами? Вот например я скачал множество интересной документации с сайта http://wiki.kryukov.biz/, однако автор уж очень любит называть файлы по-русски, да ещё со всякими двоеточиями и пробелами (чего делать НЕ следует). Возможно конечно это и не автор, а его викопедичный движок, но меня это конечно всё равно не радует.

Рисунок 4.13. Каталог с файлами имена которых в UTF-8 при кодировке KOI8-R

Каталог с файлами имена которых в UTF-8 при кодировке KOI8-R


Этот рисунок хотя и похож на предыдущий случай (напомню, там я не смог восстановить имена, и переименовал файлы по имени их inode), однако довольно сильно отличается: Во первых здесь нет вопросительных знаков, а значит все символы сохранились, и их можно восстановить. Во вторых тут есть ещё и проценты, про них после. Выполним:

$ ls | iconv -f utf-8 -c
Сл%83жебна%8F:SpecialPages
Сл%83жебна%8F:UserLogin
Сл%83жебна%8F:WhatLinksHere/
Создание_кл%8E%87ей_и_%81е%80%82и%84ика%82ов
Спе%86иал%8Cн%8Bе_п%80ава
С%81%8Bлки
С%82анда%80%82н%8Bй_ввод,_в%8Bвод_и_в%8Bвод_о%88ибки
С%82%80%83к%82%83%80и%80ованн%8Bе_%82ек%81%82ов%8Bе_%84айл%8B
Тек%81%82ов%8Bе_%80едак%82о%80%8B
Тип%8B_%84айлов
Тип%8B_%84айлов%8B%85_%81и%81%82ем
У%81%82ановка_Cyrus-imapd_в_Slackware_Linux
У%81%82ановка_Postfix_в_Slackware_Linux
У%81%82ановка_Postfix_из_и%81%85одн%8B%85_кодов
У%87а%81%82ник:Artur
У%87а%81%82ник:Frantony
У%87а%81%82ник:Svasilie
Файл:Book-02-1.png
Файл:Flag_ru.jpg
Файл:Mail-schema.jpg
Файл:Postfix_arch.gif
Файл_group

Вот, стало всё более читаемо, но остались некоторые непонятки. Оказывается, уже в изначальном каталоге некоторые русские символы сменились на код %XX, наверное это произошло при сохранении страницы программой wget. Как-бы там не было, мы можем вернуть символы обратно sed-командой s.

Другая проблема заключается в том, что не все команды и не всегда принимают такие кривые параметры, например если передать имя из буфера sed прямо в команду, а потом её выполнить, файл просто не будет найден. Необходимо пойти более прямым путём: я передавал имя прямо внутри утилиты find с опцией -exec. Думаю это самый надёжный путь.

Сначала найдём все файлы и их inode командой

$ find . ! -path "./ren/*" -printf "%i %p\n"

Я решил сделать копию нашего каталога (.) внутри него-же в подкаталоге ./ren/, потому мне потребовалось его исключить. Затем я просто вывожу в поток кривые имена и inode файлов.

Подсказка

Наверное будет быстрее и проще прямо после этого сконвертировать кривые имена засунув в конвейер iconv, однако я сделал это внутри sed.

Предостережение

Следует учесть, что команда find находит не только файлы, но и каталоги, однако, эти каталоги находятся и переименовываются раньше, чем содержащиеся в них файлы. Эти файлы переписываются уже внутрь переименованного каталога.

Далее поток с битыми именами поступает в sed-скрипт:

Пример 4.16. Переименование файлов командой iconv.

#!/bin/sed -rf

s/.*/echo -e '&' | iconv -f utf-8 -c/e

s/%80/р/g
s/%81/с/g
s/%82/т/g
s/%83/у/g
s/%84/ф/g
s/%85/х/g
s/%86/ц/g
s/%87/ч/g
s/%88/ш/g
s/%89/щ/g
s/%8A/ъ/ig
s/%8B/ы/ig
s/%8C/ь/ig
s/%8D/э/ig
s/%8E/ю/ig
s/%8F/я/ig

s/%90/А/g
s/%91/Б/g
s/%92/В/g
s/%93/Г/g
s/%94/Д/g
s/%95/Е/g
s/%96/Ж/g
s/%97/З/g
s/%98/И/g
s/%99/Й/g
s/%9A/К/ig
s/%9B/Л/ig
s/%9C/М/ig
s/%9D/Н/ig
s/%9E/О/ig
s/%9F/П/ig

s/\\/\\\\/g
s/'/\\'/g

s|^([0-9]+) \./(.*)|find -inum \1 -exec cp -v -r {} 'ren/\2' \\;|e


Замечание

Кстати, новые файлы ren/* всё равно переносятся в ren/ren/, однако это не слишком мешает (они переносятся все сразу, без переименования).

Кстати, получившиеся копия всё равно не работает, точнее смотреть её можно, но по ссылкам не попрыгать. Причина в самих ссылках:

href="/wiki/%D0%9E%D0%B3%D0%BB%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5"

Такие ссылки можно исправить следующим скриптом:

#!/bin/sed -rf

s/charset=UTF-8/charset=KOI8-R/

/href="[^"]*%D[01]%[89A-F][0-9A-F][^"]*"/ {
	s//\n&\n/
	h
	s/.*\n([^\n]+)\n.*/\1/
	s/%D([01])%([89A-F][0-9A-F])/\\xD\1\\x\2/g
	s/.*/echo -e \x27&\x27/e
	s|^href="/wiki/|href="|
	G
	s/(.*)\n(.*)\n(.*)\n(.*)/\2\1\4|/
}

После чего весь файл нужно сконвертировать в KOI8-R.

Замечание

Логика работы этого скрипта следующая: конструкция «%D0%A4» меняется на «\xD0\xA4». Затем она выводится в буфер командой echo. При этом «\xD0\xA4» меняется на 2 байта, которые в кодировке UTF-8 соответствуют русской букве «Ф». Причём данный скрипт работает даже если у вас локаль не UTF, или вообще нерусская. (кстати, если у вас всё-же UTF - то этот скрипт может и не сработать из-за несимволов).

Переименование каталогов из кодировки KOI8-R в UTF-8 (тоже iconv).

Свой тестовый комп я перевёл с Slacky 10 на 13ю. Кроме разнообразных красивостей и полезностей вроде HALа, я получил ещё и кодировку UTF-8. Не слишком понятно, зафига она вообще нужна (учитывая, что я уже слишком старый, что-бы учить китайский :-) ), но это всё привело к резкому уменьшению скорости работы с текстами... Впрочем, это всё легко лечится отключением UTF (например можно приравнять $LC_ALL в "C").

Другая проблема - это то, что старые файлы с русскими именами теперь не читаются:

Рисунок 4.14. Каталог с файлами в KOI8-R

Каталог с файлами в KOI8-R


Решить эту проблему не так просто, как решается проблема с кодировкой на внешних носителях.

Замечание

например для флешки в VFAT проблема решается так:
mount -t vfat -o iocharset=utf8,codepage=866 /dev/sda1 /mnt/usb

Дело в том, что при монтировании EXT3 никакой ошибки не происходит - такие имена вполне допустимы для наших ФС.

Именно по этому файлы приходится переименовывать. Я сделал это следующим скриптом (перед его применением выполните export LC_ALL=C, иначе sed не сможет обработать несимволы, которыми являются русские буквы в кодировке KOI8-R). Скрипт запускается так:

find КАТАЛОГ | ren-koi8.sed

Пример 4.17. Переименование файлов командой iconv. (для каталогов и файлов).

#!/bin/sed -rnf

# export LC_ALL=C

# сохранение оригинального имени файла
h

# конвертирование в UTF-8
s/'/'"'"'/g
s/.*/echo -e '&' | iconv -f koi8-r -t utf-8/ep
H

# проверка. является-ли файл каталогом?
g
s/\n.*//
s/'/'"'"'/g
s/.*/test -d '&'; echo $?/e
/^0$/{
        # это каталог
        g
        s/.*\n//
        s/'/'"'"'/g
        s~.*~mkdir -vp 't/&' >/dev/stderr; echo $?~e
        /^0$/! b error
        b
}

# это файл

g
s/'/'"'"'/g
s~(.*)\n(.*)~cp -v '\1' 't/\2' >/dev/stderr; echo $?~e
/^0$/! {
        :error
        s/.*/Erorr code &/p
        q 77
}


Замечание

Конечно тут тоже лучше применить команду ln вместо cp. Тогда каталоги будут создаваться, но вот файлы копироваться не станут - вместо этого будут созданы жёсткие ссылки. Если вам понравится результат, вы можете удалить старый каталог, и заменить его новым. Это очень полезно для случая недостаточного пространства на разделе диска.

Вы можете обсудить этот документ на форуме. Текст предоставляется по лицензии GNU Free Documentation License (Перевод лицензии GFDL).

Вы можете пожертвовать небольшую сумму яндекс-денег на счёт 41001666004238 для оплаты хостинга, интернета, и прочего. Это конечно добровольно, однако это намного улучшит данный документ (у меня будет больше времени для его улучшения). На самом деле, проект часто находится на грани закрытия, ибо никаких денег никогда не приносил, и приносить не будет. Вы можете мне помочь. Спасибо.