J'ai plusieurs centaines de PDF sous un répertoire sous UNIX. Les noms des fichiers PDF sont très longs (environ 60 caractères).
Lorsque j'essaie de supprimer tous les fichiers PDF ensemble à l'aide de la commande suivante:
rm -f *.pdf
Je reçois l'erreur suivante:
/bin/rm: cannot execute [Argument list too long]
Quelle est la solution à cette erreur? Cette erreur se produit-elle également pour les commandes mv
et cp
? Si oui, comment résoudre ces commandes?
La raison en est que bash étend l'astérisque à tous les fichiers correspondants, générant ainsi une très longue ligne de commande.
Essaye ça:
find . -name "*.pdf" -print0 | xargs -0 rm
Attention: ceci est une recherche récursive et trouvera (et supprimera) des fichiers dans des sous-répertoires également. Ne pointez sur -f
dans la commande rm que si vous êtes sûr de ne pas vouloir de confirmation.
Vous pouvez effectuer les opérations suivantes pour rendre la commande non-récursive:
find . -maxdepth 1 -name "*.pdf" -print0 | xargs -0 rm
Une autre option consiste à utiliser l'indicateur -delete
de find:
find . -name "*.pdf" -delete
C'est une limitation du noyau sur la taille de l'argument de ligne de commande. Utilisez plutôt une boucle for
.
Il s'agit d'un problème système, lié à execve
et à ARG_MAX
constant. Il y a beaucoup de documentation à ce sujet (voir man execve , wiki de debian ).
En gros, l’extension produit une commande (avec ses paramètres) qui dépasse la limite ARG_MAX
. Sur le noyau 2.6.23
, la limite a été définie sur 128 kB
. Cette constante a été augmentée et vous pouvez obtenir sa valeur en exécutant:
getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic
for
Utilisez une boucle for
comme recommandé sur BashFAQ/095 et il n'y a pas de limite, à l'exception de la RAM/de l'espace mémoire:
for f in *.pdf; do rm "$f"; done
C'est aussi une approche portable car glob a un comportement fort et cohérent parmi les shells ( partie de la spécification POSIX ).
Note: Comme l'ont noté plusieurs commentaires, ceci est en effet plus lent mais plus facile à gérer car il peut adapter des scénarios plus complexes, par exemple où l'on veut faire plus qu'une seule action.
find
Si vous insistez, vous pouvez utiliser find
mais vraiment n'utilisez pas xargs car "est dangereux (cassé, exploitable, etc.) lors de la lecture d'entrées non délimitées par NUL":
find . -maxdepth 1 -name '*.pdf' -delete
Utiliser -maxdepth 1 ... -delete
au lieu de -exec rm {} +
permet à find
d'exécuter simplement les appels système requis sans utiliser de processus externe, donc plus rapidement (grâce à @chepner comment ).
find
a une action -delete
:
find . -maxdepth 1 -name '*.pdf' -delete
Une autre solution consiste à forcer xargs
à traiter les commandes par lots. Par exemple, delete
les fichiers 100
à la fois, cd
dans le répertoire et exécutez ceci:
echo *.pdf | xargs -n 100 rm
Ou vous pouvez essayer:
find . -name '*.pdf' -exec rm -f {} \;
vous pouvez essayer ceci:
for f in *.pdf
do
rm $f
done
EDIT: ThiefMaster me suggère de ne pas divulguer une telle pratique dangereuse aux jeunes jedis de Shell, je vais donc ajouter une version plus "sûre" (pour préserver les choses lorsque quelqu'un a un "-rf. .Pdf" fichier)
echo "# Whooooo" > /tmp/dummy.sh
for f in '*.pdf'
do
echo "rm -i $f" >> /tmp/dummy.sh
done
Après avoir exécuté ce qui précède, ouvrez simplement le fichier /tmp/dummy.sh dans votre fav. éditeur et vérifiez chaque ligne pour les noms de fichiers dangereux, en les commentant s’ils le trouvent.
Copiez ensuite le script dummy.sh dans votre répertoire de travail et exécutez-le.
Tout cela pour des raisons de sécurité.
Si vous essayez de supprimer un très grand nombre de fichiers à la fois (j'ai supprimé un répertoire de 485 000+ aujourd'hui), vous rencontrerez probablement cette erreur:
/bin/rm: Argument list too long.
Le problème est que lorsque vous tapez quelque chose comme rm -rf *
, le *
est remplacé par une liste de tous les fichiers correspondants, comme "rm -rf fichier1 fichier2 fichier3 fichier4", etc. Le tampon de mémoire alloué au stockage de cette liste d'arguments est relativement petit et s'il est rempli, le shell n'exécutera pas le programme.
Pour résoudre ce problème, beaucoup de gens vont utiliser la commande find pour trouver tous les fichiers et les passer un par un à la commande “rm” comme ceci:
find . -type f -exec rm -v {} \;
Mon problème est que je devais supprimer 500 000 fichiers et que cela prenait trop de temps.
Je suis tombé sur un moyen beaucoup plus rapide de supprimer des fichiers - la commande "find" a un indicateur "-delete" intégré! Voici ce que j’ai fini par utiliser:
find . -type f -delete
En utilisant cette méthode, je supprimais des fichiers à un taux d’environ 2000 fichiers/seconde - beaucoup plus rapidement!
Vous pouvez également afficher les noms de fichiers au fur et à mesure que vous les supprimez:
find . -type f -print -delete
… Ou même montrer combien de fichiers seront supprimés, puis le temps qu'il faut pour les supprimer:
root@devel# ls -1 | wc -l && time find . -type f -delete
100000
real 0m3.660s
user 0m0.036s
sys 0m0.552s
Vous pouvez utiliser un tableau bash:
files=(*.pdf)
for((I=0;I<${#files[*]};I+=1000)); do rm -f ${files[@]:I:1000}; done
De cette façon, il supprimera des lots de 1 000 fichiers par étape.
vous pouvez utiliser cette recommandation
find -name "*.pdf" -delete
find . -type f -name '*xxx' -print -delete
La commande rm comporte une limite de fichiers que vous pouvez supprimer simultanément.
Une possibilité consiste à les supprimer en utilisant plusieurs fois la commande rm sur la base de vos modèles de fichiers, comme:
rm -f A*.pdf
rm -f B*.pdf
rm -f C*.pdf
...
rm -f *.pdf
Vous pouvez également les supprimer via find command:
find . -name "*.pdf" -exec rm {} \;
S'il s'agit de noms de fichiers avec des espaces ou des caractères spéciaux, utilisez:
find -maxdepth 1 -name '*.pdf' -exec rm "{}" \;
Cette phrase recherche dans tous les fichiers du répertoire en cours (-maxdepth 1) avec l'extension pdf (-name '* .pdf'), puis supprime chacun (-exec rm "{}").
L'expression {} remplace le nom du fichier et "{}" définit le nom du fichier sous forme de chaîne, y compris les espaces ou les caractères spéciaux.
je faisais face au même problème lors de la copie du répertoire source du formulaire vers la destination
le répertoire source avait des fichiers ~ 3 lakcs
j'ai utilisé cp avec l'option -r et cela a fonctionné pour moi
cp -r abc/def/
il copiera tous les fichiers d'abc à def sans donner d'avertissement trop long de la liste d'arguments
Et un autre:
cd /path/to/pdf
printf "%s\0" *.[Pp][Dd][Ff] | xargs -0 rm
printf
est un shell intégré et, autant que je sache, il a toujours été comme tel. Maintenant, étant donné que printf
n'est pas une commande Shell (mais intégrée), elle n'est pas sujette à l'erreur fatale "argument list too long ...
".
Ainsi, nous pouvons l’utiliser en toute sécurité avec les modèles globaux de shell tels que *.[Pp][Dd][Ff]
, puis nous dirigeons sa sortie vers la commande remove (rm
), via xargs
, ce qui permet de s’assurer qu’il contient suffisamment de noms de fichier dans la ligne de commande pour ne pas faire échouer la commande rm
qui est une commande shell.
Le \0
dans printf
sert de séparateur null pour les noms de fichiers qui sont ensuite traités par la commande xargs
, en utilisant (-0
) comme séparateur. Par conséquent, rm
n'échoue pas lorsqu'il existe des espaces blancs ou d'autres caractères spéciaux dans les noms de fichier.
J'ai rencontré ce problème à quelques reprises. La plupart des solutions exécutent la commande rm
pour chaque fichier à supprimer. C'est très inefficace:
find . -name "*.pdf" -print0 | xargs -0 rm -rf
J'ai fini par écrire un script python pour supprimer les fichiers en fonction des 4 premiers caractères du nom de fichier:
import os
filedir = '/tmp/' #The directory you wish to run rm on
filelist = (os.listdir(filedir)) #gets listing of all files in the specified dir
newlist = [] #Makes a blank list named newlist
for i in filelist:
if str((i)[:4]) not in newlist: #This makes sure that the elements are unique for newlist
newlist.append((i)[:4]) #This takes only the first 4 charcters of the folder/filename and appends it to newlist
for i in newlist:
if 'tmp' in i: #If statment to look for tmp in the filename/dirname
print ('Running command rm -rf '+str(filedir)+str(i)+'* : File Count: '+str(len(os.listdir(filedir)))) #Prints the command to be run and a total file count
os.system('rm -rf '+str(filedir)+str(i)+'*') #Actual Shell command
print ('DONE')
Cela a très bien fonctionné pour moi. J'ai pu effacer plus de 2 millions de fichiers temporaires dans un dossier en environ 15 minutes. J'ai commenté le code dans le code afin que quiconque ayant une connaissance minimale ou inexistant de python puisse manipuler ce code.
Vous pouvez créer un dossier temporaire, déplacer tous les fichiers et sous-dossiers que vous souhaitez conserver dans le dossier temporaire, puis supprimer l'ancien dossier et renommer le dossier temporaire en ancien dossier. Essayez cet exemple jusqu'à ce que vous soyez sûr de le faire en direct:
mkdir testit
cd testit
mkdir big_folder tmp_folder
touch big_folder/file1.pdf
touch big_folder/file2.pdf
mv big_folder/file1,pdf tmp_folder/
rm -r big_folder
mv tmp_folder big_folder
le rm -r big_folder
supprimera tous les fichiers du big_folder
, peu importe le nombre. Vous devez simplement faire très attention à ce que vous ayez tous les fichiers/dossiers que vous souhaitez conserver, dans ce cas, c'était file1.pdf
Essayez également ceci Si vous souhaitez supprimer des fichiers/dossiers supérieurs à 30/90 jours (+) ou inférieurs à 30/90 jours (-), vous pouvez utiliser les commandes ex ci-dessous.
Ex: Pour 90days exclut ci-dessus après 90 jours de suppression de fichiers/dossiers, cela signifie 91,92 .... 100 jours
find <path> -type f -mtime +90 -exec rm -rf {} \;
Ex: Pour ne supprimer que les derniers fichiers de 30 jours, utilisez la commande ci-dessous (-)
find <path> -type f -mtime -30 -exec rm -rf {} \;
Si vous voulez conserver les fichiers pendant plus de 2 jours
find <path> -type f -mtime +2 -exec gzip {} \;
Si vous voulez voir les fichiers/dossiers du dernier mois seulement . Ex:
find <path> -type f -mtime -30 -exec ls -lrt {} \;
Au-dessus de 30 jours, il n’ya plus que la liste des fichiers/dossiers Ex:
find <path> -type f -mtime +30 -exec ls -lrt {} \;
find /opt/app/logs -type f -mtime +30 -exec ls -lrt {} \;
J'ai rencontré un problème similaire lorsqu'il y avait des millions de fichiers journaux inutiles créés par une application qui remplissait tous les inodes. J'ai eu recours à "localiser", j'ai récupéré tous les fichiers "situés" dans un fichier texte, puis je les ai supprimés un par un. Cela a pris du temps, mais j'ai fait le travail!
Je connais seulement un moyen de contourner cela ... L'idée est d'exporter cette liste de fichiers pdf dans un fichier. Puis divisez ce fichier en plusieurs parties. Supprimez ensuite les fichiers pdf répertoriés dans chaque partie.
ls | grep .pdf > list.txt
wc -l list.txt
wc -l doit compter combien de lignes le fichier list.txt contient. Lorsque vous avez l’idée de sa durée, vous pouvez décider de la scinder en deux parties, ou autre. Utilisation de la commande split -l .__ Par exemple, divisez-la en 600 lignes chacune.
split -l 600 list.txt
cela créera quelques fichiers nommés xaa, xab, xac, etc., selon la façon dont vous l'avez scindé . Maintenant, pour "importer" chaque liste de ces fichiers dans la commande rm, utilisez ceci:
rm $(<xaa)
rm $(<xab)
rm $(<xac)
Désolé pour mon mauvais anglais.
J'ai trouvé que pour les listes de fichiers extrêmement volumineuses (> 1e6), ces réponses étaient trop lentes. Voici une solution utilisant le traitement parallèle en python. Je sais, je sais, ce n'est pas Linux ... mais rien d'autre ne fonctionne ici.
(Cela m'a sauvé des heures)
# delete files
import os as os
import glob
import multiprocessing as mp
directory = r'your/directory'
os.chdir(directory)
files_names = [i for i in glob.glob('*.{}'.format('pdf'))]
# report errors from pool
def callback_error(result):
print('error', result)
# delete file using system command
def delete_files(file_name):
os.system('rm -rf ' + file_name)
pool = mp.Pool(12)
# or use pool = mp.Pool(mp.cpu_count())
if __== '__main__':
for file_name in files_names:
print(file_name)
pool.apply_async(delete_files,[file_name], error_callback=callback_error)
Pour supprimer tout *.pdf
dans un répertoire /path/to/dir_with_pdf_files/
mkdir empty_dir # Create temp empty dir
rsync -avh --delete --include '*.pdf' empty_dir/ /path/to/dir_with_pdf_files/
Supprimer des fichiers spécifiques via rsync
en utilisant un caractère générique est probablement la solution la plus rapide au cas où vous auriez des millions de fichiers. Et cela évitera les erreurs que vous obtenez.
(Étape facultative): DRY RUN. Pour vérifier ce qui sera supprimé sans supprimer. `
rsync -avhn --delete --include '*.pdf' empty_dir/ /path/to/dir_with_pdf_files/
...
Cliquez trucs et astuces rsync pour plus de piratages rsync
Si vous devez conserver un serveur ou un système responsive lors de la suppression d'un grand nombre de fichiers, sleep
entre chaque instruction de suppression peut constituer une bonne approche.
find . -name "*.pdf" -print0 | while read -d $'\0' file
do
rm "$file"
sleep 0.005 # Sleeps for 5ms, Tweak as needed
done