Gestione dei file e cartelle e funzioni per il SO Python

Da Hacknowledge.

Vedremo in questa sezione come si lavora con i file e le cartelle e come riconoscere il sistema operativo. Riconoscere il sistema operativo in uso è necessario se si vuole scrivere software multipiattaforma, ma allo stesso tempo non si vuole avere problemi a dover gestire determinati "comportamenti" e caratteristiche di un determinato sistema operativo (da ora abbreviato in SO): nei sistemi Unix infatti esiste un sistema di permessi e ad esempio non possiamo scrivere, leggere, modificare determinati file e cartelle se non ne abbiamo i permessi. Riconoscere quindi che il SO corrente è Unix servirà quindi per far in modo di integrare nel codice proprio dei controlli per verificare se abbiamo il permesso di compiere determinate azioni come appunto, modificare file e cartelle e comportarci di conseguenza.

Indice

[modifica] Riconoscimento del Sistema Operativo

Cominciamo quindi proprio a vedere come riconoscere il sistema operativo in uso.
Per iniziare a prendere confidenza, scriviamo un programma che stampi a schermo il nostro sistema operativo.

import os
print os.name

Notiamo che name non è una funzione, ma un semplice attributo. Il valore che ci verrà restituito sarà uno tra 'posix', 'nt', 'mac', 'os2', 'ce', 'java', 'riscos'. Quelli che ci interessano maggiormente sono i primi due, ovvero linux e windows.
Complichiamo leggermente il programma avvicinandoci al modo di usarlo che ci servirà in seguito

import os
if (os.name == "nt"):
    print "Windows"
elif (os.name == "posix"):
    print "Linux"

Questo codice non va altro che stampare a schermo Windows o Linux in base al SO. Ovviamente noi non useremo il riconoscimento del SO per stamparlo a schermo, bensì per effettuare o meno i controlli sui permessi: su Windows ad esempio questi controlli non sono necessari in quanto un tale sistema di permessi non esiste.
Nota: su Windows è da verificare solamente in caso di scrittura di un file, se questo presenta l'attributo Sola lettura o meno.
Scritto in pseudo codice una simile porzione di codice potrebbe essere

import os
if (os.name == "posix"):
    controllo se ho i permessi di scrittura nella cartelle di destinazione
    if (permesso == "si"):
        scrivo il file nella cartella
    else:
        stampo a schermo un errore
        print "non hai i permessi per scrivere"
elif (os.name == "nt"):
    scrivo il file nella cartella


[modifica] Gestione dei permessi

Nota: i seguenti esempi, dove non diversamente specificato, sono per Linux, per i motivi sopra citati riguardanti la gestione dei permessi e pensati eseguiti da un utente normale (di nome user).
I permessi che ci interessano sono:

  • lettura
  • scrittura
  • eseguibilità

Per fare questo esiste una funzione, sempre nel modulo os, che prende come argomenti il percorso da testare e quale permesso vogliamo controllare. Questa funzione è access(), la cui sintassi è: access(path,mode)
mode possono essere:

  • F_OK - esistenza di path
  • W_OK - scrittura
  • R_OK - lettura
  • X_OK - eseguibilità


Esempio:

import os
print os.access("/",os.W_OK)

Non avendo l'utente normale permessi di scrittura in /, la funzione tornerà False. Se fosse stato eseguito da root avrebbe invece ritornato True. La funzione ritorna infatti True se l'utente ha i permessi necessari per compiere l'azione passata in mode, False altrimenti.
I parametri passati come mode possono anche essere concatenati. Ad esempio se avessimo bisogno di sapere se abbiano pieni poteri (W, R, X) in una determinata cartella possiamo eseguire 3 volte la funzione access() ogni volta con un parametro diverso e poi verificare che tutti e tre i valori ritornati siano true o concatenare i parametri mode.
I due codici seguenti sono equivalenti, ma il secondo, concatenando i parametri mode, è più corto.

import os
if (os.access("/home/user",os.W_OK) == True) and (os.access("/home/user",os.R_OK) == True) and (os.access("/home/user",os.X_OK) == True):
    print "Hai tutti i permessi in questa cartella"
