Ist das Zitieren von Dateinamen ausreichend sicher, um "xargs sudo rm -rf" auszuführen?

10

Ich habe ein Skript geschrieben, das alle außer den letzten beiden Dateien in einem Ordner löscht:

#!/bin/bash
ls -1 --quoting-style=shell-always /path/to/some/folder \
    | head -n -2 \
    | xargs printf -- "'/path/to/some/folder/%s'\n" \
    | xargs sudo rm -rf

Dieses Skript wird jeden Tag als Cron-Job ausgeführt.

Die Argumentation ist wie folgt:

  1. Erhalten Sie eine Liste aller Dateien mit ls -1(so dass ich eine Datei pro Zeile bekomme);

  2. Entfernen Sie die letzten beiden aus der Liste mit head -n -2;

  3. Da lsrelative Pfade gedruckt werden, xargs printfstellen Sie dem Ordnerpfad das Element voran und machen Sie ihn zu einem absoluten Pfad.

  4. Senden Sie sie an sudo rm -rfusing xargs.

Jeder hat Zugriff auf diesen Ordner, sodass jeder alle Dateien in diesem Ordner erstellen und löschen kann.

Das Problem ist: sudo rm -rf ist beängstigend. xargs sudo rm -rfist unglaublich beängstigend.

Ich möchte sichergehen, dass niemand andere Ordner / Systeme beschädigen kann, indem er clevere Dateien erstellt, die (versehentlich oder absichtlich) gelöscht werden sollen. Ich weiß nicht, etwas Kluges wie:

file with / spaces.txt

was zu einer super beängstigend führen könnte sudo rm -rf /.

EDIT: Mein Fehler, Dateinamen können nicht enthalten /, so dass dieses spezielle Problem nicht auftreten würde, aber die Frage, ob es andere Risiken gibt oder nicht, bleibt bestehen.

Aus diesem Grund verwende ich --quoting-style=shell-always, dies sollte Tricks mit Dateien mit Leerzeichen verhindern. Aber jetzt frage ich mich, ob jemand mit Leerzeichen und Anführungszeichen im Dateinamen vielleicht besonders klug sein könnte .

Ist mein Skript sicher?


Hinweis: Ich brauche, sudoweil ich auf den Ordner remote zugreife (von einem zugeordneten Netzwerklaufwerk mit mount) und ich ihn ohne sudo nicht zum Laufen bringen könnte.

