Lidando com nomes de arquivos com caracteres especiais (ex. ♫)

28

Recentemente, encontrei um arquivo cujo nome começa com o caractere '♫'. Eu queria copiar este arquivo, alimentá-lo em ffmpeg e referenciá-lo de várias outras maneiras no terminal. Eu geralmente auto-completar nomes de arquivos estranhos, mas isso falha, pois eu não posso nem digitar a primeira letra.

Eu não quero mudar para o mouse para realizar uma manobra de copiar e colar. Eu não quero memorizar um monte de códigos para possíveis cenários. Minha solução ad hoc era mudar para vim , colar !ls e copiar o caractere em questão, depois sair e colá-lo no terminal. Isso funcionou, mas é bastante horrível.

Existe uma maneira mais fácil de lidar com esses cenários?

OBSERVAÇÃO: estou usando o fish shell se isso mudar as coisas.

    
por ZirconCode 06.10.2014 / 14:25

8 respostas

34

Se o primeiro caractere do nome do arquivo for imprimível, mas não tiver alfanumérico nem espaço em branco, você poderá usar o operador [[:punct:]] glob:

$ ls *.txt
f1.txt  f2.txt  ♫abc.txt
$ ls [[:punct:]]*.txt
♫abc.txt
    
por 06.10.2014 / 14:56
7

ls tem alguns comutadores (como --quote-name, --escape, --literal) para lidar com caracteres não imprimíveis, mas neste caso parece que o caractere é "imprimível" mas não "tipificável" (pelo menos em meu teclado!), então nenhuma dessas opções parece ajudar.

Portanto, como uma abordagem geral de "força bruta" para se livrar de arquivos com qualquer caractere em seus nomes, você pode fazer isso:

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

Encontre a linha que contém o arquivo incorreto. Muito provavelmente será a primeira linha, mas vamos dizer que é a quinta. Imprima a linha 5 e codifique hexadecimal:

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

Ignorando o caractere 0a (nova linha), construa uma string de escape e use a opção -e do echo para traduzir as fugas:

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

Agora você pode copiar / mover / excluir desta forma:

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

Além disso, se você não está confinado a usar o shell script, você pode fazê-lo em Python assim:

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

Usando essa abordagem, você pode processar muitos arquivos, basta escrever a lógica para selecionar os arquivos corretos e renomeá-los sem que ocorra a sobrescrever, 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!'
    
por 06.10.2014 / 17:38
6

O mais simples que me ocorre é ls [^a-zA-Z0-9]* e faz o truque para mim, mas a resposta de terdon é melhor em chamar a atenção para a opção de shell extglob ou mesmo para uma abordagem independente de shell.

    
por 06.10.2014 / 17:21
5

Uma abordagem semelhante seria listar todos os arquivos que não iniciam com caracteres "normais". No bash você pode fazer isso com

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

No entanto, isso parece não estar disponível para fish , então você pode usar find :

$ find . -type f -not -name '[[:alpha:]]*'
    
por 06.10.2014 / 16:23
4

Renomear links simbólicos

Uma abordagem para lidar com nomes de arquivos com caracteres especiais - como primeiros caracteres ou em outro lugar no nome do arquivo, é renomear para nomes mais simples .

Isso pode ser usado mesmo se você precisar manter os nomes dos arquivos originais : Renomear uma cópia dos nomes dos arquivos.
Isso pode ser feito copiando os arquivos, mas também criando links simbólicos ou hardlinks para os arquivos e renomeá-los. cp cria links simbólicos em vez de cópias com a opção -s ( -l para hardlinks).

Use "desintoxicação" para limpar nomes

Para renomear para limpar nomes de arquivo, detox pode ser usado; Ele renomeia arquivos para limpar nomes de arquivos de acordo com várias regras, conforme definido em uma detoxrc . Por padrão, os caracteres UTF8 são apenas removidos; Com a opção -s utf_8-only , eles são substituídos por _ :

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


"detox" em links simbólicos

Combinado com o trabalho em links simbólicos, como descrito acima:

$ 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
    
por 08.10.2014 / 05:49
1

Eu não uso fish , mas a documentação diz que você pode inserir um Unicode caractere prefixando seu código de caractere hexadecimal com \u (para caracteres de 16 bits) ou \U (para caracteres de 32 bits). Eu acho que o código para é 491eb , então você poderia fazer:

mv \U000491ebabc.mp3 abc.mp3

para renomear ♫abc.mp3 .

Note que você precisa dos zeros à esquerda, caso contrário abc no final será tratado como dígitos hexadecimais e parte do código do caractere; para um caractere de 32 bits, você precisa digitar 8 dígitos.

    
por 08.10.2014 / 21:20
0

Você não disse se deseja manter esses nomes de arquivo problemáticos. Uma solução pode ser “consertar” o problema de uma vez por todas, renomeando (alguns ou todos) seus arquivos para nomes que você pode digitar executando este script:

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

Isso listará seus nomes de arquivos existentes, cada um seguido por ...? . Apenas digite Enter para deixar um arquivo como está; ou digite um novo nome para renomeá-lo. A opção -i fará com que você peça para confirmar a sobrescrita se você especificar o nome de outro arquivo existente.

Esse script pode ser modificado de várias maneiras:

  • Você pode modificar o curinga ( * ) para algo mais restritivo, por exemplo, *.avi *.mov , para que você não precise examinar todos os arquivos.
  • Você pode alterar o mv para cp , então você mantém uma cópia do arquivo com o nome atual e crie uma cópia (temporária?) com um nome tipificável.
  • Você pode criar um novo nome de arquivo com base no nome de arquivo existente. Por exemplo,

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

    que permite que você coloque um prefixo na frente do nome antigo. Se você escolheu um prefixo exclusivo, isso permitirá que você use o preenchimento automático.

por 06.10.2014 / 18:27
0

Use zsh e digite o que vem a seguir. O ZSH suporta auto fuzzy completo e pode lidar com isso. (É especialmente legal com o plug-in OH-MY-ZSH .)

    
por 09.10.2014 / 22:37