Gibt es irgendwelche Nachteile bei der Verwendung von rm $ (ls) zum Löschen von Dateien?

13

Ich habe mich gefragt, ob die Verwendung rm $(ls)zum Löschen von Dateien (oder auch rm -r $(ls)zum Löschen von Verzeichnissen) sicher ist. Weil auf allen Websites andere Möglichkeiten zur Verfügung stehen, obwohl dieser Befehl viel einfacher zu sein scheint als andere Befehle.

posixKing
quelle
3
Grundlegende Antwort, nein. ls kann keine Sonderzeichen verarbeiten. Ich kann kurz eine Antwort schreiben, um dies näher zu erläutern
Sergiy Kolodyazhnyy
5
touch 'foo -r .. bar'; rm $(ls); Wo ist mein Elternverzeichnis geblieben? Welche Alternativen sind für Sie noch komplizierter? rm *Es ist viel einfacher zu tippen und darüber nachzudenken und sicherer (aber nicht absolut sicher; siehe Dennis 'Antwort).
Peter Cordes
1
Beachten Sie zusätzlich zu den hervorragenden Antworten, dass lsdiese von Implementierung zu Implementierung variieren können und daher nicht dem Standard entsprechen. Überlegen Sie sich je nach Bedarf Alternativen wie findund stat. Sie sollten lsnur für den menschlichen Verzehr verwendet werden, niemals für die Verwendung durch andere Befehle oder in Skripten.
Paddy Landau

Antworten:

7

Was soll das tun?

  • ls listet Dateien im aktuellen Verzeichnis auf
  • $(ls)ersetzt die Ausgabe von lsOrten, die als Argument fürrm
  • Im Wesentlichen rm $(ls)sollen alle Dateien im aktuellen Verzeichnis gelöscht werden

Was ist falsch mit diesem Bild ?

lsSonderzeichen im Dateinamen können nicht richtig behandelt werden. Unix-Benutzer empfahlen im Allgemeinen , unterschiedliche Ansätze zu verwenden . Ich habe das auch in einer verwandten Frage zum Zählen von Dateinamen gezeigt . Zum Beispiel:

$ 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
$ 

Wie in Denis 'Antwort richtig erwähnt, könnte ein Dateiname mit führenden Bindestrichen auch als Argument für rmafter substitution interpretiert werden , was den Zweck des Entfernens des Dateinamens zunichte macht.

Was funktioniert

Sie möchten Dateien im aktuellen Verzeichnis löschen. Also benutze 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
$ 

Sie können den findBefehl verwenden. Dieses Tool wird häufig nicht nur für aktuelle Verzeichnisse empfohlen. Es kann den gesamten Verzeichnisbaum rekursiv durchlaufen und Dateien über verarbeiten-exec . . .{} \;

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

Python hat keine Probleme mit Sonderzeichen in Dateinamen, daher können wir dies auch verwenden (beachten Sie, dass dies nur für Dateien gilt, die Sie verwenden müssen os.rmdir()und os.path.isdir()wenn Sie mit Verzeichnissen arbeiten möchten):

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

Tatsächlich könnte der obige Befehl der ~/.bashrcKürze halber in eine Funktion oder einen Alias ​​umgewandelt werden . Beispielsweise,

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) ]'

}

Perl-Version davon wäre

perl -e 'use Cwd;my $d=cwd();opendir(DIR,$d); while ( my $f = readdir(DIR)){ unlink $f;}; closedir(DIR)'
Sergiy Kolodyazhnyy
quelle
1
Ihr "$(ls)"Beispiel funktioniert nur, wenn sich nur eine Datei im Verzeichnis befindet. Sie können auch einfach die Tabulator-Vervollständigung verwenden, um den Dateinamen zu erweitern, da dies die einzige Vervollständigung ist.
Peter Cordes
@ PeterCordes in der Tat, aus einem merkwürdigen Grund funktioniert es nur mit einer Datei. Das ist ein weiteres Argument gegen die Verwendung von lsdann :) Ich werde es herausarbeiten
Sergiy Kolodyazhnyy
Ich würde es nicht als seltsamen Grund bezeichnen: Sie zitieren entweder $(ls), um die Worttrennung zu deaktivieren, oder Sie lassen die Worttrennung zu (mit katastrophalen Ergebnissen). Die einzige saubere Möglichkeit, eine Liste mit mehreren Zeichenfolgen zu durchlaufen, ohne Daten als Code zu behandeln, besteht in der Verwendung von Array-Variablen oder \0als Trennzeichen, aber die Shell selbst kann dies nicht. Ist IFS=$'\n'immer noch am wenigsten gefährlich, kann aber nicht mithalten find -print0 | xargs -0. Oder grep -l --null. Oder du vermeidest das ganze Problem mit Dingen wie find -exec rm {} +. (Beachten Sie +, dass für jeden Aufruf von rm mehrere Argumente übergeben werden müssen; WEGE effizienter).
Peter Cordes
@PeterCordes Yup, da war ich mir völlig einig. Aber IFS=$'\n'wird auch in diesem Fall scheitern, da ich eine neue Zeile im Dateinamen habe, so dass Wordsplitting es als zwei Dateinamen anstelle von einem behandelt. Der seltsame Grund ist jedoch die Tatsache, dass mit der Standardeinstellung " IFSLeerzeichen", "Tabulator", "Zeilenvorschub" und "Original" rm "$(ls)"ebenfalls ein Fehler auftreten sollte. Der Dateiname sollte wie gesagt als zwei separate behandelt werden, was jedoch nicht der Fall war . Normalerweise benutze ich findmit -execoder find . . .-print0 | while IFS= read -d'' FILENAME ; do . . . doneStruktur mit Dateinamen umgehen. Oder man könnte es gebrauchen python, ich habe ein Beispiel hinzugefügt.
Sergiy Kolodyazhnyy
"$(ls)"wird immer zu einem Argument erweitert, da die Anführungszeichen die Erweiterung von $(ls)vor der Wortspaltung schützen . So wie sie beschützen "$foo".
Peter Cordes
26