Pedro A.
quelle
3
Hast du darüber nachgedacht, so etwas zu tun printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm?
Steeldriver
Kann eine Datei mit dem Zeichen /im Namen erstellt werden
?
3
@ George Nein, ein Dateiname darf keinen Schrägstrich enthalten.
Wjandrea
Also, als OP kluge Person sagt, habe ich mich gefragt ...
George Udosen
6
Nur weil Sie die lsAusgabe analysieren, ist dies selbst beim Zitieren bereits ein schlecht geschriebener Befehl. lsIch denke, es wird auch das Gebietsschema zum Sortieren der Reihenfolge verwendet, sodass ich nicht sehe, wozu die headletzten 2 entfernt werden sollen (es sei denn, Sie versuchen, sie zu entfernen, .und ..welche iirc sind rmsowieso nicht als Argumente zulässig . Verwenden Sie sie einfach find /path/to/folder -type f delete. Und Nein, sudowenn Sie von Cron laufen - Cron ist bereits auf Root-Ebene
Sergiy Kolodyazhnyy

Antworten:

10

Unter Linux ist jedes Zeichen ein gültiger Dateiname, der ein Zeichen darstellt, mit Ausnahme von:

  • \0 (ASCII NUL): wie für die Zeichenfolgenbeendigung in C verwendet
  • / (Schrägstrich): wie für die Pfadtrennung verwendet

Ihr Ansatz wird also in vielen Fällen definitiv nicht funktionieren, wie Sie sich vorstellen können, z. B. behandelt er eine neue Zeile ( \n) im Dateinamen? ( Hinweis: Nein ).

Einige Anmerkungen:

  • Analysiere nicht ls; Verwenden Sie spezielle Tools (für die meisten Anwendungsfälle gibt es mindestens eines).
  • Versuchen Sie beim Umgang mit Dateinamen, die NUL-getrennte Ausgabe zu nutzen, die von fast allen GNU-Tools bereitgestellt wird, die mit solchen Daten arbeiten
  • Achten Sie beim Verrohren darauf, dass beide Programme die NUL-Trennungen verstehen
  • Wann immer Sie anrufen xargs, prüfen Sie , ob Sie damit durchkommen können find ... -exec. In den meisten Fällen geht es Ihnen gut, wenn Sie findalleine sind

Ich denke, das wird dich jetzt zum Laufen bringen. steeldriver hat die NUL-getrennte Idee bereits im Kommentar ( printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm) angegeben. Verwenden Sie diese als Ausgangspunkt.

heemayl
quelle
Vielen Dank für Ihre Antwort :) Ich denke, Sie sollten den Kommentar von steeldriver zitieren, anstatt ihn nur zu erwähnen (da Kommentare nicht dauerhaft sind). Ich werde auch einen Blick darauf werfen find, danke für den Vorschlag.
Pedro A
Ich habe jedoch eine Frage: Ich verstehe nicht, was Sie unter "Ihr Ansatz wird in vielen Fällen definitiv nicht funktionieren, wie Sie sich vorstellen können" verstehen - "nicht funktionieren" wie unter "nicht sicher" oder "nicht unsicher"? Da sich "Ihr Ansatz" auf mich und nicht auf den böswilligen Benutzer bezieht und Ihre vorherigen Aussagen zu meinen Gunsten sind, bin ich verwirrt.
Pedro A
@PedroA Du bist du :) Wie ich bereits sagte, da alle Zeichen außer den beiden genannten gültig sind, solltest du dir vorstellen können ls, in welchen zahlreichen Fällen dein Parsing-Ansatz fehlschlagen würde, z. B. hast du eine neue Zeile im Dateinamen berücksichtigt?
Heemayl
Oh, eine neue Zeile im Dateinamen ... daran hatte ich nicht gedacht. Wenn es Ihnen nichts ausmacht, das auch zu Ihrer Antwort hinzuzufügen :) Tut mir leid zu fragen, aber was macht head -zdas? Es klingt lächerlich, aber ich habe mannoch infoin meinem CoreOS-Container Linux ... Konnte auch nicht im Internet finden. Ich bekommehead: invalid option 'z'
Pedro A
@PedroA Newline ist nur ein Fall, es gibt viele, wie Sie vielleicht erraten haben . Sie benötigen GNU head(wird mit GNU geliefert coreutils). Hier ist die Online - Version: manpages.ubuntu.com/manpages/xenial/man1/head.1.html
heemayl
3

xargs unterstützt einige Anführungszeichen: mit einfachen Anführungszeichen, doppelten Anführungszeichen oder Backslash, wodurch beliebige Argumente akzeptiert werden¹, jedoch mit einer Syntax, die sich von der Anführungszeichen-Syntax der Bourne-ähnlichen Shells unterscheidet.

Die GNU-Implementierung von lsUbuntu verfügt über keinen Anführungsmodus, der mit dem xargsEingabeformat kompatibel ist .

Es ls --quoting-style=shell-alwaysist kompatibel mit ksh93-, bash- und zsh-Shells, die die Syntax angeben, jedoch nur, wenn die Ausgabe von lsvon der Shell im selben Gebietsschema interpretiert wird, in dem lssie ausgegeben wurde. Außerdem sollten einige Gebietsschemas, z. B. BIG5, BIG5-HKSCS, GBK oder GB18030, vermieden werden.

Mit diesen Muscheln können Sie also tatsächlich Folgendes tun:

typeset -a files
eval "files=($(ls --quoting-style=shell-always))"
xargs -r0a <(printf '%s\0' "${files[@]:0:3}") ...

Aber das hat wenig Vorteil gegenüber:

files=(*(N))                 # zsh
files=(~(N)*)                # ksh93
shopt -s nullglob; files=(*) # bash

Der einzige Fall, in dem es nützlich wird, ist, wenn Sie die -tOption verwenden möchten, lsdie Dateien nach mtime / atime / ctime oder -S/ zu sortieren -V. Aber selbst dann können Sie genauso gut zshFolgendes verwenden:

files=(*(Nom))

Zum Beispiel, um die Dateien nach mtime zu sortieren (verwenden Sie oLfür -Sund nfür -V).

So entfernen Sie alle bis auf die beiden zuletzt geänderten regulären Dateien:

rm -f -- *(D.om[3,-1])

¹ Es gibt immer noch einige Längenbeschränkungen (durch execve()und in einigen Nicht-GNU- xargsImplementierungen viel niedrigere willkürliche), und einige Nicht-GNU- xargsImplementierungen drosseln die Eingabe, die Sequenzen von Bytes enthält, die keine gültigen Zeichen bilden.

Stéphane Chazelas
quelle