renommer en masse (ou afficher correctement) les fichiers avec des caractères spéciaux

20

J'ai un tas de répertoires et sous-répertoires qui contiennent des fichiers avec des caractères spéciaux, comme ce fichier:

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

Find révèle une séquence d'échappement:

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

La seule raison pour laquelle je peux même taper leurs noms sur la console est à cause de la complétion des onglets. Cela signifie également que je peux les renommer manuellement (et supprimer le caractère spécial).

J'ai mis LC_ALL sur UTF-8, ce qui ne semble pas aider (pas non plus sur un nouveau shell):

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

Je me connecte à la machine en utilisant ssh depuis mon mac. C'est une installation Ubuntu:

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

Shell est Bash, TERM est défini sur xterm-color.

Ces fichiers existent depuis un certain temps et n'ont pas été créés à l'aide de cette installation d'Ubuntu. Je ne sais donc pas quels étaient les paramètres d'encodage du système.

J'ai essayé des choses dans le sens de:

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

Mais je ne trouve pas de solution qui fasse tout ce que je veux:

  1. Identifiez tous les fichiers qui ont des caractères non affichables (ce qui précède ignore beaucoup trop)
  2. Pour tous ces fichiers dans une arborescence de répertoires (récursivement), exécutez mv oldname newname
  3. En option, la possibilité de translittérer des caractères spéciaux tels que ä en a (non requis, mais serait génial)

OU

  1. Afficher correctement tous ces fichiers (et aucune erreur dans les applications lors de la tentative d'ouverture)

J'ai des morceaux, comme itérer sur tous les fichiers et les déplacer, mais identifier les fichiers et les formater correctement pour la commande mv semble être la partie difficile.

Toute information supplémentaire expliquant pourquoi ils ne s'affichent pas correctement ou comment "deviner" le codage correct est également la bienvenue. (J'ai essayé convmv mais il ne semble pas faire exactement ce que je veux: http://j3e.de/linux/convmv/ )

RobbieV
la source
La seule réponse ci-dessous suit la première façon (les trouver et renommer votre nouvel encodage), mais la deuxième manière serait également intéressante: maintenant, quand vous connaissez l'encodage utilisé pour les noms de fichiers distants, comment faire un ssh vers l'hôte distant dans un tel une manière dont les noms de fichiers s'affichent correctement (et peuvent être gérés en tapant leurs noms avec votre clavier)?
imz - Ivan Zakharyaschev

Réponses:

21

Je suppose que vous voyez ce caractère invalide parce que le nom contient une séquence d'octets qui n'est pas UTF-8 valide. Les noms de fichiers sur les systèmes de fichiers Unix typiques (y compris le vôtre) sont des chaînes d'octets, et c'est aux applications de décider du codage à utiliser. De nos jours, il y a une tendance à utiliser UTF-8, mais ce n'est pas universel, en particulier dans les environnements locaux qui ne pourraient jamais vivre avec de l'ASCII ordinaire et qui utilisaient d'autres encodages avant même que l'UTF-8 n'existe.

Essayez LC_CTYPE=en_US.iso88591 lsde voir si le nom de fichier a du sens dans ISO-8859-1 (latin-1). Si ce n'est pas le cas, essayez d'autres paramètres régionaux. Notez que seul le LC_CTYPEparamètre régional est important ici.

Dans un environnement local UTF-8, la commande suivante vous montrera tous les fichiers dont le nom n'est pas UTF-8 valide:

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

Vous pouvez vérifier si elles ont plus de sens dans un autre lieu avec recodage ou iconv :

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

Une fois que vous avez déterminé qu'un certain nombre de noms de fichiers sont dans un certain encodage (par exemple latin1), une façon de les renommer est

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

Cela utilise la commande perl rename disponible sur Debian et Ubuntu. Vous pouvez le transmettre -npour montrer ce qu'il ferait sans renommer réellement les fichiers.

Gilles 'SO- arrête d'être méchant'
la source
Merci, je vais essayer certaines de ces choses plus tard dans la journée! On dirait que ce sera la réponse acceptée :)
RobbieV
La trouvaille | La commande grep '[[: print:]]' semble simplement renvoyer tous les fichiers. L'UTF-8 ne devrait-il pas être compatible avec de nombreux autres encodages avec des caractères "normaux"?
RobbieV
@RobbieV: J'ai fait une faute de frappe et je voulais grep [^[:print:]]rechercher des caractères non imprimables. Mais je viens de tester avec GNU grep et les séquences UTF-8 invalides ne sont pas capturées [^[:print:]](ce qui est logique car ce ne sont pas des caractères non imprimables, ce ne sont pas du tout des caractères). J'ai édité mon article avec un moyen plus long de saisir les lignes avec des séquences utf8 invalides. Notez que j'ai également fixé la direction des exemples recodeet iconv.
Gilles 'SO- arrête d'être méchant'
Cela a parfaitement fonctionné. J'ai essayé toutes les commandes sauf celle iconv, et elles fonctionnent toutes comme prévu. Magie pure!
RobbieV
Même l'encodage latin1 suggéré était le bon :)
RobbieV
1

