Массовое переименование (или правильное отображение) файлов со специальными символами

20

У меня есть несколько каталогов и подкаталогов, которые содержат файлы со специальными символами, например, этот файл:

robbie@phil:~$ ls testsktest.txt 
test?sktest.txt

Find обнаруживает escape-последовательность:

robbie@phil:~$ find testsktest.txt -ls 
424512 4000 -rwxr--r-x   1 robbie   robbie    4091743 Jan 26 00:34 test\323sktest.txt

Единственная причина, по которой я могу даже ввести их имена в консоли, заключается в завершении табуляции. Это также означает, что я могу переименовать их вручную (и удалить специальный символ).

Я установил LC_ALL в UTF-8, что, похоже, не помогает (также не в новой оболочке):

robbie@phil:~$ echo $LC_ALL
en_US.UTF-8

Я подключаюсь к машине, используя ssh с моего mac. Это установка Ubuntu:

robbie@phil:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=7.10
DISTRIB_CODENAME=gutsy
DISTRIB_DESCRIPTION="Ubuntu 7.10"

Shell это Bash, TERM установлен в цвет xterm.

Эти файлы были там в течение достаточно долгого времени, и они не были созданы с использованием этой установки Ubuntu. Так что я не знаю, какими были системные настройки кодирования.

Я пробовал что-то вроде:

find . -type f -ls | sed 's/[^a-zA-Z0-9]//g'

Но я не могу найти решение, которое делает все, что я хочу:

  1. Определите все файлы, которые имеют не отображаемые символы (приведенное выше игнорирует слишком много)
  2. Для всех этих файлов в дереве каталогов (рекурсивно) выполните mv oldname newname
  3. По желанию, возможность транслитерации специальных символов, таких как ä, в (не обязательно, но было бы здорово)

ИЛИ

  1. Правильно отображать все эти файлы (и без ошибок в приложениях при попытке их открыть)

У меня есть кусочки, такие как перебирать все файлы и перемещать их, но идентификация файлов и их правильное форматирование для команды mv, кажется, трудная часть.

Любая дополнительная информация о том, почему они не отображаются правильно, или как «угадать» правильную кодировку, также приветствуется. (Я пробовал convmv, но, похоже, он не выполняет именно то, что мне нужно: http://j3e.de/linux/convmv/ )

RobbieV
источник
Единственный ответ, приведенный ниже, следует первому способу (найдите их и переименуйте в новую кодировку), но второй способ также будет интересен: теперь, когда вы знаете кодировку, используемую для удаленных имен файлов, как выполнить ssh для удаленного хоста таким образом. способ, которым имена файлов отображаются правильно (и ими можно управлять, набирая их имена с помощью клавиатуры)?
imz - Иван Захарящев

Ответы:

21

Я думаю, вы видите этот недопустимый символ, потому что имя содержит последовательность байтов, которая не является допустимой UTF-8. Имена файлов в типичных файловых системах Unix (включая вашу) являются байтовыми строками, и приложения должны решать, какую кодировку использовать. В настоящее время существует тенденция использовать UTF-8, но он не универсален, особенно в локалях, которые никогда не могли жить с простым ASCII и использовали другие кодировки еще до того, как UTF-8 даже существовал.

Попробуйте LC_CTYPE=en_US.iso88591 lsпосмотреть, имеет ли имя файла смысл в ISO-8859-1 (латиница-1). Если это не так, попробуйте другие локали. Обратите внимание, что LC_CTYPEздесь важна только настройка локали.

В локали UTF-8 следующая команда покажет вам все файлы, чье имя не является допустимым UTF-8:

grep-invalid-utf8 () {
  perl -l -ne '/^([\000-\177]|[\300-\337][\200-\277]|[\340-\357][\200-\277]{2}|[\360-\367][\200-\277]{3}|[\370-\373][\200-\277]{4}|[\374-\375][\200-\277]{5})*$/ or print'
}
find | grep-invalid-utf8

Вы можете проверить, имеют ли они смысл в другой локали с помощью recode или iconv :

find | grep-invalid-utf8 | recode latin1..utf8
find | grep-invalid-utf8 | iconv -f latin1 -t utf8

Как только вы определили, что группа имен файлов находится в определенной кодировке (например, latin1), один из способов переименовать их

find | grep-invalid-utf8 |
rename 'BEGIN {binmode STDIN, ":encoding(latin1)"; use Encode;}
        $_=encode("utf8", $_)'

Здесь используется команда perl rename, доступная в Debian и Ubuntu. Вы можете передать его, -nчтобы показать, что он будет делать, не переименовывая файлы.

Жиль "ТАК - перестань быть злым"
источник
Спасибо, я попробую некоторые из этих вещей позже сегодня! Похоже, это будет принятый ответ :)
RobbieV
Находка | Команда grep '[[: print:]]', кажется, просто возвращает все файлы. Разве UTF-8 не должен быть совместим со многими другими кодировками с «нормальными» символами?
RobbieV
@RobbieV: я опечатал и имел grep [^[:print:]]в виду поиск непечатных символов. Но я только что протестировал GNU grep, и недопустимые последовательности UTF-8 не обнаруживаются [^[:print:]](что имеет смысл, поскольку они не непечатаемые символы, они вообще не символы). Я отредактировал свой пост более длинным способом подрезания строк с недопустимыми последовательностями utf8. Обратите внимание, что я также зафиксировал направление recodeи iconvпримеры.
Жиль "ТАК - перестать быть злым"
Это сработало отлично. Перепробовал все команды кроме iconv one, и все они работают как положено. Чистая магия!
RobbieV
Даже предложенная кодировка latin1 была правильной :)
RobbieV
1

