Como posso excluir um arquivo cujo nome de arquivo tenha caracteres não imprimíveis

46

De alguma forma, consegui criar um arquivo que parece não ter um nome de arquivo. Encontrei algumas informações sobre como obter mais detalhes do arquivo no seguinte segmento.

No entanto, tentei algumas das sugestões listadas e não consigo excluir o arquivo. Não sei ao certo o que fiz para criá-lo, mas aconteceu ao tentar copiar um arquivo xml.

Algumas informações sobre o arquivo são as seguintes;

> ls -lb
total 296
-rw-r--r--   1 voyager  endeavor  137627 Jan 12 12:49 \177

> file *
:               XML document

> ls -i
 417777   

Tentei encontrar usando o interruptor inum e depois canalizá-lo para que parecesse a maneira mais infalível de se livrar dele. No entanto, o exemplo dado na parte inferior do segmento vinculado abaixo falhou para mim. O exemplo foi:

> find -inum 41777 -exec ls -al {} \;
find: illegal option -- i
find: [-H | -L] path-list predicate-list

então tentei usar a lista de caminhos primeiro, como a seguir, mas isso também não funcionou:

> find . -inum 41777 -exec ls -al {} \;

Não sei ao certo qual é o caractere não imprimível \ 177 ou como posso passar isso para um rmcomando, mas quero ter certeza de que não vou estragar nenhum outro arquivo / diretório na minha tentativa de excluir esse arquivo.

Mr Moose
fonte
1
\177é o DELcaractere ASCII .
Keith Thompson
9
417777! = 41777
um CVn
Excelente ponto Michael. Provavelmente é por isso que meu comando nunca funcionou.
Moose
@KeithThompson É octal sem liderança 0?
gato
1
@cat: Neste contexto, sim. Segue a sintaxe C para literais de cadeia e caracteres.
Keith Thompson

Respostas:

46

O arquivo tem um nome, mas é composto de caracteres não imprimíveis. Se você usa ksh93, bash, zsh, mksh ou FreeBSD sh, você pode tentar removê-lo especificando seu nome não imprimível. Primeiro, verifique se o nome está correto com: ls -ld $'\177' Se ele mostra o arquivo certo, use rm:rm $'\177'

Outra abordagem (um pouco mais arriscada) é usar rm -i -- *. Com a opção -i, o rm exige confirmação antes de remover um arquivo, para que você possa pular todos os arquivos que deseja manter, exceto aquele.

Boa sorte!

antje-m
fonte
1
Eu suspeito que rm -i *não teria funcionado neste caso; por causa das novas linhas, o shell se expandiria *para algo que rmnão seria reconhecido como o nome do arquivo.
Keith Thompson
6
@KeithThompson: A expansão de globos no shell não está sujeita a mais interpretações. Não haveria uma nova linha no nome expandido, a menos que contivesse uma.
Christoffer Hammarström
@ ChristofferHammarström: Hmm. Vou fazer algumas experiências e comentar mais.
21412 Keith Thompson
@ ChristofferHammarström: Há uma nova linha no nome expandido porque o nome do arquivo contém caracteres de nova linha. Mas parece que eu subestimei bashe GNU rm. No meu sistema, rm *, rm -i *, e rm Ctrl-V DEL TAB ENTERtodo o trabalho corretamente, mesmo que o nome do arquivo contém um caractere de nova linha (eu usei "\177foo\nbar\n"). Alguns comandos não lidam bem com esses nomes de arquivos, mas as ferramentas GNU parecem robustas; versões não GNU das mesmas ferramentas também podem não se comportar. De qualquer forma, é útil conhecer os vários truques nessas respostas.
Keith Thompson
1
@KeithThompson, qualquer shell manipulará o globbing de arquivos corretamente, não apenas o Bash, e rmnunca fará nenhuma forma de divisão de palavras, seja GNU rmou não. O shell expande os globs e, quando executa o comando solicitado, passa nos nomes de arquivos resultantes como argumentos separados por nulo (no código C). O que é perigoso é canalizar uma lista de arquivos para algo que use novas linhas como delimitadores; consulte Por que repetir as práticas inadequadas de saída do find?
Curinga
23

Para aqueles que usam, vimexecute-o no diretório de trabalho atual:

$ vim ./ 

