PyPills – Lezione 2 – Stream, parsing and many more!

Buongiorno a tutti. Oggi vi voglio parlare di come è fatto un processo. Potete immaginare un processo come una scatola nera o meglio ancora, per chi ha fatto un minimo di elettronica, come un tripolo (sennò cercate transistor su wikipedia).

Un processo nasce, cresce e muore con 3 stream “attaccati” ad esso:

Stdin, anche noto come Standard Input, è lo stream da cui il processo legge gli input provenienti dall’ esterno, tipicamente dall’ utente che l’ha lanciato o da un altro programma.

Stdout, anche noto come Standard Output, è lo stream di dati che un processo produce alla fine del suo job, volgarmente detto risultato.

Stderr, anche noto come Standard Error è lo stream in cui il programma “vomita” eventuali codici d’errore a seguito di un comportamento inaspettato.

Perchè vi dico tutto questo? Il python nasce come glue, come colla, è infatti molto usato come collante tra vari programmi. Fortunatamente per noi esiste una tecnica nota come pipelining che permette di destinare lo stdout di un processo A come stdin di un altro processo, creano cosi vere e proprie pipeline (catene di montaggio). Chi ha familiarità con il mondo Unix non si stupirà delle mie parole. Un esempio?

cat /etc/pippo.txt | wc -l

Cosa abbiamo fatto? Abbiamo letto un file con cat e abbiamo mandato lo stdout di cat in input a wc, che ci ha calcolato il numero di linee del nostro file di testo. Ma ora veniamo al python.

Su Python-it.org si era intavolata una conversazione su come fosse possibile recuperare le informazioni da un file, ad esempio come da un file MP3 fosse possibile recuperare le ID3. L’utente Flame_Alchemist, che ringrazio, ha fatto osservare che tali info, qualora presenti, risiedono nei primi 128 byte del file Mp3.

Ecco allora l’idea: sfruttiamo un tool Unix chiamato hexdump (calma Linuxofili, funziona anche da voi 😀 ) che permette di fare il dump esadecimale di qualsiasi file e poi, col nostro amato Python, ci andiamo a recuperare le informazioni che vogliamo. E’ rozzo e probabilmente non lo userete mai, l’ho scritto solo per farvi vedere due cose:

-Come sia possibile gestire il pipeline in un programma python

-Uno studio di caso di come si realizza un banale parser a partire da un dump esadecimale.

import sys

import os

class Id3Parser:

    def __init__(self):

        self.input = str.split(str.rstrip(sys.stdin.read()),"\n")

    def get_char_list(self):

        '''Ritorna il dump cosi come e' stato preso in stdin, ogni carattere

        rappresenta una cifra esadecimale'''

        return self.input

    def get_hex_list(self):

        '''Permette di ottenere una lista con il valore decimale di tutti

        i caratteri esadecimale presi da stdin'''

        hex_list = [self.hexstr2ascii(el) for el in self.input]

        return hex_list

    def get_ascii_list(self):

        '''Questa funzione preso un valore decimale ne trova il corrispettivo

        valore ascii, comprimento la lista in modo che i caratteri non ascii

        (minori di 32 in decimale) facciano da spaziatore tra un carattere

        valido e l'altro'''

        lst =[]

        prev=0

        for el in self.get_hex_list():

            if el < 32 and prev < 32:

                continue

            elif el < 32 and prev > 32:

                lst.append("00")

            else:

                lst.append(chr(el))

            prev = el

        return lst

    def hexstr2ascii(self, value):

        '''Questa funzione, preso un qualsiasi valore ascii rappresentante un

        esadecimale (es. '4B') ne trova il corrispettivo valore decimale.'''

        symbol_dict={'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,

                     '6':6,'7':7,'8':8,'9':9,

                     'A':10,'B':11,'C':12,'D':13,'E':14,'F':15}

        fst_hex = symbol_dict[value[0]]

        snd_hex = symbol_dict[value[1]]

        return (fst_hex*16 + snd_hex)

    def get_info_list(self):

        '''Questa funzione ritorna una lista delle informazioni sensibili.'''

        lst=[]

        info=""

        for el in self.get_ascii_list():

            if (el == "00"):

                lst.append(info)

                info=""

                continue

            else:

                info+=el

        return lst

if __name__ == "__main__":

    from id3parser import Id3Parser

    parser = Id3Parser()

    print parser.get_info_list()

COME SI USA:

Da shell:

$ hexdump -n 128 -v -e ‘1/1 “%02X” “\n”‘ <nome_file> | python id3parser.py

dove al posto di nome_file dovete mettere il titolo della canzone.

NOTA: Poichè il blog converte gli apici e i doppi apici in backquote, vi invito a riscrivervi tali apici se copiate ed incollate il codice nella shell, altrimenti non funziona (ringrazio Eugenio per la segnalazione).

Ci sono altri n-mila modi di farlo, mi interessava farlo così per farvi vedere come python sia un valido collante tra processi applicativi.

Vi allego come sempre il codice, alla prossima!

Alfredo Di Napoli

Codice: id3parser.py

PyPills : Lezione 1 – os.system()

Buongiorno a tutti 🙂

In questo articolo inauguro un altra sezione, chiamata PyPills, cioè “Python in pillole”. Il mio obiettivo non è tanto quello di insegnare le cose base, in quanto la rete abbonda di materiale elettronico per imparare il Python, che già di per se è molto semplice. Quello che mi propongo di fare è fare luce sulle parti un pò nebulose. Una delle cose che ho più odiato nel Java era l’impossiblità di eseguire istruzioni di basso livello con un comando. Il C lo permette ma è un pò complicato lavorare sulle stringhe in quanto, come vi ricordo, per C una stringa è un array di caratteri, e come tale bisogna lavorare coi puntatori e non con gli operatori di concatenazione come il + (in C non esite per concatenare stringhe). Il python può. Questa è una frase chiave. Volete realizzare un missile per andare su marte? Python sicuramente potrà, ma potete realizzarci sicuramente un dispositivo usb per il lancio di missili (l’hanno fatto veramente). Come direbbe lo spot della Playstation 3 “Possibilità infinite” 😀 .

Tornando a noi, un comando veramente magico in Python è:

os.system(string command)

(notate che il tipo string l’ho inventato in quanto, vi ricordo, in python i tipi non vanno dichiarati esplicitamente). Cosa fa questo comando? La semplicità è potenza e viceversa, infatti questo comando esegue un comando di sistema. Quale comando? Un qualsiasi comando Unix o Dos (ma noi siamo in Unix, tant’è parleremo di questo 😛 ). Alcuni tra i più smaliziati diranno “vabbè ma non fa nulla che non si possa fare facendo uno script in bash” e ciò è sacrosanto, ma possiamo ad esempio creare il nostro bello scriptino e lanciarlo embedded all’ interno del nostro programma Python così:

import os

os.system(“./myscript.sh”)

Potente no?  Ve l’avevo detto io 😛 

Nella prossima lezione metteremo un pò da parte Python e vedremo un pò di comandi Unix molto utili, parleremo dei filtri e del pipelining.

Alla prossima!

Alfredo