Je sais que c'est une vieille question mais j'ai cherché toute la nuit une solution similaire. J'ai trouvé quelques conseils utiles mais ils n'ont pas fait exactement ce dont j'avais besoin, j'ai donc dû en mélanger quelques-uns pour obtenir le résultat correct que je cherchais

pour supprimer simplement les caractères spéciaux et les remplacer par un point (.)

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

à utiliser dans un cronjob j'ai fait ce qui suit pour exécuter chaque minute

*/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

J'espère que quelqu'un trouve cela utile car cela a fait ma journée :)

Topps70
la source
(1) Pour plus de clarté, vous pouvez changer `…`pour $(…)- voir ceci , ceci et ceci . (2) Vous devez toujours citer les références de vos variables shell (par exemple, "$f") sauf si vous avez une bonne raison de ne pas le faire et que vous êtes sûr de savoir ce que vous faites. Cela s'applique même à echo "$f" | sed …. Elle s'applique également à l' expression entière $(…)(ou `…`); à savoir mv "$f" "$(echo "$f" | sed "…")". … (Suite)
Scott
(Suite)… (3) Vous devriez dire , pour vous protéger contre les noms de fichiers commençant par . (4) Si vous avez des fichiers nommés "foo ♥ bar.txt" et "foo ♠ bar.txt", cela (tentera) de les renommer tous les deux en "foo.bar.txt", provoquant probablement tous les fichiers à détruire. (5) Pourquoi diable voudriez-vous faire cela une fois par minute? mv -- "$f" …-
Scott
J'ai un script torrent qui télécharge automatiquement les fichiers. et parfois certains fichiers contiennent des caractères qui jettent le téléchargeur hors tension. donc en renommant simplement les fichiers avec des caractères spéciaux, mon cron a résolu tous mes problèmes et le téléchargeur fait son travail en douceur.
Topps70
donc (ce fichier tha, t was - down_loaded.ext) se transforme en (this.fi.le.tha.t.was.down.loaded.ext)
Topps70
0

Maintenant, lorsque vous savez quel encodage est utilisé pour les noms de fichiers sur l'extrémité distante ("latin1" - selon les commentaires de la première réponse), vous pouvez également suivre la deuxième façon - exécuter un terminal local et ssh dans un tel manière que les noms de fichiers distants s'affichent correctement (plutôt que la première façon: les renommer) .

Comme moi , vous pouvez démarrer un terminal localement qui fonctionnerait dans cet encodage spécial, peut-être comme ceci:

LC_ALL = en_US.latin1 xvt &

xvt représente votre programme de terminal.

Peut-être que les paramètres régionaux existants sont appelés en_US.iso88591, et non en_US.latin1, comme je l'ai supposé.

imz - Ivan Zakharyaschev
la source
0

Cela ne répond pas aux exigences de masse, mais je viens d'avoir un problème similaire où j'avais plusieurs versions d'un fichier avec des noms similaires qui ne différaient que par un seul caractère étrange. Malheureusement, cela signifiait que je ne pouvais pas renommer les contrevenants à l'aide de l'astuce générique que j'utilise habituellement.

À la fin, j'ai utilisé Filezilla pour me connecter en tant que client SFTP, j'ai parcouru les fichiers et les ai renommés à l'aide de l'interface graphique. Filezilla a très bien géré les caractères douteux.

kabadisha
la source