e navegue até o arquivo com as teclas de seta ou j/k. Em seguida, pressione Shift+De confirme a exclusão com y.


fonte
Eu geralmente fico com medo de vim, mas que trabalhou muito bem
tofutim
9

Provavelmente, existe uma maneira de passar o nome do arquivo rm, mas se você estiver preocupado em estragar tudo, poderá usar um gerenciador de arquivos da GUI. O Emacs vem com um modo de edição de diretório que você pode usar se tiver:

  1. Abra a pasta em emacs. Você pode executar emacs /path/to/folderou abrir o emacs, pressionar Esc+ xe executar dired, o que solicitará o caminho

    http://so.mrozekma.com/unix-dired-delete1.png

  2. Vá para a linha com o arquivo que deseja excluir e pressione d. Você deve ver um Dna margem esquerda ao lado do arquivo:

    http://so.mrozekma.com/unix-dired-delete2.png

  3. Pressione xpara salvar suas alterações. Ele será solicitado para garantir que você deseja excluir o arquivo; pressione y:

    http://so.mrozekma.com/unix-dired-delete3.png

Michael Mrozek
fonte
1
Parece uma ótima idéia, mas não consigo encontrar emacs nesta máquina. Correndo o risco de iniciar uma guerra santa ... isso pode ser feito no vim?
Moose
2
Para aqueles que usam, vimexecute-o no diretório de trabalho atual: vim ./e navegue até o arquivo com as teclas de seta. Em seguida, pressione Shift+De confirme a exclusão com y.
@hesse Wow. Não é algo que eu esperaria vimapoiar
Michael Mrozek
1
@MichaelMrozek concordou, tem todos esses recursos que eu tendem a descobrir localmente.
1
@ Kevin modo nyan: nyan-mode.buildsomethingamazing.com
Tim
8

Você já demonstrou com file *que o shell glob ( *) é capaz de pegar esse arquivo, então agora basta fazer um rmnesse glob.

  • Mude para o diretório em que o arquivo está.
  • Corre rm -i *
  • Digite ntodos os arquivos, exceto aquele com o nome aparentemente invisível
Patrick
fonte
7

O arquivo realmente tem um nome, mas é composto de caracteres não imprimíveis.

Um método fácil é confiar na conclusão do shell: pressione Tabrepetidamente até que o nome do arquivo "estranho" seja inserido. Você deve ter seu shell configurado para alternar entre possíveis conclusões:

  • No bash, você precisa chamar menu-complete, que não está vinculado por padrão, e não completeque está vinculado Tabpor padrão. Como alternativa, use Esc *para inserir conclusões para todos os arquivos e remova os que não deseja excluir da linha de comando.
  • No zsh, as configurações padrão são boas; você precisa ter setopt auto_menuou setopt menu_completedefinir.

Se a conclusão não for útil em seu shell, você poderá ligar rm -i -- *e responder "não", exceto para esse arquivo específico. Se o nome do arquivo não começar com um caractere imprimível, você poderá restringir as correspondências a arquivos cujo primeiro caractere não esteja em um conjunto de caracteres imprimíveis:

rm -i [!!-~]*
rm -i [![:print:]]*
rm -i -- [!a-z]

(Cuidado se seu padrão pode corresponder a um arquivo chamado -f, que rmseria tratado como uma opção.)

Outro método é chamar ls -ipara localizar o inode do arquivo (um inode identifica exclusivamente um arquivo em um determinado sistema de arquivos) e depois find -inumoperar nesse arquivo específico. Este método é útil principalmente quando o diretório contém muitos arquivos.

ls -i
# note the inode number 12345
find . -xdev -inum 12345 -delete

Se o seu findnão tiver uma -deleteopção, ligue para rm:

find . -xdev -inum 12345 -exec rm -- {} \;

(Sim, eu sei que a maior parte disso já foi publicada. Mas as respostas com as informações relevantes estão tornando isso mais complicado do que deveria ser.)

Gilles 'SO- parar de ser mau'
fonte
4

Tente usar echo -epara obter o personagem \177e depois xargspassá-lo para rm. echonão usa escapes decimais, então converta para hex: Acontece que 177é octal; echorequer um \0(literal, não nulo byte) antes:

