Ci sono degli svantaggi nell'uso di rm $ (ls) per eliminare i file?


13

Mi chiedevo se usare rm $(ls)per cancellare file (o anche rm -r $(ls)per cancellare directory) fosse sicuro? Perché in tutti i siti Web, le persone offrono altri modi per farlo anche se questo comando sembra molto più semplice di altri comandi.


3
Risposta di base, no. Non riesce a gestire caratteri speciali. Posso scrivere una risposta tra un po 'per spiegarlo più in dettaglio
Sergiy Kolodyazhnyy,

5
touch 'foo -r .. bar'; rm $(ls); dov'è finita la mia directory principale? Inoltre, che tipo di alternative stai vedendo che sono ancora più complicate di così? rm *è molto più facile da scrivere e pensare, e più sicuro (ma non perfettamente sicuro; vedi la risposta di Dennis).
Peter Cordes,

1
Oltre alle risposte eccellenti, tenere presente che lspuò variare tra implementazioni e quindi non è standard. A seconda di ciò di cui hai bisogno, considera alternative come finde stat. Dovresti usarlo lssolo per il consumo umano, mai per altri comandi o script.
Paddy Landau,

Risposte:


7

Cosa si intende fare?

  • ls elenca i file nella directory corrente
  • $(ls)sostituisce l'output di lsluoghi che come argomentorm
  • Essenzialmente ha lo rm $(ls)scopo di eliminare tutti i file nella directory corrente

Cosa c'è che non va in questa immagine?

lsimpossibile gestire correttamente caratteri speciali nel nome file. Gli utenti di Unix generalmente consigliano di utilizzare approcci diversi . Ho anche dimostrato che in una domanda correlata sul conteggio dei nomi dei file . Per esempio:

$ touch file$'\n'name                                                                                                    
$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ 

Inoltre, come correttamente menzionato nella risposta di Denis, un nome di file con trattini iniziali potrebbe essere interpretato come argomento a rm dopo la sostituzione, il che vanifica lo scopo di rimuovere il nome file.

Cosa funziona

Si desidera eliminare i file nella directory corrente. Quindi usa glob rm *:

$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ rm *
$ ls
$ 

Puoi usare il findcomando. Questo strumento è spesso consigliato non solo per la directory corrente: può attraversare ricorsivamente l'intero albero di directory e operare sui file tramite-exec . . .{} \;

$ touch "file name"                                
$ find . -maxdepth 1 -mindepth 1                                                                                         
./file name
$ find . -maxdepth 1 -mindepth 1 -exec rm {} \;                                                                          
$ ls
$ 

Python non ha problemi con caratteri speciali nei nomi dei file, quindi potremmo anche impiegarlo (nota che questo è solo per i file, dovrai usare os.rmdir() e os.path.isdir()se vuoi operare su directory):

python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

In effetti, il comando sopra potrebbe essere trasformato in funzione o alias in ~/.bashrc per brevità. Per esempio,

rm_stuff()
{
    # Clears all files in the current working directory
    python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

}

La versione di Perl sarebbe

perl -e 'use Cwd;my $d=cwd();opendir(DIR,$d); while ( my $f = readdir(DIR)){ unlink $f;}; closedir(DIR)'

1
Il tuo "$(ls)"esempio funziona solo se nella directory è presente un solo file. Potresti anche usare il completamento con tabulazione per espandere il nome del file poiché è l'unico completamento.
Peter Cordes,

@PeterCordes infatti, per una strana ragione funziona solo con un file. Questo è un altro argomento contro l'uso lsallora :) Lo modificherò
Sergiy Kolodyazhnyy,

Non la definirei una strana ragione: o citi $(ls)per disabilitare la divisione delle parole, o lasci che la divisione delle parole avvenga (con risultati disastrosi). L'unico modo pulito per passare un elenco di più stringhe senza trattare i dati come codice è con variabili di matrice o con \0come separatore, ma la shell stessa non può farlo. È ancora IFS=$'\n'il meno pericoloso, ma non può eguagliare find -print0 | xargs -0. Or grep -l --null. Oppure eviti l'intero problema con cose come find -exec rm {} +. (Notare il +passaggio di più argomenti a ciascuna invocazione di rm; MODO più efficiente).
Peter Cordes,