Я знаю, что это старый вопрос, но я всю ночь искал подобное решение. Я нашел несколько полезных советов, но они не делали именно то, что мне нужно, поэтому мне пришлось смешивать и сочетать несколько, чтобы получить правильный результат, который я искал

просто удалить специальные символы и заменить их точкой (.)

for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done

чтобы использовать в cronjob я сделал следующее, чтобы бегать каждую минуту

*/1 * * * * cd /path/to/files/ && for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done >/dev/null 2>&1

Я надеюсь, что кто-то найдет это полезным, поскольку это сделало мой день :)

Topps70
источник
(1) Для ясности, вы можете захотеть перейти `…`на ... $(…)увидеть это , это и это . (2) Вы всегда должны цитировать ссылки на переменные оболочки (например, "$f"), если у вас нет веских причин не делать этого, и вы уверены, что знаете, что делаете. Это относится даже к echo "$f" | sed …. Это также относится ко всему $(…)(или `…`) выражению; то есть mv "$f" "$(echo "$f" | sed "…")". … (Продолжение)
Скотт
(Продолжение)… (3) Вы должны сказать , чтобы защитить от имен файлов, начинающихся с . (4) Если у вас есть файлы с именами «foo ♥ bar.txt» и «foo ♠ bar.txt», это (попытка) переименует их обоих в «foo.bar.txt», возможно, вызывая все, кроме одного из файлы должны быть уничтожены. (5) С какой стати вы хотите делать это раз в минуту? mv -- "$f" …-
Скотт
У меня есть торрент-скрипт, который автоматически загружает файлы. и иногда в некоторых файлах есть символы, которые сбрасывают загрузчик. поэтому, просто переименовывая файлы специальными символами, мой cron исправил все мои проблемы, и загрузчик сделал свою работу плавно.
Topps70
так (этот фильм, который был - down_loaded.ext) превращается в (this.fi.le.tha.t.was.down.loaded.ext)
Topps70
0

Теперь, когда вы знаете, какая кодировка используется для имен файлов на удаленном конце («latin1» - в соответствии с комментариями к первому ответу), вы также можете пойти по второму пути - запустить локальный termninal и ssh таким образом: способ, которым удаленные имена файлов отображаются правильно (а не первый способ: переименуйте их) .

Как и я , вы можете запустить терминал локально, который будет работать в этой специальной кодировке, например, так:

LC_ALL = en_US.latin1 xvt &

xvt обозначает вашу терминальную программу.

Возможно, существующая локаль называется en_US.iso88591, а не en_US.latin1, как я предполагал.

imz - Иван Захарящев
источник
0

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

В конце я использовал Filezilla для подключения в качестве SFTP-клиента, просмотрел файлы и переименовал их с помощью графического интерфейса. Филезилла неплохо справился с хитрыми персонажами.

kabadisha
источник