Manejo de nombres de archivo con primeros caracteres especiales (ej. ♫)

30

Recientemente me encontré con un archivo cuyo nombre comienza con el carácter '♫'. Quería copiar este archivo, alimentarlo ffmpegy hacer referencia a él de varias otras maneras en el terminal. Por lo general, autocompleto nombres de archivo extraños, pero esto falla ya que ni siquiera puedo escribir la primera letra.

No quiero cambiar al mouse para realizar una maniobra de copiar y pegar. No quiero memorizar un montón de códigos para posibles escenarios. Mi solución ad hoc fue cambiar vim, pegar !lsy copiar el personaje en cuestión, luego salir y pegarlo en el terminal. Esto funcionó pero es bastante horrible.

¿Hay una manera más fácil de lidiar con tales escenarios?

NOTA: Estoy usando la concha de pescado si cambia las cosas.

Código de circón
fuente
77
¿Puedes usar otras partes del archivo para formar una expresión regular para trabajar con él? *restoffile.avi¿o algo como esto?
slm
1
En este caso, el nombre restante era una mezcla de kanji y katakana (escritura japonesa), así que no con facilidad.
ZirconCode
3
Entendido, solo pensé en preguntar. ¿Entonces la respuesta de jimmij lo resuelve? ¿También le importaría pegar una captura de pantalla de los archivos ofensivos? Probablemente sea útil para otros que puedan leer esto más adelante.
slm
1
Estoy tratando de que funcione en este momento. No sé cómo publicar una pantalla, pero ejecutar los siguientes comandos le dará mi problema simulado:touch '♫ 漢字カ' touch '♫ 漢字タ'
ZirconCode
1
Con zsh puede usar las opciones para que la pestaña le brinde un menú desde el cual puede seleccionar el archivo apropiado.
Kevin

Respuestas:

35

Si el primer carácter del nombre del archivo se puede imprimir pero no es alfanumérico ni espacio en blanco, puede usar el [[:punct:]]operador glob:

$ ls *.txt
f1.txt  f2.txt  ♫abc.txt
$ ls [[:punct:]]*.txt
♫abc.txt
jimmij
fuente
Hmm, no sabía acerca de estos operadores globales, los leí y aprendí un poco (gracias), resuelve el problema que tenía, que es un solo archivo extraño en mi directorio. Ahora tengo este problema con una gran multitud de archivos, ¿debo hacer una nueva pregunta o actualizar esta?
ZirconCode
He aceptado tu respuesta, publicaré el segundo escenario mañana cuando tenga tiempo. Gracias por la ayuda.
ZirconCode
6

Lo más simple que se me ocurre es ls [^a-zA-Z0-9]*y me sirve, pero la respuesta de terdon es mejor para llamar la atención sobre la opción de shell extglob o incluso un enfoque independiente de shell.

usuario86880
fuente
Esta es una puñalada bastante decente. Podrías ls [^[:alnum:]]*por lo mismo. Pero es mejor usar la clase de caracteres que es , en lugar de las clases que no lo son ; por ls [[:punct:]]*lo tanto , enumerará este archivo.
Rico
6

ls tiene algunos interruptores (como --quote-name, --escape, --literal) para tratar con caracteres no imprimibles, pero en este caso parece que el carácter es "imprimible" pero no "escribible" (¡al menos en mi teclado! ), por lo que ninguno de estos interruptores parece ayudar.

Por lo tanto, como un enfoque general de "fuerza bruta" para deshacerse de los archivos con cualquier carácter en sus nombres, puede hacer esto:

$ /bin/ls -1A|cat -n  # list all files (except . and ..), 1 per line, add line numbers
     1  ♫
     2  f1.txt
     3  f2.txt

Encuentra la línea que contiene el archivo ofensivo. Es muy probable que sea la primera línea, pero digamos que es la quinta. Imprima la línea 5 y la codifique hexadecimal:

$ /bin/ls -1A|sed -n 5p|xxd -g 1
0000000: e2 99 ab 0a                                      ....

Ignorando el carácter 0a (nueva línea), construya una cadena de escape y use la opción -e de echo para traducir los escapes:

$ echo -e '\xe2\x99\xab'
♫

Ahora puede copiarlo / moverlo / eliminarlo así:

$ cp -vi $(echo -e '\xe2\x99\xab') better_name
‘♫’ -> ‘better_name’

Además, si no está limitado a usar el script de shell, puede hacerlo en Python de esta manera:

$ python
>>> import os
>>> os.listdir('.')
[ ..., '\xe2\x99\xab', ... ]
>>> print '\xe2\x99\xab'
♫
>>> import shutil
>>> shutil.copy('\xe2\x99\xab', 'better_name')

Con este enfoque, puede procesar muchos archivos, solo tiene que escribir la lógica para seleccionar los archivos correctos y renombrarlos sin tropezar, etc.

for f in os.listdir('.'):
  if not f.isalnum():
    newname = generate_newname(f)
    if not os.path.exists(newname):
      shutil.copy(f, newname)
    else:
      print newname, 'already exists!'
Matthew Breithaupt
fuente
5

Un enfoque similar sería enumerar todos los archivos que no comienzan con caracteres "normales". En bash puedes hacer esto con

$ shopt -s extglob
$ ls !([[:alpha:]]*)

Sin embargo, eso no parece estar disponible fish, por lo que podría usar finden su lugar:

$ find . -type f -not -name '[[:alpha:]]*'
terdon
fuente
4

Renombrar enlaces simbólicos