Nein, es ist nicht sicher und die häufig verwendete Alternative rm *ist nicht viel sicherer.

Es gibt viele Probleme mit rm $(ls). Wie andere bereits in ihren Antworten behandelt haben, wird die Ausgabe von lsnach Zeichen aufgeteilt, die im internen Feldtrennzeichen vorhanden sind .

Im besten Fall funktioniert es einfach nicht. Im schlimmsten Fall wollten Sie nur Dateien (aber keine Verzeichnisse) entfernen - oder selektiv einige Dateien mit entfernen -i-, aber c -rfim aktuellen Verzeichnis befindet sich eine Datei mit dem Namen . Mal sehen was passiert.

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

Der Befehl rm -i $(ls)sollte nur Dateien entfernen und vor dem Entfernen der einzelnen Dateien fragen, aber der Befehl, der letztendlich ausgeführt wurde, sollte gelesen werden

rm -i a b c -rf

so tat es etwas ganz anderes.

Beachten Sie, dass dies rm *nur unwesentlich besser ist. Mit der Verzeichnisstruktur wie zuvor verhält es sich hier wie vorgesehen, aber wenn Sie eine Datei mit dem Namen haben -rf, haben Sie immer noch Pech.

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

Es gibt mehrere bessere Alternativen. Die einfachsten beinhalten nur rm und globbing.

  • Der Befehl

    rm -- *
    

    funktioniert genau wie vorgesehen, wobei --signalisiert wird, dass alles danach nicht als Option interpretiert werden soll.

    Dies ist seit über zwei Jahrzehnten Teil der POSIX-Utility-Syntaxrichtlinien. Es ist weit verbreitet, aber Sie sollten nicht erwarten, dass es überall vorhanden ist.

  • Der Befehl

    rm ./*
    

    bewirkt, dass der Glob anders erweitert wird und daher keine Unterstützung durch das aufgerufene Dienstprogramm erforderlich ist.

    In meinem Beispiel von oben sehen Sie den Befehl, der letztendlich ausgeführt wird, indem das Echo vorangestellt wird .

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

    Der Zeilenabstand ./verhindert , dass rm versehentlich Dateinamen wie Optionen behandelt.

Dennis
quelle
1
Sehr guter Punkt, Dateinamen mit - werden nach Erweiterung zu Flags rm. +1
Sergiy Kolodyazhnyy
1
Auch fieser: touch 'foo -rf .. bar. Ich glaube nicht, dass ein Angreifer höher als das übergeordnete Verzeichnis werden kann, es sei denn, wir können in lsder Ausgabe ein Pfadtrennzeichen erzeugen.
Peter Cordes
@ Peter-Angreifer müssten über Schreibrechte verfügen, um das Patentverzeichnis überhaupt zu entfernen, oder?
Sergiy Kolodyazhnyy
1
@PeterCordes Ich bin mir nicht sicher, ob alle Versionen von rm dies ausfallsicher haben, aber unter Ubuntu, openSUSE und Fedora wird rm: refusing to remove '.' or '..' directory: skipping '..'beim Versuch, das übergeordnete Verzeichnis zu entfernen, etwas Ähnliches angezeigt .
Dennis
@ Serg: Der Angreifer sendet Ihnen nur eine .zip-Datei mit diesem Dateinamen und lässt Sie sich in den Fuß schießen, indem Sie ihn extrahieren und dann versuchen, den Inhalt zu löschen. Oder indem Sie diesen Dateinamen in / var / tmp oder so erstellen.
Peter Cordes