¿Por qué los nombres de mis carpetas terminaron así, y cómo puedo solucionar esto usando un script?

15

Lo siento si esto tiene una respuesta en otra parte, no tengo idea de cómo buscar mi problema.

Estaba ejecutando algunas simulaciones en un servidor HPC redhat linux, y mi código para manejar la estructura de carpetas para guardar la salida tenía un error desafortunado. Mi código matlab para crear la carpeta fue:

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

donde sp.run_numberhabía un número entero Olvidé convertirlo en una cadena, pero por alguna razón la ejecución mkdir(folder);(en matlab) todavía tuvo éxito. De hecho, las simulaciones se ejecutaron sin problemas y los datos se guardaron en el directorio correspondiente.

Ahora, cuando se consulta / imprime la estructura de carpetas, aparece la siguiente situación:

  • Cuando intento tabular autocompletar: run_ run_^A/ run_^B/ run_^C/ run_^D/ run_^E/ run_^F/ run_^G/ run_^H/ run_^I/
  • Cuando uso ls: run_ run_? run_? run_? run_? run_? run_? run_? run_? run_? run_?.
  • Cuando transfiero a mi Mac usando rsync, la --progressopción muestra: run_\#003/etc.con (supongo) que el número que coincide con el entero está sp.run_numberrelleno con tres dígitos, por lo que la décima ejecución esrun_\#010/
  • Cuando veo las carpetas en el buscador veo run_ run_ run_ run_ run_ run_ run_ run_ run_ run_?
  • Mirando esta pregunta y usando el comando ls | LC_ALL=C sed -n lobtengo:
run_$
run_\001$
run_\002$
run_\003$
run_\004$
run_\005$
run_\006$
run_\a$
run_\b$
run_\t$
run_$

No puedo acceder a cdlas carpetas con ninguna de estas representaciones.

Tengo miles de estas carpetas, así que tendré que arreglar esto con un script. ¿Cuál de estas opciones es la representación correcta de la carpeta? ¿Cómo puedo referirme programáticamente a estas carpetas para cambiarles el nombre con un nombre formateado correctamente usando un script bash? Y supongo que por curiosidad, ¿cómo diablos sucedió esto en primer lugar?

Phill
fuente
44
"Cuando trato de tabular autocompletar: ... Si intento escribir ..." ¿Por qué escribir y no dejar que se complete automáticamente si es por ti? Tampoco ^Aes ^seguido literalmente A, sino Ctrl-A (puede escribirlo usando Ctrl-V Ctrl-A ya que Ctrl-A es generalmente un acceso directo para el shell).
muru
@muru que no funciona ... Llego tan lejos run_y tengo que escribir algo
Phill
Lo siento, comenté antes de ver tu edición, que logra llevarme a través de cd
Phill
99
Por cierto, la "razón" por la que mkdir en matlab hizo esto se debe a que los ÚNICOS caracteres no válidos en un archivo o nombre de directorio en sistemas de archivos unix son NUL y barra diagonal /. Cualquier otro carácter es válido, incluidos los caracteres de control. No sé qué habría hecho matlab si sp.run_number fuera 0 (probablemente abortar con un error o producir run_, ya que el byte NUL terminaría la cadena del nombre del directorio). Por supuesto, esto también sería problemático para los valores de 16 bits (o superiores) que tenían un byte NUL en ellos, y también variaría de acuerdo con la endianidad del sistema que ejecuta matlab.
cas

Respuestas:

26

Puede usar la renameutilidad perl (aka prenameo file-rename) para cambiar el nombre de los directorios.

NOTA: Esto no debe confundirse con renamefrom util-linuxni con ninguna otra versión.

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

Esto utiliza la ord()función de perl para reemplazar cada carácter de control en el nombre del archivo con el número ordinal para ese carácter. por ejemplo, se ^Aconvierte en 1, se ^Bconvierte en 2, etc.

los -n opción es que una ejecución en seco muestre qué rename haría si lo dejara. Elimínelo (o reemplácelo con -vuna salida detallada) para cambiar el nombre.

El emodificador en la s/LHS/RHS/egoperación hace que perl ejecute el RHS (el reemplazo) como código perl, y los $1datos coincidentes (el carácter de control) del LHS.

Si desea números rellenados con ceros en los nombres de archivo, puede combinarlos ord()consprintf() . p.ej

$ 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)$

Los ejemplos anteriores funcionan si y solo si sp.run_number su script matlab estaba en el rango de 0..26 (por lo que produjo caracteres de control en los nombres de directorio).

Para tratar CUALQUIER carácter de 1 byte (es decir, de 0..255), usaría:

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

Si sp.run_numberpudiera ser> 255, tendrías que usar perl'sunpack() función lugar de ord(). No sé exactamente cómo matlab genera un int no convertido en una cadena, por lo que tendrá que experimentar. Ver perldoc -f unpackpara más detalles.

por ejemplo, lo siguiente descomprimirá los valores sin signo de 8 y 16 bits y los pondrá a cero a 5 dígitos de ancho:

 rename -n 's/run_(.*)/sprintf("run_%05i",unpack("SC",$1))/e' run_*/
