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

30

Recentemente, encontrei um arquivo cujo nome começa com o caractere '♫'. Eu queria copiar esse arquivo, alimentá-lo ffmpege fazer referência a ele de várias outras maneiras no terminal. Normalmente, eu preencho automaticamente nomes de arquivos estranhos, mas isso falha, pois nem consigo digitar a primeira letra.

Não quero mudar para o mouse para executar uma manobra de copiar e colar. Não quero memorizar vários códigos para possíveis cenários. Minha solução ad hoc foi trocar vim, colar !lse copiar o personagem em questão e sair e colar no terminal. Isso funcionou, mas é bastante horrível.

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

NOTA: Estou usando a casca do peixe se ela mudar as coisas.

ZirconCode
fonte
7
Você pode usar outras partes do arquivo para formar um regex para trabalhar com ele? *restoffile.aviou algo parecido com isto?
Slm
11
Nesse caso, o nome restante era uma mistura de Kanji e Katakana (escrita japonesa), portanto não com facilidade.
ZirconCode
3
Entendido, apenas pensei em perguntar. A resposta de jimmij resolve então? Você também se importaria de colar uma captura de tela dos arquivos incorretos? Provavelmente seria útil para outras pessoas que possam ler isso mais tarde.
slm
11
Estou tentando fazê-lo funcionar agora. Eu não sei como postar um screeny, mas a execução dos seguintes comandos dará a você o meu problema simulado:touch '♫ 漢字カ' touch '♫ 漢字タ'
ZirconCode
11
Com o zsh, você pode usar as opções para que a guia dê um menu no qual você pode selecionar o arquivo apropriado.
7274 Kevin

Respostas:

35

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

$ ls *.txt
f1.txt  f2.txt  ♫abc.txt
$ ls [[:punct:]]*.txt
♫abc.txt
jimmij
fonte
Hmm, eu não conhecia esses operadores glob, li sobre eles e aprendi um pouco (obrigado), resolve o problema que tive, que é um único arquivo estranho no meu diretório. Agora tenho esse problema com uma grande multidão de arquivos, devo fazer uma nova pergunta ou atualizar esta?
ZirconCode
Aceitei sua resposta, postarei o segundo cenário amanhã quando tiver tempo. Obrigado pela ajuda.
ZirconCode
6

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

user86880
fonte
Esta é uma facada decente o suficiente. Você poderia ls [^[:alnum:]]*pela mesma coisa. Mas é melhor usar a classe de personagem que é , em vez das classes que não são ; portanto ls [[:punct:]]*, listará esse arquivo.
Rich
6

O ls possui algumas opções (como --quote-name, --escape, --literal) para lidar com caracteres não imprimíveis, mas, neste caso, parece que o personagem é "imprimível", mas não "tipificável" (pelo menos no meu teclado! ), portanto, nenhuma dessas opções parece ajudar.

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

$ /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. Provavelmente será a primeira linha, mas digamos que seja a quinta. Imprima a linha 5 e codifique hexadecimalmente:

$ /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 de echo para traduzir os escapes:

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

Agora você pode copiar / mover / excluir da seguinte forma:

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

Além disso, se você não estiver confinado ao uso de shell script, poderá fazê-lo no Python assim:

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

Usando esta abordagem, você pode processar muitos arquivos, basta escrever a lógica para selecionar os arquivos corretos e renomeá-los sem bater de frente, 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
fonte
5

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

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

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

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

Renomear links simbólicos

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

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

Use "desintoxicação" para limpar nomes

Para renomear para limpar nomes de arquivos, detoxpode ser usado; Ele renomeia os arquivos para limpar os nomes dos arquivos de acordo com várias regras, conforme definido em um detoxrcarquivo. Por padrão, os caracteres UTF8 são removidos; Com a opção, -s utf_8-onlyeles são substituídos por _:

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


"desintoxicação" 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
Volker Siegel
fonte
2

Não uso fish, mas a documentação diz que você pode inserir um caractere Unicode prefixando seu código de caractere hexadecimal por \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

renomear ♫abc.mp3.

Observe que você precisa dos zeros iniciais, caso contrário, abcno final, serão tratados como dígitos hexadecimais e parte do código de caractere; para um caractere de 32 bits, é necessário inserir 8 dígitos.

Barmar
fonte
2

Não sei se já era o caso em 2014 quando você fez a pergunta, mas nas versões atuais de fish(a partir de 2019), você pode pressionar Tabduas vezes para obter uma seleção no estilo zsh, na qual é possível usar as teclas de seta para selecione visualmente o arquivo desejado sem precisar digitar nenhuma parte do nome do arquivo.

Stéphane Chazelas
fonte
2

O Fish não suporta curingas de colchete ¹ por design.

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

O comando não pesquisa em diretórios e exibe escondidos nomes de arquivos que não comecem com os personagens letters, digits, . _ -(cf a documentação find).

Nota: $argv é uma variável de matriz especial (Fish shell) que contém os argumentos da função, portanto, o comando subjacente pode receber qualquer expressão (por exemplo, apelido ).

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

¹ Na verdade, peixes suporta expansão suporte (expansão variável de matriz) mas Bash usa outra terminologia (parâmetro de nome de arquivo e expansões).

Fólkvangr
fonte
1

Use zshe digite o que vem a seguir. O ZSH suporta auto-difusa e pode lidar com isso. (É especialmente interessante com o plug-in OH-MY-ZSH .)

Martin Thoma
fonte
0

Você não disse se deseja manter esses nomes de arquivos problemáticos. Uma solução pode ser “corrigir” 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 ...?. Basta digitar Enterpara deixar um arquivo como está; ou digite um novo nome para renomeá-lo. A -iopção fará com que você solicite a confirmação da substituição, se você especificar o nome de outro arquivo existente.

Este script pode ser modificado de várias maneiras:

  • Você pode modificar o curinga ( *) para algo mais restritivo, por exemplo *.avi *.mov, para não precisar olhar para todos os arquivos.
  • Você pode alterar mvpara cp, para manter uma cópia do arquivo com seu nome atual e criar uma cópia (temporária?) Com um nome digitável.
  • Você pode criar um novo nome de arquivo baseado no nome do arquivo existente. Por exemplo,

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

    que permite colocar um prefixo na frente do nome antigo. Se você escolher um prefixo exclusivo, isso permitirá que você complete automaticamente.

G-Man Diz 'Reinstate Monica'
fonte