Come posso eliminare un file il cui nome file ha caratteri non stampabili


46

In qualche modo sono riuscito a creare un file che non sembra avere un nome file. Ho trovato alcune informazioni su come ottenere maggiori dettagli sul file nel seguente thread.

Tuttavia, ho provato alcuni dei suggerimenti elencati e non riesco a eliminare il file. Non sono sicuro di cosa ho fatto per crearlo, ma è successo mentre cercavo di copiare un file XML.

Alcune informazioni sul file sono le seguenti;

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

> file *
:               XML document

> ls -i
 417777   

Ho cercato di trovare usando l'interruttore inum e poi convogliarlo in modo che sembrasse il modo più sicuro di sbarazzarsene. Tuttavia, l'esempio fornito nella parte inferiore del thread collegato di seguito non è riuscito per me. L'esempio era:

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

quindi ho provato a usare prima l'elenco dei percorsi come il seguente, ma neanche quello ha funzionato:

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

Non sono sicuro di quale sia il carattere non stampabile \ 177 o come posso passarlo a un rmcomando, ma voglio davvero assicurarmi di non rovinare altri file / directory nel mio tentativo di eliminare questo file.


1
\177è il DELcarattere ASCII .
Keith Thompson,

9
417777! = 41777
un CVn

Ottimo punto Michele. Questo è probabilmente il motivo per cui il mio comando non ha mai funzionato.
Moose,

@KeithThompson È ottale senza un vantaggio 0?
gatto

1
@cat: in questo contesto, sì. Segue la sintassi C per i letterali di stringhe e caratteri.
Keith Thompson,

Risposte:


46

Il file ha un nome, ma è composto da caratteri non stampabili. Se usi ksh93, bash, zsh, mksh o FreeBSD sh, puoi provare a rimuoverlo specificando il suo nome non stampabile. Per prima cosa assicurati che il nome sia giusto con: ls -ld $'\177' Se mostra il file giusto, quindi usa rm:rm $'\177'