cas
fuente
Gracias por los detalles! Estoy tratando de probarlo a cabo con la -nopción, pero me está diciendo su una opción no válida - la información de versión me da rename from util-linux 2.23.2de manera segura su I'mnot la misma función
Phill
3
es por eso que especifiqué la versión perl de la renameutilidad. util-linux's renamees muy diferente, mucho menos capaz, y las opciones de línea de comando son incompatibles. Si está ejecutando Debian o similar, intente instalar el file-renamepaquete. de lo contrario, instale el paquete apropiado para su distribución. puede que ya esté instalado, intente ejecutarlo prenameo file-renameno solo rename.
cas
Sí, pensé que ese era el caso. Veré si puedo conseguir que uno de esos funcione. Gracias de nuevo por tomarse el tiempo para ayudarme.
Phill
11

Y supongo que por curiosidad, ¿cómo diablos sucedió esto en primer lugar?

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

donde sp.run_numberhabía un número entero Olvidé convertirlo en una cadena, pero por alguna razón ejecutémkdir(folder) ; (en matlab) todavía tuvo éxito.

Entonces, parecería que mkdir([...])en Matlab concatena los miembros de la matriz para construir el nombre de archivo como una cadena. Pero le diste un número, y los números son lo que realmente son los personajes de una computadora. Entonces, cuando sp.run_numberfue 1, te dio el personaje con valor1 , y luego el personaje con valor 2, etc.

Esos son caracteres de control, no tienen símbolos imprimibles, e imprimirlos en un terminal tendría otras consecuencias. Entonces, en cambio, a menudo están representados por diferentes tipos de escapes: \001(octal), \x01(hex), ^Ason todas representaciones comunes para el personaje con valor 1. El carácter con valor cero es un poco diferente, es el byte NUL que se usa para marcar el final de una cadena en C y en las llamadas al sistema Unix.

Si fue más alto que 31, comenzaría a ver caracteres imprimibles, 32 es espacio (aunque no muy visible), 33 = !, 34 = "etc.

Entonces,

  • run_ run_^A/ run_^B/- El primero run_corresponde al que tiene un byte cero, la cadena termina allí. Los demás muestran que a su shell le gusta usar mostrar los códigos de control ^A. La notación también insinúa el hecho de que el carácter con valor numérico 1 se puede ingresar como Ctrl-A, aunque debe decirle al shell que interprete no como un carácter de control, sino como un literal, Ctrl-V Ctrl-Adebe hacerlo al menos en Bash.

  • ls: run_ run_? run_?- lsno le gusta imprimir caracteres no imprimibles en el terminal, los reemplaza con signos de interrogación.

  • rsync: run_\#003/ese es nuevo para mí, pero la idea es la misma, la barra invertida marca un escape y el resto es el valor numérico del personaje. Me parece que el número aquí está en octal, como en el más común \003.

  • usando el comando ls | LC_ALL=C sed -n l... run_\006$ run_\a$ run_\b$ run_\t$- \a, \by \tson C escapes para alarma (campana), retroceso y tabulación, respectivamente. Tienen los valores numéricos 7, 8 y 9, por lo que debe quedar claro por qué vienen después \006. Usar esos escapes en C es otra forma de marcar los caracteres de control. Los signos de dólar finales marcan el final de la línea.

En cuanto a cd, suponiendo que mis suposiciones sean correctas, cd run_debería ir a ese único directorio sin un carácter final extraño, y cd run_?debería dar un error ya que el signo de interrogación es un carácter global que coincide con cualquier carácter individual, y hay varios nombres de archivo coincidentes, pero cdsolo espera uno.

¿Cuál de estas opciones es la representación correcta de la carpeta?

Todos ellos, en cierto sentido ...

En Bash, puede usar las comillas \000y \x00escapa dentro de las $'...'comillas para representar los caracteres especiales, por lo que$'run_\033 (octal) o $'run_\x1b'corresponder al directorio con el valor de carácter 27 (que resulta ser ESC). (No creo que Bash admita escapes con números decimales).

La respuesta de cas tiene un guión para cambiarles el nombre, así que no iré allí.

ilkkachu
fuente
Si es GNU ls, hay algunas opciones de comillas que incluyen -b/ --escapey --quoting-style=, o la QUOTING_STYLEvariable de entorno, para controlar cómo se muestran los caracteres que no se imprimen. Sin embargo, no creo que haya una opción para hacer que prefiera los escapes octales a las versiones de los personajes.
Toby Speight
3

Lo más fácil sería crear el nombre de archivo incorrecto y el nombre de archivo correcto en el mismo entorno donde ocurrió el error, y luego simplemente mover / cambiar el nombre de las carpetas a los nombres correctos.

Para evitar colisiones entre nombres existentes, utilice mejor otra carpeta de destino.

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

Si es posible, preferiría arreglar el script y simplemente ejecutarlo nuevamente; arreglar algunos errores extraños post mortem probablemente cuesta más y puede introducir nuevos problemas.

¡Buena suerte!

Peter
fuente