La citazione dei nomi dei file è abbastanza sicura per eseguire `xargs sudo rm -rf`?


10

Ho scritto uno script che elimina tutto tranne gli ultimi due file in una cartella:

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

Questo script verrà eseguito come cron job ogni giorno.

Il ragionamento è il seguente:

  1. Ottieni un elenco di tutti i file utilizzando ls -1(in modo da ottenere un file per riga);

  2. Rimuovere gli ultimi due dall'elenco usando head -n -2;

  3. Poiché lsstampa percorsi relativi, usa l' xargs printfoggetto per anteporre il percorso della cartella e renderlo un percorso assoluto;

  4. Mandali a sudo rm -rfusare xargs.

Tutti hanno accesso a questa cartella, quindi chiunque può creare ed eliminare qualsiasi file in questa cartella.

Il problema è: sudo rm -rf fa paura. xargs sudo rm -rfè incredibilmente spaventoso.

Voglio essere sicuro che nessuno può danneggiare altre cartelle / sistemi creando file intelligenti da eliminare (o accidentalmente o di proposito). Non lo so, qualcosa di intelligente come:

file with / spaces.txt

che potrebbe risultare in un super spaventoso sudo rm -rf /.

EDIT: Il mio errore, i nomi dei file non possono contenere /, quindi questo specifico problema non si verifica, ma la domanda se ci sono altri rischi rimane valida.

Questo è il motivo per cui sto usando --quoting-style=shell-always, questo dovrebbe impedire qualsiasi trucco con file con spazi. Ma ora mi chiedo se qualcuno potrebbe essere più intelligente con spazi e citazioni nel nome del file, forse.

La mia sceneggiatura è sicura?


Nota: ho bisogno sudoperché accedo alla cartella da remoto (da un'unità di rete mappata utilizzando mount) e non sono riuscito a farlo funzionare senza sudo.


3
Hai mai pensato di fare qualcosa del genere printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm?
steeldriver

Posso /creare un file con il personaggio nel nome Sto provando a raggiungere questo risultato qui
George Udosen

3
@George No, un nome file non può contenere una barra.
wjandrea,

Quindi, quando OP dice una persona intelligente, mi chiedevo ...
George Udosen,

6
Semplicemente perché stai analizzando l' lsoutput questo è già un comando scritto male, anche con la citazione. lsusa anche le impostazioni locali per l'ordinamento, penso, quindi non vedo lo scopo di headrimuovere gli ultimi 2 (a meno che tu non stia cercando di sbarazzarti di .e ..che iirc non sia consentito come argomenti per rmcomunque. Basta usare find /path/to/folder -type f delete. E no sudose corri da cron - cron è già al livello principale
Sergiy Kolodyazhnyy

Risposte:


10

In Linux, qualsiasi carattere è un nome file valido che costituisce carattere eccetto:

  • \0 (ASCII NUL): utilizzato per la terminazione di stringa in C
  • / (barra): utilizzato per la separazione del percorso

Quindi, il tuo approccio sicuramente non funzionerà in molti casi come puoi immaginare, ad esempio gestisce una newline ( \n) nel nome del file? ( Suggerimento: No ).

Alcune note:

  • Non analizzare ls; utilizzare strumenti dedicati (ce n'è almeno uno per la maggior parte dei casi d'uso)
  • Quando si ha a che fare con nomi di file, provare a sfruttare l'output separato NUL fornito da quasi tutti gli strumenti GNU che funzionano con tali dati
  • Prestare attenzione durante il piping, assicurarsi che entrambi i programmi siano in grado di comprendere le separazioni NUL
  • Ogni volta che invochi xargs, vedi se riesci a cavartela find ... -exec; nella maggior parte dei casi, starai bene solo findda solo

Penso che questi ti faranno andare per ora. steeldriver ha già fornito l'idea separata NUL nel commento ( printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm), usandola come punto di partenza.


Grazie per la risposta :) Penso che dovresti citare il commento di Steeldriver invece di menzionarlo (poiché i commenti non sono permanenti). Darò un'occhiata findanche a, grazie per il suggerimento.
Pedro A

Ho una domanda però: non capisco cosa intendi con "il tuo approccio non funzionerà sicuramente in molti casi come puoi immaginare" - "non funziona" come in "non sicuro" o "non pericoloso"? Perché "il tuo approccio" si riferisce a me e non all'utente malizioso, e le tue precedenti dichiarazioni sono a mio favore, quindi sono confuso.
Pedro A

@PedroA Sei tu :) Come ho detto, poiché tutti i personaggi sono validi tranne i due citati, dovresti essere in grado di immaginare i numerosi casi in cui il tuo lsapproccio di analisi fallirebbe, ad esempio hai preso in considerazione una nuova riga nel nome del file?
heemayl

Oh, una nuova riga nel nome del file ... Non ci avevo pensato. Se non ti dispiace aggiungere anche quello alla tua risposta :) Inoltre, mi dispiace chiedere, ma cosa fa head -z? Sembra ridicolo ma non ho maninfonel mio contenitore CoreOS linux ... Neanche in internet. Ricevohead: invalid option 'z'
Pedro A

@PedroA Newline è solo un caso, ce ne sono molti come potresti aver intuito . Hai bisogno di GNU head(viene fornito con GNU coreutils). Ecco la versione online: manpages.ubuntu.com/manpages/xenial/man1/head.1.html
heemayl

3

xargs supporta alcune quotazioni: con virgolette singole, doppie virgolette o barra rovesciata che gli consente di accettare argomenti arbitrari¹, ma con una sintassi diversa dalla sintassi di quotazione delle shell tipo Bourne.

L'implementazione GNU di lscome si trova su Ubuntu non ha alcuna modalità di quotazione compatibile con il xargsformato di input.

La sua ls --quoting-style=shell-alwaysè compatibile con ksh93, bash e zsh citando sintassi, ma solo quando l'uscita lsviene interpretato dalla shell nella stessa localizzazione come lsera quando output. Inoltre, alcuni locali, come quelli che usano BIG5, BIG5-HKSCS, GBK o GB18030 dovrebbero essere evitati.

Quindi con quelle shell, puoi effettivamente fare:

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

Ma questo ha poco vantaggio rispetto a:

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

L'unico caso in cui diventa utile è quando si desidera utilizzare l' -topzione di lsordinare i file per mtime / atime / ctime o -S/ -V. Ma anche allora, potresti anche usare zsh:

files=(*(Nom))

per esempio per ordinare i file per mtime (usare oLper -S, e nper -V).

Per rimuovere tutti tranne i due file regolari modificati più di recente:

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

¹ ci sono ancora alcune limitazioni di lunghezza (da parte di execve()e in alcune xargsimplementazioni non GNU molto inferiori a quelle arbitrarie), e alcune xargsimplementazioni non GNU si strozzeranno con input che contengono sequenze di byte che non formano caratteri validi.

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.