else:
    print "Non hai tutti i permessi in questa cartella"
import os
if (os.access("/home/user",os.W_OK and os.R_OK and os.X_OK) == True):
    print "Hai tutti i permessi in questa cartella"
else:
    print "Non hai tutti i permessi in questa cartella"

Vediamo ora la principale funzione per cambiare i permessi, ovvero chmod().
Sempre definita nel modulo os, la sua sintassi è chmod(path,mode) In genere i parametri mode vengono passati sotto forma di numeri, come nell'FTP, altrimenti si possono usare parametri testuali definiti nel modulo stat (che è quindi da includere).
Da notare che su windows questa funzione permette di aggiungere o togliere l'attributo sola lettura a un file. Vediamo un codice che su windows da l'attributo sola lettura a un file se non ce l'ha o viceversa

import os
import stat
if (os.access("C:\\test.txt",os.W_OK) == False):
    os.chmod("C:\\test.txt",stat.S_IWRITE)
else:
    os.chmod("C:\\test.txt",stat.S_IREAD)

Visto quanto detto parlando dell'importazione dei modulo, questo codice potrebbe ad esempio essere scritto anche così:

from os import access, chmod, W_OK
from stat import S_IWRITE, S_IREAD
if (access("C:\\test.txt",W_OK) == False):
    chmod("C:\\test.txt",S_IWRITE)
else:
    chmod("C:\\test.txt",S_IREAD)

[modifica] Gestione delle cartelle

Vediamo ora come si gesticono le cartelle.
La funzione per creare una cartella si chiama mkdir() e ha come sintassi

  mkdir(path,[mode]))

dove path è il percorso della cartella da creare, mode sono i permessi da assegnarle (di default 777).
Nel caso però si debba creare una cartella che richiede però la creazione di cartelle intermedie (ad esempio, devo creare la cartella /home/user/dir/cartella_da_creare, ma anche la cartella dir non esiste e quindi devo crearla) mkdir() ritornerà errore perchè non crea queste cartelle intermedie. Una funzione per creare cartelle comprese quelle intermedie è makedirs(), che ha la stessa sintassi di mkdir(). Vediamo un breve esempio per capire meglio.

from os import mkdir
mkdir("/home/user/cartella/dest")

Se la cartella "cartella" non esiste ci verrà ritornato "OSError: [Errno2] No such file or directory '/home/user/cartella/dest'", in quanto non crea le cartelle intermedie.

from os import makedirs
makedirs("/home/user/cartella/dest")

In questo caso non ci verrà restituito invece nessun errore e saranno create le cartelle "cartella" e "dest".
Ci saranno ritornati errori solo le cartella di destinazione esiste o non abbiamo i permessi per crearla.
Per cancellare una cartella si usa invece rmdir(), che accetta come parametro il percorso della cartella da eliminare.
Per eliminare ricorsivamente le cartelle vuote in un percorso vuoto si usa removedirs(), che accetta il percorso della cartella come argomento.
Per rinominare una cartella (e anche un file in quanto la funzione è la stessa) si usa rename(), che accetta come parametro il nome originario e quello finale

   rename(src,dst)

Questa funzione può creare problemi quando dst esiste già, generando un errore o sovrascrivendo il file.
Per elencare tutte le cartelle e i file in una cartella si usa invece listdir(), che elenca i file e cartelle contenuti nel percorso (da passare come argomento).
A questa funzione si può abbinare l'uso di isdir(), che verifica appunto che il percorso passato come argomento sia una cartella. Per i file si usa invece isfile(), ma la vedremo in seguito.
Nota: queste due funzioni sono contenute nel modulo os.path che vedremo in seguito
Vediamo ora un piccolo script che combina queste due funzioni (listdir() e isdir()) che elenca solo le cartelle contenute in un percorso.

import os
 
path = "/home/user"
dirs_tmp = os.listdir(path)
dirs = []
for i in dirs_tmp:
    i = str(path)+str(i)
    if (os.path.isdir(i)):
        dirs.append(i)
 
for i in dirs:
    print i

