Come analizzo un elenco di file per ottenere solo i nomi dei file in Python?
Domanda
Quindi diciamo che sto usando ftplib per recuperare un elenco di file di registro da un server FTP. Come analizzerei quell'elenco di file per ottenere solo i nomi dei file (l'ultima colonna) all'interno di un elenco? Vedi il link sopra per esempio output.
Soluzione
L'uso di retrlines () probabilmente non è la migliore idea lì, dal momento che stampa solo sulla console e quindi dovresti fare cose difficili anche per ottenere quell'output. Una scommessa probabilmente migliore sarebbe quella di utilizzare il metodo nlst (), che restituisce esattamente quello che vuoi: un elenco dei nomi dei file.
Altri suggerimenti
Questa migliore risposta
Puoi usare ftp.nlst ()
invece di ftp.retrlines ()
. Ti darà esattamente quello che vuoi.
Se non puoi, leggi quanto segue:
Generatori per processi sysadmin
Nella sua ormai famosa recensione, Trucchi per generatori per programmatori di sistemi Un'introduzione , David M. Beazley fornisce molte ricevute per rispondere a questo tipo di problema relativo ai dati con wuick e codice riutilizzabile.
E.G:
# empty list that will receive all the log entry
log = []
# we pass a callback function bypass the print_line that would be called by retrlines
# we do that only because we cannot use something better than retrlines
ftp.retrlines('LIST', callback=log.append)
# we use rsplit because it more efficient in our case if we have a big file
files = (line.rsplit(None, 1)[1] for line in log)
# get you file list
files_list = list(files)
Perché non generiamo immediatamente l'elenco?
Bene, perché farlo in questo modo ti offre molta flessibilità: puoi applicare qualsiasi generatore intermedio per filtrare i file prima di trasformarlo in files_list
: è proprio come pipe, aggiungi una linea, aggiungi un processo senza surriscaldamento (poiché è generatori). E se ti sbarazzi di retrlines
, funziona ancora perché è ancora meglio perché non memorizzi l'elenco nemmeno una volta.
EDIT: bene, ho letto il commento all'altra risposta e dice che questo non funzionerà se c'è spazio nel nome.
Bene, questo illustrerà perché questo metodo è utile. Se vuoi cambiare qualcosa nel processo, devi solo cambiare una linea. Scambia:
files = (line.rsplit(None, 1)[1] for line in log)
e
# join split the line, get all the item from the field 8 then join them
files = (' '.join(line.split()[8:]) for line in log)
Ok, qui potrebbe non essere ovvio, ma per gli script di processo batch enormi, è bello :-)
E un metodo leggermente meno ottimale, comunque, se sei bloccato usando retrlines () per qualche motivo, è passare una funzione come secondo argomento a retrlines (); verrà chiamato per ogni elemento nell'elenco. Quindi qualcosa del genere (supponendo che tu abbia un oggetto FTP chiamato 'ftp') funzionerebbe anche:
filenames = []
ftp.retrlines('LIST', lambda line: filenames.append(line.split()[-1]))
L'elenco 'nomi file' sarà quindi un elenco dei nomi dei file.
C'è qualche motivo per cui ftplib.FTP.nlst () non funziona per te? Ho appena controllato e restituisce solo i nomi dei file in una determinata directory.
Poiché ogni nome di file nell'output inizia nella stessa colonna, tutto ciò che devi fare è ottenere la posizione del punto sulla prima riga:
drwxrwsr-x 5 ftp-usr pdmaint 1536 20 marzo 09:48.
Quindi tagliare il nome del file dalle altre righe usando la posizione di quel punto come indice iniziale.
Dato che il punto è l'ultimo carattere sulla linea, puoi usare la lunghezza della linea meno 1 come indice. Quindi il codice finale è qualcosa del genere:
lines = ftp.retrlines('LIST')
lines = lines.split("\n") # This should split the string into an array of lines
filename_index = len(lines[0]) - 1
files = []
for line in lines:
files.append(line[filename_index:])
Se il server FTP supporta il comando MLSD
, consultare la sezione "caso di directory singola" da che rispondi.
Utilizza un'istanza (ad esempio ftpd
) della classe FTPDirectory
, chiama il suo metodo .getdata
con ftplib.FTP
directory_filenames= [ftpfile.name for ftpfile in ftpd.files]
Credo che dovrebbe funzionare per te.
file_name_list = [' '.join(each_file.split()).split()[-1] for each_file_detail in file_list_from_log]
NOTE -
-
Qui sto assumendo che tu voglia i dati nel programma (come elenco), non sulla console.
-
each_file_detail è ogni riga prodotta dal programma.
-
'' .join (each_file.split ())
Per sostituire più spazi con 1 spazio.