Un enfoque para manejar nombres de archivos con caracteres especiales, como primeros caracteres o en cualquier otra parte del nombre de archivo, es cambiar el nombre a nombres más simples .

Esto se puede usar incluso si necesita conservar los nombres de archivo originales : cambie el nombre de una copia de los nombres de archivo.
Eso se puede hacer copiando los archivos, pero también creando enlaces simbólicos o enlaces duros a los archivos, y renómbrelos. cpcrea enlaces simbólicos en lugar de copias con la opción -s( -lpara enlaces duros).

Use "desintoxicación" para limpiar nombres

Para renombrar para limpiar nombres de archivos, detoxse puede usar; Cambia el nombre de los archivos para limpiar los nombres de archivo de acuerdo con varias reglas definidas en un detoxrcarchivo. Por defecto, los caracteres UTF8 simplemente se eliminan; Con la opción -s utf_8-onlyson reemplazados por _:

$ touch '♫ 漢字カ' ♫foo
$ ls -1
♫foo
♫ 漢字カ
$ detox -s utf_8-only * 
$ ls -1                
_ ___
_foo


"desintoxicación" en enlaces simbólicos

Combinado con trabajar en enlaces simbólicos como se describe arriba:

$ mkdir orig
$ cd orig 
$ touch '♫ 漢字カ' ♫foo
$ cd ..
$ mkdir clean
$ cd clean 
$ cp -s ../orig/* .
$ ll               
lrwxrwxrwx 1 14 Oct  8 05:52 ♫foo -> ../orig/♫foo
lrwxrwxrwx 1 21 Oct  8 05:52 ♫\ 漢字カ -> ../orig/♫\ 漢字カ
$ ls -1
♫foo
♫ 漢字カ
$ detox --special -s utf_8-only *
$ ll                                
lrwxrwxrwx 1 21 Oct  8 05:52 _\ ___ -> ../orig/♫\ 漢字カ
lrwxrwxrwx 1 14 Oct  8 05:52 _foo -> ../orig/♫foo
Volker Siegel
fuente
2

No uso fish, pero la documentación dice que puede ingresar un carácter Unicode prefijando su código de caracteres hexadecimales con \u(para caracteres de 16 bits) o \U(para caracteres de 32 bits). Creo que el código para es 491eb, así que podrías hacer:

mv \U000491ebabc.mp3 abc.mp3

para cambiar el nombre ♫abc.mp3.

Tenga en cuenta que necesita los ceros a la izquierda, de lo contrario abcal final se tratarán como dígitos hexadecimales y parte del código de caracteres; para un carácter de 32 bits necesita ingresar 8 dígitos.

Barmar
fuente
2

No sé si ya era el caso en 2014 cuando hizo la pregunta, pero en las versiones actuales de fish(a partir de 2019), puede presionar Tabdos veces, para obtener una selección de estilo zsh donde puede usar las teclas de flecha para seleccione visualmente el archivo que desee sin tener que escribir ninguna parte del nombre del archivo.

Stéphane Chazelas
fuente
2

Fish no admite comodines de soporte ¹ por diseño.

function find_special_filename
    find ! -path './.*' -name '[^-.a-zA-Z0-9_]*' $argv
end

El comando no busca en los directorios y muestra los nombres de archivos ocultos que no comienzan con los caracteres letters, digits, . _ -(cf la documentación de find).

Nota: $argv es una variable de matriz especial (Fish shell) que contiene los argumentos de la función, por lo tanto, el comando subyacente puede recibir cualquier expresión (por ejemplo, alias ).

find_special_filename -exec mv '{}' misc/ \;

¹ De hecho, Fish admite la expansión de soporte (expansión variable de matriz) pero Bash usa otra terminología (expansiones de parámetros y nombres de archivo).

Fólkvangr
fuente
1

Usa zshy escribe lo que viene después. ZSH admite el autocompletado difuso y puede manejarlo. (Es especialmente agradable con el complemento OH-MY-ZSH ).

Martin Thoma
fuente
0

No dijo si desea mantener estos nombres de archivo problemáticos. Una solución podría ser "solucionar" el problema de una vez por todas renombrando (algunos o todos) sus archivos a nombres que pueda escribir ejecutando este script:

#!/bin/sh
for old in *
do
      printf "%s ...? " "$old"
      if read new  &&  [ "$new" != "" ]
      then
             mv -i "$old" "$new"
      fi
done

Esto mostrará una lista de sus nombres de archivo existentes, cada uno seguido de ...?. Simplemente escriba Enterpara dejar un archivo como está; o escriba un nuevo nombre para cambiarle el nombre. La -iopción hará que le pida que confirme la sobrescritura si especifica el nombre de otro archivo existente.

Este script se puede modificar de varias maneras:

  • Puede modificar el comodín ( *) a algo más restrictivo, por ejemplo *.avi *.mov, para que no tenga que mirar cada archivo.
  • Puede cambiar mva cp, por lo que conserva una copia del archivo con su nombre actual y crea una copia (¿temporal?) Con un nombre que se pueda escribir.
  • Puede crear un nuevo nombre de archivo basado en el nombre de archivo existente. Por ejemplo,

    if read pfx  &&  [ "$pfx" != "" ]
    then
            mv -i "$old" "$pfx$old"
    fi
    

    que le permite colocar un prefijo delante del nombre anterior. Si elige un prefijo único, esto le permitirá usar el autocompletado.

G-Man dice 'restablecer a Mónica'
fuente