Un altro approccio (un po 'più rischioso) è usare rm -i -- *. Con l'opzione -i rm è necessaria la conferma prima di rimuovere un file, quindi è possibile saltare tutti i file che si desidera conservare tranne quello.

In bocca al lupo!


1
Sospetto che rm -i *non avrebbe funzionato in questo caso; a causa delle nuove righe, la shell si espanderebbe *in qualcosa che rmnon riconoscerebbe come nome del file.
Keith Thompson,

6
@KeithThompson: l'espansione globale nella shell non è soggetta a ulteriori interpretazioni. Non ci sarebbe una nuova riga nel nome espanso se non ne contenesse uno.
Christoffer Hammarström,

@ ChristofferHammarström: Hmm. Farò alcuni esperimenti e commenterò ulteriormente.
Keith Thompson,

@ ChristofferHammarström: c'è una nuova riga nel nome espanso perché il nome del file contiene caratteri di nuova riga. Ma sembra che ho sottovalutato bashe GNU rm. Sul mio sistema, rm *, rm -i *, e rm Ctrl-V DEL TAB ENTERtutto il lavoro correttamente, anche se il nome del file contiene un carattere di fine riga (io ho usato "\177foo\nbar\n"). Alcuni comandi non gestiscono bene tali nomi di file, ma gli strumenti GNU sembrano robusti; Le versioni non GNU degli stessi strumenti potrebbero non comportarsi altrettanto. In ogni caso, i vari trucchi di queste risposte sono utili da sapere.
Keith Thompson,

1
@KeithThompson, qualsiasi shell gestirà correttamente il globbing dei file, non solo Bash, e rmnon farà mai alcuna forma di suddivisione delle parole sia che sia GNU rmo meno. La shell espande i globs e, quando esegue il comando richiesto, passa i nomi dei file risultanti come argomenti separati da null (nel codice C). Ciò che è pericoloso è reindirizzare un elenco di file a qualcosa che utilizza le nuove righe come delimitatori; vedi Perché il looping sull'output di find è una cattiva pratica?
Wildcard il

23

Per coloro che usano vimeseguirlo nella directory di lavoro corrente:

$ vim ./ 

e vai al file con i tasti freccia o j/k. Quindi premi Shift+De conferma l'eliminazione con y.


generalmente mi spavento di VIM, ma ha funzionato davvero bene
fino

9

Probabilmente c'è un modo per passare il nome file rm, ma se sei preoccupato di rovinare qualcosa puoi usare un file manager della GUI. Emacs viene fornito con una modalità di modifica delle directory che è possibile utilizzare se è installata:

  1. Apri la cartella in emacs. Puoi eseguire emacs /path/to/foldero aprire emacs, premere Esc+ xed eseguire dired, che richiederà il percorso

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

  2. Passare alla riga con il file che si desidera eliminare e premere d. Dovresti vedere a Dnel margine sinistro accanto al file:

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

  3. Premere xper salvare le modifiche. Ti verrà richiesto di assicurarti di voler eliminare il file; premere y:

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


1
Sembra un'ottima idea, ma non riesco a trovare emacs su questa macchina. A rischio di iniziare una guerra santa ... questo può essere fatto in modo vim?
Moose,

2
Per coloro che usano vimeseguirlo nella directory di lavoro corrente: vim ./e navigare al file con i tasti freccia. Quindi premi Shift+De conferma l'eliminazione con y.

@hesse Wow. Non qualcosa che mi sarei aspettato vimdi supportare
Michael Mrozek

1
@MichaelMrozek ha concordato, ha tutte queste caratteristiche che occasionalmente tendo a scoprire.


8

Hai già dimostrato file *che shell glob ( *) è in grado di prelevare questo file, quindi ora rmesegui semplicemente quel glob.

  • Passare alla directory in cui si trova il file.
  • Correre rm -i *
  • Immettere nper tutti i file tranne quello con il nome del file apparentemente invisibile

7

Il file in realtà ha un nome, ma è composto da caratteri non stampabili.

Un metodo semplice è fare affidamento sul completamento della shell: premere Tabpiù volte fino a quando non viene inserito il nome "strano" del file. È necessario che la shell sia configurata per scorrere tra i possibili completamenti:

  • In bash, è necessario invocare menu-complete, che non è associato per impostazione predefinita, piuttosto che complete, a cui è associato per Tabimpostazione predefinita. In alternativa, utilizzare Esc *per inserire completamenti per tutti i file e rimuovere quelli che non si desidera eliminare dalla riga di comando.
  • In zsh, le impostazioni predefinite vanno bene; devi avere setopt auto_menuo setopt menu_completeimpostare.

Se il completamento non è utile nella shell, è possibile chiamare rm -i -- *e rispondere "no" ad eccezione di questo file specifico. Se il nome del file non inizia con un carattere stampabile, puoi limitare le corrispondenze ai file il cui primo carattere non è in un set di caratteri stampabili:

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

(Fai attenzione se il tuo modello può corrispondere a un file chiamato -f, che rmpotrebbe essere considerato un'opzione.)

Un altro metodo consiste nel chiamare ls -iper trovare l'inode del file (un inode identifica in modo univoco un file su un determinato filesystem), quindi find -inumper operare su quel particolare file. Questo metodo è utile soprattutto quando la directory contiene molti file.

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

Se il tuo findnon ha -deleteun'opzione, chiama rm:

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

(Sì, lo so che gran parte di questo è già stato pubblicato. Ma le risposte con le informazioni pertinenti lo stanno rendendo più complicato di quanto dovrebbe essere.)


4

Prova a usare echo -eper ottenere il personaggio \177, quindi xargspassalo a rm. echonon prende fughe decimali, quindi convertito esadecimale: scopre la 177è ottale; echorichiede un \0(letterale, non nullo byte) prima di:

echo -ne '\0177\0' | xargs -0 rm

Stai dicendo che \ xb1 \ 0 è la rappresentazione esadecimale di \ 177?
Moose,

@MrMoose \xb1è decimale 177, \0lo termina con null e -0dice xargsdi prendere un nome con terminazione null anziché uno con terminazione di nuova riga.
Kevin,

Sto usando bash su SunOS e quando ho provato il tuo comando, ho ottenuto xargs: opzione illegale - 0. Ho guardato la pagina man e non ho visto un'opzione per la terminazione nulla. Ora sono riuscito a ottenere una soluzione per il problema. Vedi la risposta contrassegnata come risposta.
Mr Moose,

Il tuo echo, così com'è, invia 2 nomi di file a xargs, e poi su rm... il secondo nome di file è \n.. Ciò provoca un errore o elimina un file con quel nome ('\ n')
Peter. O

@perer Sì, l'ho appena notato. Ho aggiunto -nper sbarazzarsi della newline.
Kevin,

3

Posso solo su di te garantire che il file ha un nome. Capita solo di essere un nome che contiene caratteri non stampabili.

Se rmnon ha funzionato, utilizzare l'output di findcon l' -inumopzione come argomento per rmnon è di grande aiuto; passerà solo lo stesso argomento rm, con lo stesso problema.

Il file è l'unico in quella directory, giusto?

La prima cosa che proverei è cdentrare nella directory, quindi digitare rm ./, quindi premere il tasto Tab. Ciò dovrebbe espandere l'argomento al nome effettivo del file e il precedente ./dovrebbe impedire rmdi interpretare l'argomento come qualcosa di diverso da un nome di file.

Un'altra soluzione è quella cddi accedere alla directory principale, quindi utilizzare rm -r(o, se necessario, rm -rf) nella directory. Ciò dovrebbe rimuovere la directory e tutti i suoi contenuti, indipendentemente dal nome. È quindi possibile utilizzare mkdirper ricreare la directory (e forse chmodper ripristinarne le autorizzazioni).

MODIFICARE :

Guardando di ls -lbnuovo l'output, penso che il nome del file inizi con un DELcarattere ASCII (che non è male, non fatale) e contiene uno o più caratteri di nuova riga (che è davvero cattivo). Per quanto ne so, il rmcomando non può interpretare una nuova riga come parte del nome di un file.

Ma la unlink()chiamata di sistema può. Ecco uno script Perl che ho messo insieme che ripeterà i file nella directory corrente e ti chiederà per ognuno se vuoi rimuoverlo. Non utilizza un comando esterno come rmrimuovere i file, quindi dovrebbe essere in grado di gestire qualsiasi personaggio divertente supportato dal file system (qualsiasi cosa diversa da ASCII NULe /).

Fare rm -ro rm -rfsulla directory di contenimento dovrebbe comunque funzionare, ma se vuoi solo rimuovere un singolo file puoi provare questo.

#!/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;
}

(Un'altra soluzione, se ci sono altri file nella directory che si desidera conservare, è spostare tutti gli altri file in una directory temporanea, quindi eseguire il nuke e ricreare la directory e spostare indietro gli altri file.)

(Oh, e qualunque cosa tu abbia fatto per creare quel file, prova a non farlo più!)


1

Se riesci a trovare esattamente quel file usando il findcomando, puoi usare -deletepredicato. Fai solo attenzione e crea -deleteun ultimo parametro del comando find, altrimenti ti farà molto male ...


1

Sei vicino. Passo dopo passo, ecco come eliminare il file per numero di inode.

Innanzitutto, trova il numero di inode del file desiderato, utilizzando ls -li. Il numero dell'inode sarà nella prima colonna dell'output.

Per esempio:

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

In questo caso, il numero di inode è 311010

Utilizzare findper cercare il file in base al numero di inode.

find . -inum 311010 

Assicurarsi che findrestituisca solo il nome del file che si desidera eliminare. Per esempio:

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

Una volta che sei sicuro di findtrovare il file giusto, passa il -deleteparametro a find. Eliminerà il file per te.

find . -inum 311010 -delete

0

Le altre risposte sono fantastiche e funzioneranno in quasi tutti gli ambienti.

Ma, se ti capita di avere una gui in esecuzione, apri la directory in nautilus, dolphin, konqueror o il tuo file manager preferito e dovresti essere in grado di vedere ed eliminare facilmente qualsiasi file che ha un nome non collaborativo con un paio di clic del mouse .

Se non sei un normale utente di emacs o vim (come indicato in molte altre risposte), questo potrebbe essere il modo più semplice di procedere.


0

Si è verificato un problema simile con un file denominato "\ 033" (l'attuale carattere "Escape").

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

Quanto sopra ti chiederà di rimuovere i file nella directory corrente che non iniziano con una lettera o un numero.


0

Invece, puoi rinominare la directory corrente in DirX_OLD, creare una nuova directory con il nome DirXe spostare tutti i file richiesti da DirX_OLDa DirX. Dopo aver copiato i file è possibile eliminareDirX_OLD


0

Nel caso in cui aiuti qualcuno aggiungerò i miei due centesimi. Sono stato in grado di ottenere i nomi dei file utilizzando statil carattere jolly in questo modo:

stat -c %N *

Produzione:

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

Quindi potrei usare le stringhe tra virgolette C ANSI (usate nella risposta accettata) per accedere ai file:

rm $'\220g\201\asetrans.conf'

0

Ogni file ha un nome, devi solo trovare esattamente quale.

Userò questi file come esempi:

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

Quei file mostreranno come ?al solito ls:

$ ls
??  ?  ?002  a  abc  b

Ma se lspermetti l' -Qopzione, dovrebbe essere chiaro quale sia il vero nome del file:

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

quelli sono i numeri di inode e i nomi "Citati" nella 1colonna dei file in pwd.

Quello sta usando l'abilità di una shell (POSIX) per usare le espansioni (globbing):

Per avere i nomi con qualsiasi carattere non stampabile:

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

E per elencare i file che possono avere solo caratteri non stampabili:

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

Quindi, per -irimuovere selettivamente (l' opzione (interattiva)) rimuoverli utilizzare:

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

Devi solo scegliere quelli da cancellare rispondendo yalla domanda sopra.


0

Se hai una bash con completamento automatico, questo potrebbe funzionare per te. Ho digitato il carattere mostrato nell'elenco delle directory. Quindi ho attivato il completamento automatico e ho rinominato la cosa in qualcosa di stampabile.

Ecco cosa ha funzionato per me:

$ 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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.