J'ai écrit un script qui supprime tous sauf les deux derniers fichiers d'un dossier:
#!/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
Ce script sera exécuté quotidiennement comme un travail cron.
Le raisonnement est le suivant:
Obtenez une liste de tous les fichiers en utilisant ls -1
(afin que je reçoive un fichier par ligne);
Supprimez les deux derniers de la liste en utilisant head -n -2
;
Puisque ls
imprime les chemins relatifs, utilisez le xargs printf
pour ajouter le chemin du dossier et en faire un chemin absolu;
Envoyez-les à Sudo rm -rf
en utilisant xargs
.
Tout le monde a accès à ce dossier, ainsi tout le monde peut créer et supprimer tous les fichiers de ce dossier.
Le problème est:Sudo rm -rf
est effrayant. xargs Sudo rm -rf
est incroyablement effrayant.
Je veux être sûr que personne ne peut endommager d'autres dossiers/systèmes en créant des fichiers intelligents à supprimer (accidentellement ou volontairement). Je ne sais pas, quelque chose d'intelligent comme:
file with / spaces.txt
ce qui pourrait entraîner un très effrayant Sudo rm -rf /
.
EDIT: Mon erreur, les noms de fichiers ne peuvent pas contenir /
, ce problème spécifique n’aurait donc pas lieu, mais la question de savoir s’il existe ou non d’autres risques subsiste.
C'est pourquoi j'utilise --quoting-style=Shell-always
, cela devrait empêcher toute astuce avec des fichiers avec des espaces. Mais maintenant je me demande si quelqu'un pourrait être très intelligent avec des espaces et des citations , peut-être dans le nom du fichier.
Est-ce que mon script est en sécurité?
Remarque: J'ai besoin de Sudo
car j'accède au dossier à distance (à partir d'un lecteur réseau mappé à l'aide de mount
) et je ne pouvais pas le faire fonctionner sans Sudo.
Sous Linux, tout caractère est un caractère de nom de fichier valide sauf:
\0
(ASCII NUL): utilisé pour la terminaison de chaîne en C/
(barre oblique): utilisé pour la séparation de cheminAinsi, votre approche ne fonctionnera certainement pas dans de nombreux cas, comme vous pouvez l'imaginer, par exemple. gère-t-il une nouvelle ligne (\n
) dans le nom du fichier? (Indice: Non ).
Quelques notes:
ls
; utiliser des outils dédiés (il y en a au moins un pour la plupart des cas d'utilisation)xargs
, voyez si vous pouvez vous en sortir avec find ... -exec
; dans la plupart des cas, vous irez bien avec find
seulJe pense que ceux-ci vont vous aider pour le moment. steeldriver a déjà fourni l'idée séparée NUL dans le commentaire (printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm
), utilisez-la comme point de départ.
xargs
supporte certains guillemets: avec des guillemets simples, des guillemets doubles ou des barres obliques inverses qui lui permettent d'accepter des arguments arbitraires¹, mais avec une syntaxe différente de celle des guillemets de type Bourne.
L'implémentation GNU de ls
telle qu'elle est trouvée sur Ubuntu n'a aucun mode de citation compatible avec le format d'entrée xargs
name__.
Son ls --quoting-style=Shell-always
est compatible avec la syntaxe citant les shells ksh93, bash et zsh, mais uniquement lorsque la sortie de ls
est interprétée par le shell dans les mêmes paramètres régionaux que le ls
lors de sa sortie. De plus, certaines langues, comme celles qui utilisent BIG5, BIG5-HKSCS, GBK ou GB18030 devraient être évitées.
Donc, avec ces coquilles, vous pouvez réellement faire:
typeset -a files
eval "files=($(ls --quoting-style=Shell-always))"
xargs -r0a <(printf '%s\0' "${files[@]:0:3}") ...
Mais cela a peu d’avantage sur:
files=(*(N)) # zsh
files=(~(N)*) # ksh93
shopt -s nullglob; files=(*) # bash
Cela devient utile uniquement lorsque vous souhaitez utiliser l'option -t
de ls
pour trier les fichiers par mtime/atime/ctime ou -S
/-V
. Mais même dans ce cas, vous pourriez aussi bien utiliser zsh
name __:
files=(*(Nom))
par exemple, pour trier les fichiers par mtime (utilisez oL
pour -S
et n
pour -V
).
Pour supprimer tous les fichiers normaux les plus récemment modifiés, à l'exception des deux derniers:
rm -f -- *(D.om[3,-1])
¹ il y a toujours des limitations de longueur (par execve()
et dans certaines implémentations non-GNU xargs
beaucoup moins élevées), et certaines implémentations non-GNU xargs
vont étouffer les entrées contenant des séquences d'octets ne formant pas des caractères valides.