@PeterCordes Yup, totalmente d'accordo lì. Ma IFS=$'\n'fallirà anche in questo caso, dato che ho una nuova riga nel nome del file, quindi il wordplitting lo tratterà come due nomi di file anziché uno. La strana ragione, tuttavia, è il fatto che con default IFSche è spazio, tab, newline, originale rm "$(ls)"dovrebbe anche fallire, dovrebbe trattare come ho detto il nome del file come due separati, ma non è così. In genere io uso findcon -exec, o find . . .-print0 | while IFS= read -d'' FILENAME ; do . . . donela struttura di trattare con i nomi dei file. O si potrebbe usare python, ho aggiunto un esempio di questo.
Sergiy Kolodyazhnyy,

"$(ls)"si espande sempre in un argomento perché le virgolette proteggono l'espansione $(ls)dalla divisione delle parole. Proprio come proteggono "$foo".
Peter Cordes,

26

No, non è sicuro e l'alternativa comunemente usata rm * non è molto più sicura.

Ci sono molti problemi con rm $(ls). Come altri hanno già coperto nelle loro risposte, l'output di lsverrà suddiviso in caratteri presenti nel separatore di campo interno .

Nel migliore dei casi, semplicemente non funziona. Nel peggiore dei casi, intendevi rimuovere solo i file (ma non le directory) - o rimuovere selettivamente alcuni file con -i- ma c'è un file con il nome c -rfnella directory corrente. Vediamo cosa succede.

$ mkdir a
$ touch b
$ touch 'c -rf'
$ rm -i $(ls)
$ ls
c -rf

Il comando rm -i $(ls)doveva rimuovere solo i file e chiedere prima di rimuoverli tutti, ma il comando che alla fine era stato eseguito leggeva

rm -i a b c -rf

quindi ha fatto qualcos'altro del tutto.

Nota che rm *è solo leggermente migliore. Con la struttura di directory come prima, si comporterà come previsto qui, ma se hai un file chiamato -rf, sei ancora sfortunato.

$ mkdir a
$ touch b
$ touch ./-rf
$ rm -i *
$ ls
-rf

Esistono diverse alternative migliori. I più semplici coinvolgono solo rm e globbing.

  • Il comando

    rm -- *
    

    funzionerà esattamente come previsto, dove -- segnala che tutto ciò che lo segue non deve essere interpretato come un'opzione.

    Questo fa parte delle linee guida sulla sintassi dell'utilità POSIX da oltre due decenni. È molto diffuso, ma non dovresti aspettarti che sia presente ovunque.

  • Il comando

    rm ./*
    

    fa espandere il glob in modo diverso e quindi non richiede alcun supporto dall'utilità chiamata.

    Per il mio esempio dall'alto, puoi vedere il comando che alla fine verrà eseguito anteponendo l' eco .

    $ echo rm ./*
    rm ./a ./b ./-rf
    

    Il ./comando principale impedisce a rm di trattare accidentalmente uno qualsiasi dei nomi di file come opzioni.


1
Ottimo punto, i nomi dei file con - dopo l'espansione diventano flag per rm. +1
Sergiy Kolodyazhnyy,

1
Anche nastier: touch 'foo -rf .. bar. Non credo che un utente malintenzionato possa raggiungere un livello superiore alla directory principale, a meno che non sia possibile produrre un separatore di percorso lsnell'output.
Peter Cordes,

L'attaccante di @Peter dovrebbe avere i permessi di scrittura per rimuovere la directory dei brevetti in primo luogo, no?
Sergiy Kolodyazhnyy,

1
@PeterCordes Non sono sicuro che tutte le versioni di rm abbiano questo fail-safe, ma su Ubuntu, openSUSE e Fedora, dice rm: refusing to remove '.' or '..' directory: skipping '..'o qualcosa di simile quando si tenta di rimuovere la directory principale.
Dennis,

@Serg: l'attaccante ti manda solo un .zip con quel nome di file e ti permette di spararti nel piede estraendolo e quindi cercando di eliminare il contenuto. O creando quel nome file in / var / tmp o qualcosa del genere.
Peter Cordes,
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.