web-dev-qa-db-fra.com

Pourquoi les noms de mes dossiers se sont-ils retrouvés ainsi et comment puis-je résoudre ce problème à l'aide d'un script?

Désolé si cela a une réponse ailleurs, je ne sais pas comment rechercher mon problème.

J'exécutais des simulations sur un serveur HPC Linux redhat, et mon code pour gérer la structure des dossiers pour enregistrer la sortie avait un bug malheureux. Mon code matlab pour créer le dossier était:

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

sp.run_number était un entier. J'ai oublié de le convertir en chaîne, mais pour une raison quelconque, l'exécution de mkdir(folder); (dans matlab) a quand même réussi. En fait, les simulations se sont déroulées sans accroc et les données ont été enregistrées dans le répertoire correspondant.

Maintenant, lorsque la structure du dossier est interrogée/imprimée, j'obtiens les situations suivantes:

  • Lorsque j'essaie de taper la saisie semi-automatique: run_ run_^A/ run_^B/ run_^C/ run_^D/ run_^E/ run_^F/ run_^G/ run_^H/ run_^I/
  • Lorsque j'utilise ls: run_ run_? run_? run_? run_? run_? run_? run_? run_? run_? run_?.
  • Lorsque je transfère sur mon Mac à l'aide de rsync, l'option --progress Affiche: run_\#003/ Etc. avec (je suppose) le nombre correspondant à l'entier dans sp.run_number Complété à trois chiffres, donc le La 10ème manche est run_\#010/
  • Lorsque je regarde les dossiers dans le Finder, je vois run_ run_ run_ run_ run_ run_ run_ run_ run_ run_?
  • En regardant cette question et en utilisant la commande ls | LC_ALL=C sed -n l J'obtiens:
run_$
run_\001$
run_\002$
run_\003$
run_\004$
run_\005$
run_\006$
run_\a$
run_\b$
run_\t$
run_$

Je ne parviens pas à cd dans les dossiers à l'aide de ces représentations.

J'ai des milliers de ces dossiers, je vais donc devoir corriger cela avec un script. Laquelle de ces options est la représentation correcte du dossier? Comment puis-je faire référence par programmation à ces dossiers afin de les renommer avec un nom correctement formaté à l'aide d'un script bash? Et je suppose que par curiosité, comment diable est-ce arrivé en premier lieu?

15
Phill

Vous pouvez utiliser l'utilitaire Perl rename (alias prename ou file-rename) Pour renommer les répertoires.

REMARQUE: À ne pas confondre avec rename de util-linux, Ou toute autre version.

rename -n 's/([[:cntrl:]])/ord($1)/eg' run_*/

Cela utilise la fonction ord() de Perl pour remplacer chaque caractère de contrôle dans le nom de fichier par le numéro ordinal de ce caractère. Par exemple, ^A devient 1, ^B devient 2, etc.

L'option -n Est pour un essai à sec pour montrer ce que renameferait si vous le permettez. Supprimez-le (ou remplacez-le par -v Pour une sortie détaillée) pour le renommer réellement.

Le modificateur e dans l'opération s/LHS/RHS/eg Fait que Perl exécute le RHS (le remplacement) en tant que code Perl, et le $1 Est les données correspondantes (le caractère de contrôle) du LHS.

Si vous voulez des nombres remplis par zéro dans les noms de fichiers, vous pouvez combiner ord() avec sprintf(). par exemple.

$ rename -n 's/([[:cntrl:]])/sprintf("%02i",ord($1))/eg' run_*/ | sed -n l
rename(run_\001, run_01)$
rename(run_\002, run_02)$
rename(run_\003, run_03)$
rename(run_\004, run_04)$
rename(run_\005, run_05)$
rename(run_\006, run_06)$
rename(run_\a, run_07)$
rename(run_\b, run_08)$
rename(run_\t, run_09)$

Les exemples ci-dessus fonctionnent si et seulement sisp.run_number Dans votre script matlab était dans la plage de 0 à 26 (il a donc produit des caractères de contrôle dans les noms de répertoire).

Pour traiter N'IMPORTE QUEL caractère de 1 octet (c'est-à-dire de 0 à 255), vous utiliseriez:

rename -n 's/run_(.)/sprintf("run_%03i",ord($1))/e' run_*/

Si sp.run_number Pouvait être supérieur à 255, vous devriez utiliser la fonction unpack() de Perl au lieu de ord(). Je ne sais pas exactement comment matlab génère un int non converti dans une chaîne, vous devrez donc expérimenter. Voir perldoc -f unpack Pour plus de détails.