echo -ne '\0177\0' | xargs -0 rm
Kevin
fonte
Você está dizendo que \ xb1 \ 0 é a representação hexadecimal de \ 177?
Moose
@MrMoose \xb1é decimal 177, \0nula-o e -0diz xargspara usar um nome nulo-terminado em vez de um nome terminado por nova linha.
Kevin
Estou usando o bash no SunOS e, quando tentei seu comando, obtive xargs: opção ilegal - 0. Olhei para a página do manual e não consegui ver uma opção para encerramento nulo. Eu consegui uma correção para o problema agora. Veja a resposta marcada como resposta.
Moose
Seu echo, como está, envia 2 nomes de arquivos para xargs e depois para rm... o segundo nome de arquivo é \n.. Isso resulta em um erro ou na exclusão de um arquivo com esse nome ('\ n')
Peter. O
@perer Sim, acabei de perceber isso. Eu adicionei -npara me livrar da nova linha.
Kevin
3

Eu posso apenas sobre a garantia que o arquivo não tem um nome. Por acaso é um nome que contém caracteres não imprimíveis.

Se rmnão funcionou, usar a saída de findcom a -inumopção como argumento rmprovavelmente não ajudará; só vai passar o mesmo argumento para rm, com o mesmo problema.

O arquivo é o único nesse diretório, certo?

A primeira coisa que eu tentaria é cdentrar no diretório, digitar rm ./e pressionar a tecla Tab. Isso deve expandir o argumento para o nome real do arquivo e o anterior ./deve impedir a rminterpretação do argumento como algo diferente de um nome de arquivo.

Outra solução é cdacessar o diretório pai e, em seguida, usar rm -r(ou, se necessário rm -rf) no diretório. Isso deve remover o diretório e todo o seu conteúdo, independentemente do nome. Você pode usar mkdirpara recriar o diretório (e talvez chmodrestaurar suas permissões).

EDIT:

Analisando sua ls -lbsaída novamente, acho que o nome do arquivo começa com um DELcaractere ASCII (que é ruim, não fatal) e contém um ou mais caracteres de nova linha (o que é realmente ruim). Até onde eu sei, o rmcomando não pode interpretar uma nova linha como parte de um nome de arquivo.

Mas a unlink()chamada do sistema pode. Aqui está um script Perl que criei que irá percorrer os arquivos no diretório atual e perguntar a cada um se você deseja removê-lo. Ele não usa um comando externo como rmpara remover os arquivos, portanto, deve ser capaz de lidar com qualquer caractere engraçado suportado pelo sistema de arquivos (qualquer coisa que não seja ASCII NULe /).

Fazer rm -rou rm -rfno diretório contendo ainda deve funcionar, mas se você quiser apenas remover um único arquivo, pode tentar isso.

#!/usr/bin/perl

use strict;
use warnings;

opendir my $DIR, '.' or die ".: $!\n";
my @files = sort grep { -f } readdir $DIR;
closedir $DIR;

foreach my $file (@files) {
    my $pfile = printable($file);
    print "Remove $pfile? ";
    my $answer = scalar <STDIN>;
    if ($answer =~ /^[Yy]/) {
        print "Removing $pfile\n";
        unlink $file or warn "Unable to remove $pfile: $!\n";
    }
}

sub printable {
    my($s) = @_;
    $s =~ s/[^ -~]/?/g;
    return $s;
}

(Outra solução, se houver outros arquivos no diretório que você deseja manter, é mover todos os outros arquivos para um diretório temporário, em seguida, nuke e recrie o diretório e mova os outros arquivos de volta.)

(Ah, e o que você fez para criar esse arquivo, tente não fazê-lo novamente!)

Keith Thompson
fonte
1

Se você puder encontrar exatamente esse arquivo usando o findcomando, poderá usar o -deletepredicado. Apenas tome cuidado, e faça -deleteum último parâmetro do comando find ou isso vai doer muito ...

Michał Šrajer
fonte
1

Você está perto. Passo a passo, veja como excluir o arquivo pelo número do inode.

Primeiro, encontre o número do inode do arquivo que você deseja, usando ls -li. O número do inode estará na primeira coluna da saída.

Por exemplo:

$ls -li
311010 -rw-rw-r-- 1 me me 3995 Apr  6 16:27 -???\# ;-)

Nesse caso, o número do inode é 311010

