Come gestire enormi quantità di file nella shell?


9

$ ls ./dir_with_huge_amount_of_files/errors/

Supponiamo che una directory sia piena di immagini con timestamp unix, intendo molto misurato in molti GB o anche di più. I comandi shell come lsriceveranno avvisi in stile overflow perché non sono progettati per funzionare con milioni (o più) di immagini. Come posso gestire una così grande quantità di file? Se, ad esempio, voglio trovare l'immagine nel mezzo (in base al timestamp nel nome e nel tempo di creazione), esiste un file system che offre una funzione di ricerca integrata? Quali comandi useresti? Ho provato il comodo lsefind con i flag necessari ma sono stati molto lenti o hanno generato avvisi, quindi sto pensando che ho bisogno di un file system migliore o di un db o qualcosa del genere per preindicizzare le immagini. Fondamentalmente ho bisogno di un array in cui gli inode delle foto dovrebbero essere posizionati in ordine cronologico. Come farlo? Successivamente, potrebbero essere aggiunti metadati con unix-timestamps.

[Aggiornare]

C'è un grave difetto nelle risposte attuali, le persone inviano solo una sorta di risposte senza test empirici. Se avessero testato i loro suggerimenti, probabilmente avrebbero fallito. Quindi, ti ho creato uno strumento da riga di comando con il quale puoi creare il sandbox per creare l'enorme quantità di file e testare i tuoi suggerimenti come con 1e7 quantità di file. La generazione dei file può richiedere molto tempo, quindi sii paziente. Se qualcuno conosce un modo più rapido per farlo, modifica il codice. Digita python code.py --helpper ottenere aiuto. Divertiti!

Esempio di utilizzo per creare molti file diretti

$ ls ./data2
ls: ./data2: No such file or directory
$ python testFill.py -n 3 -d 7                                                 
$ tree data2/                                                                  
data2/
|-- 0
|   |-- 1302407302636973
|   |-- 1302407302638022
|   `-- 1302407302638829
|-- 1
|   |-- 1302407302639604
|   |-- 1302407302641652
|   `-- 1302407302642399
|-- 2
|   |-- 1302407302643158
|   |-- 1302407302645223
|   `-- 1302407302646026
|-- 3
|   |-- 1302407302646837
|   |-- 1302407302649110
|   `-- 1302407302649944
|-- 4
|   |-- 1302407302650771
|   |-- 1302407302652921
|   `-- 1302407302653685
|-- 5
|   |-- 1302407302654423
|   |-- 1302407302656352
|   `-- 1302407302656992
`-- 6
    |-- 1302407302657652
    |-- 1302407302659543
    `-- 1302407302660156

7 directories, 21 files

Codice testFill.py

# Author: hhh
# License: ISC license

import os, math, time, optparse, sys

def createHugeAmountOfFiles(fileAmount, dirAmount):
   counter = 0
   DENSITY = 1e7
   dir = "./data/"

   do = dir+str(counter)+"/"
   while (os.path.exists(do)):
      counter = counter+1
      do = dir+str(counter)+"/"

   os.mkdir(do)

   for d in range(int(dirAmount)):
      for f in range(int(fileAmount)):
         timeIt = int(time.time()*1e6)
         if (not os.path.exists(do)):
            os.mkdir(do)

         if (timeIt % DENSITY == 0):
            counter = counter+1
            do = dir+str(counter)+"/"

            if (not os.path.exists(do)):
               os.mkdir(do)


         do = dir+str(counter)+"/"
         if(not os.path.exists(do)):
            os.mkdir(do)

         f = open(do+str(timeIt), 'w')
         f.write("Automatically created file to test Huge amount of files.")
         f.close()
      counter = counter +1


def ls(dir):
   for root, dirs, files in os.walk("./data/"+dir):
      print(files)

def rm(dir):
   for root, dirs, files in os.walk("./data/"+dir):
      for f in files:
         os.remove("./data/"+dir+"/"+f)