Da notare che abbiamo dovuto unire path con gli elementi ritornati da listdir() per averne il percorso completo.

[modifica] Gestione dei file

Vediamo ora come si gestiscono i file, il che implica creazione e scrittura, lettura, rinominazione e cancellazione.
Un file viene aperto tramite la funzione open() che prende come argomenti il percorso del file e il modo per aprirlo. I modi per aprire un file sono in genere tre:

  • w, che crea un file o ne sovrascrive uno con lo stesso nome
  • a, che serve per appendere contenuto a un file

  • r, per leggere un file

Entrambi i primi due modi permettono di creare un file.
La funzione restituisce un oggetto e la sua sintassi è quindi:

   file = open(file,modo)

Su questo oggetto si useranno poi i vari metodi, i cui principali sono read(), readlines(), write() e close().
Vediamo ora un breve script che include queste funzioni per vedere come funzionano.

f=open("/home/user/test.txt","w")
f.write("Prima riga del file di prova\n")
f.write("Seconda riga del file di prova\nTerza riga")
f.close()
 
f=open("/home/user/test.txt","a")
f.write("\nRiga aggiunta con append")
f.close()
 
f=open("/home/user/test.txt","r")
contenuto_file=f.read()
print contenuto_file
f.close()
 
f=open("/home/user/test.txt","r")
linee= f.readlines()
print "\n"
print linee
f.close()
 
f=open("/home/user/test.txt","r")
linee= []
for linea in f:
    linee.append(linea)
print "\n"
print linee
f.close()
 
f=open("/home/user/test.txt","w")
f.close()
 
f=open("/home/user/test.txt","r")
contenuto_file2=f.read()
print contenuto_file2
f.close()

Come possiamo vedere, creiamo prima il file con il modo "w", poi scriviamo usando il metodo write(). Nel secondo blocco apriamo il file con append ("a") e aggiungiamo un'altra riga. Come vediamo il file non viene cancellato nè sovrascritto. Vengono aggiunte le nuove righe in fondo al file.
Apriamo poi il file con "r" (read) e lo leggiamo in tre modi diversi: read(), readlines() e un normale for. Il secondo e il terzo metodo sono essenzialmente equivalenti come si può notare dai vari print, mentre il primo metodo ritorna una stringa invece di una lista (come fanno invece gli altri due metodi). Dopo ripriamo il file con "w" e lo richiudiamo senza fare altro. Poi lo apriamo con "r", andiamo a leggerne il contenuto e vediamo che il file è vuoto: "w" infatti, a differenza di "a" azzera il file.
Riassumendo, la sintassi delle varie funzioni sono:

   f = open(file,modo)
   f.write(stringa_da_scrivere) N.B. si possono scrivere solo stringhe, quindi qualsiasi altra cosa andrà prima convertita con str()
   contenuto = f.read() - ritorna una stringa
   contenuto = f.readlines() ritorna una lista
   f.close() N.B. E' buona regola chiudere sempre un file quando non è più in uso

[modifica] Il modulo os.path

Questo modulo contiene funzioni utili sempre riferite al sistema operativo e le principali sono:

  • split()
  • dirname()
  • basename()
  • join
  • getsize()

Split() divide un percorso restituendo due elementi, la cartella finale e tutto ciò che la precede

import os.path
print os.path.split("/home/user/python")

ritornerà "/home/user/" e "python".
Uguale funzione hanno dirname() e basename(), solo che basename() ritorna solo il primo elemento ritornato da split(), dirname() il secondo.
Join() al contrario unisce due percorsi.
join(path1,[path2,..])
Getsize() invece ritorna la dimensione in byte di un file.

[modifica] Il modulo shutil

Questo modulo consente praticamente di copiare dei file. Le funzioni principali sono

  • copyfile()
  • copy()

La prima funzione copia il contenuto di un file dentro un altro file, la seconda copia un file in un'altra cartella o file.
La sintassi è la stessa per entrambi

   copy(src,dst)

dove src è il file di origine, dst la cartella o file di destinazione.
N.B. Se dst esiste già, verrà sovrascritto!

Strumenti personali