par exemple. ce qui suit décompressera les valeurs non signées 8 bits et 16 bits et les mettra à zéro à 5 chiffres:

 rename -n 's/run_(.*)/sprintf("run_%05i",unpack("SC",$1))/e' run_*/
26
cas

Et je suppose que, par curiosité, comment cela a-t-il pu arriver en premier lieu?

folder = [sp.saveLocation, 'run_', sp.run_number, '/'];

sp.run_number était un entier. J'ai oublié de le convertir en chaîne, mais pour une raison quelconque, j'exécute mkdir(folder); (dans matlab) a quand même réussi.

Ainsi, il semblerait que mkdir([...]) dans Matlab concatène les membres du tableau pour construire le nom de fichier sous forme de chaîne. Mais vous lui avez donné un numéro à la place, et les chiffres sont ce que sont vraiment les personnages sur un ordinateur. Ainsi, lorsque sp.run_number Était 1, Il vous a donné le caractère avec la valeur 1, Puis le caractère avec la valeur 2, Etc.

Ce sont des caractères de contrôle, ils n'ont pas de symboles imprimables, et les imprimer sur un terminal aurait d'autres conséquences. Au lieu de cela, ils sont souvent représentés par différentes sortes d'échappement: \001 (Octal), \x01 (Hex), ^A Sont toutes des représentations courantes pour le personnage de valeur 1. Le caractère avec la valeur zéro est un peu différent, c'est l'octet NUL qui est utilisé pour marquer la fin d'une chaîne en C et dans les appels système Unix.

Si vous êtes allé au-dessus de 31, vous commenceriez à voir des caractères imprimables, 32 est un espace (pas très visible cependant), 33 = !, 34 = " Etc.

Donc,

  • run_ run_^A/ run_^B/ - Le premier run_ Correspond à celui avec un octet zéro, la chaîne s'arrête là. Les autres montrent que votre Shell aime utiliser afficher les codes de contrôle avec ^A. La notation indique également que le caractère de valeur numérique 1 peut être entré comme Ctrl-A, bien que vous deviez dire au shell d'interpréter non pas comme un caractère de contrôle, mais comme un littéral, Ctrl-VCtrl-A devrait le faire au moins dans Bash.

  • ls: run_ run_? run_? - ls n'aime pas imprimer les caractères non imprimables sur le terminal, il les remplace par des points d'interrogation.

  • rsync: run_\#003/ - celui-ci est nouveau pour moi, mais l'idée est la même, la barre oblique inverse marque une fuite et le reste est la valeur numérique du caractère. Il me semble que le nombre ici est en octal, comme dans le plus courant \003.

  • en utilisant la commande ls | LC_ALL=C sed -n l ... run_\006$run_\a$run_\b$run_\t$ - \a, \b et \t sont des échappements C pour l'alarme (cloche), le retour arrière et la tabulation, respectivement. Ils ont les valeurs numériques 7, 8 et 9, il devrait donc être clair pourquoi ils viennent après \006. L'utilisation de ces échappements C est une autre façon de marquer les caractères de contrôle. Les signes en dollars de fin marquent la fin de la ligne.

En ce qui concerne cd, en supposant que mes hypothèses sont correctes, cd run_ Devrait aller dans ce seul répertoire sans caractère de fin impair, et cd run_? Devrait donner une erreur puisque le point d'interrogation est un caractère glob qui correspond à n'importe quel caractère, et il y a plusieurs noms de fichiers correspondants, mais cd n'en attend qu'un.

Laquelle de ces options est la représentation correcte du dossier?

Tous, dans un sens ...

Dans Bash, vous pouvez utiliser les échappements \000 Et \x00 À l'intérieur des guillemets $'...' Pour représenter les caractères spéciaux, donc $'run_\033 (Octal) ou $'run_\x1b' correspond au répertoire avec la valeur de caractère 27 (qui se trouve être ESC). (Je ne pense pas que Bash supporte les échappements avec des nombres décimaux.)

la réponse de cas a un script pour les renommer, donc je ne vais pas y aller.

11
ilkkachu

Le plus simple serait de créer le mauvais nom de fichier et le bon nom de fichier dans le même environnement où l'incident s'est produit, puis de déplacer/renommer les dossiers avec les bons noms.

Pour éviter les collisions entre les noms existants, mieux utiliser un autre dossier de destination.

./saveLocationA/wrongname1 -> ./saveLocationB/correctname1
./saveLocationA/wrongname2 -> ./saveLocationB/correctname2
./saveLocationA/wrongname3 -> ./saveLocationB/correctname3

Si possible, je préférerais corriger le script et le relancer; réparer un bug post mortem bizarre coûte probablement plus cher et peut introduire de nouveaux problèmes.

Bonne chance!

3
Peter