def parseCli():
   parser = optparse.OptionParser()
   parser.add_option("-f", "--file", dest="filename",
                     help="Location to remove files only in ./Data.", metavar="FILE")
   parser.add_option("-n", "--number", dest="number",
                     help="Number of files to generate", metavar="NUMBER")
   parser.add_option("-r", "--remove", dest="remove",
                     help="Data -dir content to remove", metavar="NUMBER")
   parser.add_option("-d", "--dir", dest="dir",
                     help="Amount of dirs to generate", metavar="NUMBER")
   parser.add_option("-q", "--quiet",
                     action="store_false", dest="verbose", default=True,
                     help="don't print status messages to stdout")

   return parser.parse_args()

def main():
   (options, args) = parseCli()

   if (options.filename):
      ls(options.filename)
   if (options.number and options.dir):
      createHugeAmountOfFiles(options.number, options.dir)
   if (options.remove):
      rm(options.remove)


main()

2
@hhh per i set di dati su questa scala un db correttamente indicizzato è probabilmente l'unica opzione
xenoterracide

@xenoterracide: ma anche dbs deve implementare una ricerca veloce con qualcosa come array, db suona eccessivamente. La fonte per scattare foto è qui: github.com/fsphil/fswebcam . Forse, potrei modificarlo un po 'nel momento in cui salva l'immagine in modo da poter aggiungere una riga con il numero di inode e il timestamp unix al file. Ora non con le immagini ma con la linea, sarebbe molto più veloce cercare le immagini. O ancora più facilmente, ogni volta che un'immagine viene salvata su un disco, aggiungo una riga a un file del suo timestamp. Soluzione circolare. Ma non risolverà il problema con le immagini attuali, quindi domanda rilevante.

@hhh quale filesystem stai usando? o non ha ancora importanza ... ext ha alcune funzionalità di miglioramento delle prestazioni che potrebbero non essere attive per impostazione predefinita. Sebbene anche quelli probabilmente non tratteranno sulla scala di cui stai parlando. I DB sono ottimizzati per queste cose e hanno varie soluzioni di indicizzazione per gestirle. per esempio un indice btree non è solo un semplice array ...
xenoterracide

@xenoterracide: ext3, non sono sicuro che sia importante. Penso che la soluzione che ho illustrato risolva il problema per i futuri problemi di ricerca, ma non aiuta affatto con le foto attuali, è molto dispendioso in termini di ricerca.

1
Hai milioni di file in una singola directory? In tal caso, potresti considerare di dividerli per uno o più livelli secondari, in base ai primi caratteri del nome del file, ad esempio:a/b/abcdef.jpg
alex

Risposte:


4

Prova una shell diversa. Consiglierei di provare zsh per esempio e vedere se consente più parametri.

Se ho capito bene, parte del nome file è un timestamp UNIX. Potrebbe essere consigliabile dividere i file in cartelle. Se il formato data / ora è un numero di epoca UNIX, metti pezzi di frazioni di quel numero, diciamo 10000, in una cartella separata.

Se un timestamp ISO 8601 fa parte del nome file, è sufficiente dividere per anno, mese o giorno.


1
ls e find non sono integrati in bash o zsh, quindi non è chiaro come il cambio di shell possa aiutare in questo caso.
Robin Green,

Riguarda l'espansione della shell. Se la shell non può espandere il globbing, questo potrebbe essere il problema.
polemon

Ho fatto alcune prove di esecuzione di comandi su circa 1E6 file, ZSH affronta gli stessi problemi: "$ cp * Test/ ksh: cp: Argument list too long % rm * zsh: sure you want to delete all the files in /home/user/Downloads [yn]? y zsh: argument list too long: rm % ls * zsh: argument list too long: ls ". Siamo spiacenti ma non riesco a vedere come questo sia correlato alla domanda -1 perché è stato così facile testarlo, creare solo file 1e6 ed eseguire i comandi.

1

Sarebbe locate(e ovviamente updatedb) essere di alcun aiuto a voi?


1
updatedbusi find.
dave1010,

@ dave1010, certo, ma lo fa ogni tanto in background, quindi se è accettabile per l'OP non essere necessariamente aggiornato ogni minuto, ma forse una volta al giorno, quindi programmare l'aggiornamentob in un'ora tranquilla (o programma aggiornatob frequentemente ma a bassa priorità, che è comunque quello che dovrebbe essere), quindi usare individuare è molto veloce per trovare quello che vuoi. Quindi la domanda chiave è quanto deve essere aggiornato il DB (o l'indice per qualsiasi altro sistema simile).
asoundmove,
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.