Use findpara procurar o arquivo pelo número do inode.

find . -inum 311010 

Verifique se findretorna apenas o nome do arquivo que você deseja excluir. Por exemplo:

$find . -inum 311010
./-???\# ;-)

Quando tiver a certeza que findé encontrar o arquivo certo, em seguida, passar o -deleteparâmetro para find. Isso excluirá o arquivo para você.

find . -inum 311010 -delete
Christian Long
fonte
0

As outras respostas são ótimas e funcionarão em quase todos os ambientes.

Mas, se houver uma GUI em execução, basta abrir o diretório no nautilus, dolphin, konqueror ou seu gerenciador de arquivos favorito e você poderá ver e excluir facilmente qualquer arquivo que tenha um nome não cooperativo com apenas alguns cliques do mouse .

Se você não é um usuário regular do emacs ou vim (como mostrado em várias outras respostas), esse pode ser o caminho mais fácil.

Joe
fonte
0

Ocorreu um problema semelhante com um arquivo chamado "\ 033" (o caractere "Escape" real).

for x in $( ls | awk '!/^[A-Z]/ && !/^[a-z]/ && !/^[0-9]/');do rm -i -- $x ; done

O texto acima solicitará que você remova os arquivos no diretório atual que não iniciam com uma letra ou número.

Signal15
fonte
0

Em vez disso, você pode renomear o diretório atual para DirX_OLD, criar um novo diretório com o nome DirXe mover todos os arquivos necessários de DirX_OLDpara DirX. Após copiar os arquivos, você pode excluirDirX_OLD

user106382
fonte
0

Para o caso de ajudar alguém, vou acrescentar meus dois centavos. Consegui obter o (s) nome (s) do arquivo usando stato curinga assim:

stat -c %N *

Resultado:

`\220g\201\acontexts'
`\220g\201\alogins'
`\220g\201\amodules'
`\220g\201\apolicy'
`\220g\201\asetrans.conf'

Então eu poderia usar seqüências de caracteres C-C citadas ANSI (como usadas na resposta aceita) para acessar os arquivos:

rm $'\220g\201\asetrans.conf'
miken32
fonte
0

Todo arquivo tem um nome, você só precisa encontrar exatamente qual.

Vou usar esses arquivos como exemplos:

$ touch $'\177' $'\001\002' $'\033\027' a b abc $'a\177b'

Esses arquivos serão mostrados como ?no normal ls:

$ ls
??  ?  ?002  a  abc  b

Mas se você lspermitir a -Qopção, deve ficar claro qual é o nome do arquivo real:

$ ls -1iQ *
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"
26739118 "a"
26739121 "a\177b"
26739120 "abc"
26739119 "b"

esses são os números dos inodes e os nomes "Quoted" na 1coluna dos arquivos em pwd.

Isso está usando a habilidade de um shell (POSIX) para usar expansões (globbing):

Para ter os nomes com qualquer caractere não imprimível:

$ ls -1iQ *[![:print:]]*
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"
26739121 "a\177b"

E para listar arquivos que podem ter apenas caracteres não imprimíveis:

$ ls -1iQ [![:print:]]*
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"

Para -iremovê-los seletivamente (a opção (interativa)), use:

$ rm -i [![:print:]]*
rm: remove regular file ''$'\001\002'? n
rm: remove regular file ''$'\033\027'? n
rm: remove regular file ''$'\177'? n

Você só precisa escolher quais apagar, respondendo yà pergunta acima.

Isaac
fonte
0

Se você tem um bash com preenchimento automático, isso pode funcionar para você. Digitei o caractere que foi mostrado na lista de diretórios. Em seguida, ativei o preenchimento automático e renomei o item para algo imprimível.

Aqui está o que funcionou para mim:

$ ll
-rw-r--r--.  1 xxxxxxxx  xxxxx 25608 Oct 11 11:11 ?
$ mv ?  
(hit the [tab] and it gets extended to ...)
$ mv ^[
(now complete the command and rename to something printable)
$ mv ^[ weirdchar
$ ll
-rw-r--r--.  1 xxxxxxxx  xxxxx 25608 Oct 11 11:11 weirdchar
(now delete)
$ rm weirdchar
Sascha
fonte
-4
rm -dfvr /"(null)" 

trabalhou para mim.

Ryan S
fonte