Articoli con tag Software

Analizzatore spettrografico di file

A seguito di uno scambio di idee con gli amici di CFItaly, mi è venuta l’idea di provare a giocare un po’ con l’analisi statistica del contenuto di un file generico. Non è una idea nuova (rif. Denis Frati), certamente no, ma al solito ogni tanto mi piace di curiosare in cose per me nuove.

Il risultato è un programmino semplicissimo, che ho chiamato Prisma.

L’idea è questa: in ogni tipologia di file la presenza di certi valori di byte non è casuale, ma è in qualche modo legata al contenuto del file stesso. Per cui un file di testo avrà una particolare distribuzione dei valori dei byte, mentre una immagine o un file eseguibile ne avranno una differente, anche in funzione della codifica. Analizzando le frequenze con cui ogni valore di byte compare nel file si potrebbero individuare anomalie nel contenuto del file stesso, come ad esempio un testo nascosto in un file audio, oppure più semplicemente file camuffati: immagini spacciate come file compressi e simili.

Funziona in questo modo: si forniscono in standard input i dati da analizzare, per esempio un file, e in uscita si hanno tre differenti tipi di output a scelta. Il primo, banale, è un file CSV con i dati di frequenza complessivi di tutti i dati forniti in ingresso. E’ utilizzabile per fare un grafico con OpenOffice Calc e simili.

L’altro output è grafico, sotto forma di immagine PNG, in due varianti: la più semplice mostra uno spettrogramma complessivo per tutti i dati in input, ed è semplicemente una differente rappresentazione dei dati forniti anche in CSV, tanto che si possono chiedere entrambi. La seconda opzione è più intrigante: produce una immagine larga 256 pixel in cui ogni riga è lo spettrogramma di un blocco di dati in input, e la dimensione del blocco è configurabile. In pratica si ha un colpo d’occhio sul contenuto del file, reso in forma grafica.

Spettro di un file di testo
Questo è lo spettro di un file di testo (la licenza GNU GPL per la precisione).

Spettro di un file Wave
Questo è quello che si ottiene usando un file Wave.

Spettrogramma di un file Wave
Questo invece è lo spettrogramma dello stesso file (clicca sull’immagine per la dimensione reale).

Il primo file è stato ottenuto col comando:


$ cat COPYING | prisma -p asciitext.png

Lo spettrogramma del file Wave invece è stato ottenuto col comando:


$ cat test.wav | prisma -p wave-big.png -b 100000

Per chi è interessato, il programma è rilasciato sotto licenza GNU GPLv2, ed il tarball dei sorgenti con il makefile per compilare ed installare il tutto, con tanto di pagine man in italiano ed inglese è scaricabile qui. Richiede le librerie e gli header di ImageMagick per la compilazione

Sono ovviamente graditi commenti e suggerimenti per il miglioramento.

Tags: , , ,

Database, campi ad autoincremento ed INSERT

Mi trovo, mio malgrado, a sviluppare una applicazione web in PHP per gestire un impianto. Le ragioni che mi hanno portato a questo sono troppo lunghe da spiegare, per cui andiamo al sodo.

Alle spalle dell’applicazione vi è un database PostgreSQL per mantenere lo stato e la coerenza del flusso di lavorazione (è una applicazione di gestione di processo). Alcune tabelle nel database hanno un campo ad autoincremento, che in PostgreSQL è definito dal tipo serial (esiste anche in MySQL ed è un alias equivalente a: BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE). Questo campo ha la caratteristica interessante che al momento di una query INSERT se viene omesso dai dati da inserire nel nuovo record, viene assegnato automaticamente da PostgreSQL con l’intero successivo all’ultimo usato, partendo da 1. Per intenderci: il primo INSERT lo assegnerà al valore 1, il secondo al 2, il terzo a 3 e via così. Questo garantisce che non vi saranno duplicati, e quindi il campo si può usare come identificativo univoco. Tant’è che nelle tabelle in questione il campo l’ho chiamato ID, con la solita mancanza di fantasia.

Fin qui nulla di strano, ma… Dopo una query di INSERT, a parte controllare se sia andata a buon fine, se mi serve di sapere quale ID è stato assegnato al record inserito come faccio? Sulle prime avevo ingenuamente (e molto inespertamente) pensato di fare una query con gli stessi valori inseriti e vedere che ID mi restituiva. Ma vi potevano essere record con valori uguali tranne l’ID, quindi questa soluzione non va. Poi ho pensato di usare il MAX sull’intera colonna degli ID, ed anche qui vi potevano essere parecchie controindicazioni.

Poi, come sempre accade, invece di smanettare a cervello spento, mi sono fatto la domanda chiave: possibile che non esista una funzione nativa apposita? Ebbene, esiste ed è proprio quello che serve, oltre ad essere assolutamente corretta anche in caso di inserimenti multipli concorrenti sulla stessa tabella. La funzione è currval in PostgreSQL e LAST_INSERT_ID in MySQL. In entrambi i database la funzione è isolata per connessione al database, quindi anche in caso di utilizzo concorrente con query di INSERT parallele, il valore restituito è coerente. Se si avessero dei dubbi, basta consultare la documentazione in merito, e alla peggio ricorrere ad una transazione per isolare l’operazione.

Come si usa è presto detto: appena dopo una query di INSERT, si controlla se è andata a buon fine, poi si esegue questa query (per PostgreSQL):


SELECT currval(tabella_id_seq) as id;

che andiamo a spiegare. Al momento della creazione di una tabella che contiene un campo di tipo serial, PostgreSQL crea implicitamente un oggetto sequence, che altro non è che un numero intero che conserva l’ultimo valore usato nel rispettivo campo di tipo serial. Il nome assegnato all’oggetto sequenza è il nome della tabella, seguito dal nome del campo e dalla stringa “seq”, separati da un segno di underscore. Se ad esempio la tabella si chiama libri ed il campo al suo interno di tipo serial si chiama idlibro, la sequenza implicitamente creata avrà come nome: libri_idlibro_seq. Quindi la query specifica per questa tabella per conoscere l’ultimo valore di idlibro generato da PostgreSQL sarà:


SELECT currval(libri_idlibro_seq) as ultimoid;

Molto più semplice che inventarsi funzioni apposite (dal funzionamento quanto mai incerto), o peggio inventare l’acqua calda, ossia scrivere codice per una funzione che esiste già nativa sul software che andiamo ad utilizzare.

Riferimenti

  • Il manuale di PostgreSQL
  • Il manuale di MySQL
  • Il manuale di PHP, alla voce pg_last_oid(), che indirizza alla funzione giusta, ossia currval

Tags: , , ,

Aggiungere interfacce grafiche agli script shell #3

In questa terza puntata vediamo come Zenity ci può aiutare con i file.

L’opzione è --file-selection, ed accetta l’opzione generale --title per dare il titolo alla finestra che appare:


$ zenity  --file-selection --title="Prendi un file"

Questo il risultato:

Zenity File Selection 1

Al solito il valore di ritorno è true, ossia zero, se si preme il pulsante OK, false, diverso da zero, se si preme il pulsante Annulla o si chiude la finestra. Il file scelto viene stampato sullo standard output con il path completo. Se dalla finestra mostrata in figura scelgo il file avatar.png all’uscita di Zenity con la pressione del tasto OK viene stampato:


$ zenity  --file-selection --title="Prendi un file"
/home/mario/Desktop/avatar.png

Ecco un breve script shell che mostra come usare la selezione file di Zenity:


#!/bin/bash

# mostra la finestra di selezione file e assegna il file scelto
# che zenity manda allo standard output alla variabile "filescelto"
# notare i "backtick" -> ` `
filescelto=`zenity --file-selection --title="Zenity: seleziona un file"`

# prende il valore di ritorno del comando precedente
ritorno=$?

# controlla se si è premuto OK o Annulla
if [ "$ritorno" -ne 0 ]; then
  # è stato premuto annulla o chiusa la finestra
  zenity --warning --title="Test di Zenity" --text="Hai annullato"
  exit 1
else
  # è stato premuto OK
  zenity --info --title="Test di Zenity" --text="Hai scelto il file: '$filescelto'"
fi

Lo script è disponibile qui.

La directory di partenza è quella attuale, ma è possibile cambiarla a piacimento usando l’opzione --filename=FILE, mettendo al posto di FILE il percorso voluto, completo di barra finale se è una directory:


$ zenity  --file-selection --title="Prendi un file" --filename=/usr/share/doc/

Mostra questa finestra:

Zenity File Selection 2

Usato in questo modo permette di scegliere un solo file. Se vogliamo selezionare più file possiamo usare le due opzioni --multiple e --separator=C dove C è il carattere che vogliamo venga utilizzato per separare i file fra loro nell’output, quello predefinito è la barra verticale (il pipe, ‘|‘). Per selezionare più file basta tenere premuto il tasto Control (Ctrl) mentre si puntano i file con un clic del mouse.

Se invece vogliamo restringere la selezione solo alle directory, si usa l’opzione --directory. In questo caso i file saranno visualizzati in grigio e non saranno selezionabili. Vale anche per le directory l’opzione --multiple.

Se intendiamo salvare un file, le finestre di dialogo mostrare non aiutano, perché non permettono di digitare il nome di un file. Basta però utilizzare l’opzione --save, ed appare la consueta finestra di dialogo del salvataggio file. Il file non viene creato, e si può controllare l’esistenza di un file con lo stesso nome includendo l’opzione --confirm-overwrite. In questo caso se viene scelto un file già esistente viene proposto un messaggio di avvertimento:

Zenity File Selection 3

Se si risponde Annulla si rimane nella finestra di selezione e si può scegliere un altro nome, mentre se si preme Sostituisci, Zenity ritorna con il nome del file scelto. Il file non viene comunque toccato, sta a noi decidere il suo destino nel resto dello script shell.

Combinando invece le due opzioni --save e --directory, il comportamento cambia: il nome che viene scritto è inteso come nome di directory, che viene creata immediatamente alla pressione del tasto OK.

Invece l’opzione --multiple è incompatibile con la modalità salvataggio, ed è ignorata con l’emissione di un messaggio di avvertimento.

Tags: , ,

Aggiungere interfacce grafiche agli script shell #2

Nella prima parte abbiamo visto come porre una semplice domanda e come notificare all’utente lo stato dell’esecuzione in uno script shell.

In questa seconda parte ci occuperemo della possibilità offerta da Zenity di manipolare l’input e l’output di testo.

Per avere un input dall’utente in forma di testo scritto, l’opzione da usare è --entry, in questo modo:


$ zenity --entry --title="Un input di testo" --text="Inserisci qualcosa"

Il cui risultato è:

Zenity Entry Dialog 1

Possiamo decidere la dimensione della finestra di dialogo con l’opzione --width=N dove N è la larghezza totale in pixel, e possiamo fornire un testo predefinito già inserito nella casella con l’opzione --entry-text=stringa, come in questo esempio:


$ zenity --entry --width=300 --title="Un input di testo" --text="Inserisci qualcosa" --entry-text="Questo è già dentro"

Il cui risultato è:

Zenity Entry Dialog 2

Il testo digitato viene stampato nello standard output alla pressione del pulsante OK, mentre niente viene restituito se si preme il pulsante Annulla. Contestualmente, il valore di ritorno è zero (true) se si preme OK e diverso da zero se si preme annulla o si chiude la finestra dal pulsante nella barra del titolo.

Ecco un pezzetto di codice in Bash che distingue fra le varie situazioni:


#!/bin/bash

# mostra la finestra di dialogo e assegna quello che zenity manda allo
# standard output alla variabile "varstringa"
# notare i "backtick" -> ` `
varstringa=`zenity --entry --width=300 --title="Zenity: input di testo" --text="Inserisci qualcosa"`

# prende il valore di ritorno del comando precedente
ritorno=$?

# controlla se si è premuto OK o Annulla
if [ "$ritorno" -ne 0 ]; then
  # è stato premuto annulla o chiusa la finestra
  zenity --warning --title="Test di Zenity" --text="Hai annullato"
  exit 1
else
  # è stato premuto OK
  zenity --info --title="Test di Zenity" --text="Hai scritto: '$varstringa'"
fi

Il codice è anche qui.

Una ulteriore opzione, --hide-text, permette di nascondere il testo durante la digitazione come durante l’inserimento di una password:


$ zenity --entry --title="Input nascosto" --text="Inserisci una password" --hide-text

Il cui risultato, digitando qualcosa, è:

Zenity Entry Dialog 3

Se invece di una singola riga vogliamo mostrare un testo lungo, come ad esempio un file di log, l’opzione da usare è --text-info, nella sua forma più semplice:


$ zenity --text-info --title="Vedere un intero file" --filename zenity-entry-02

che mostra il file zenity-entry-02 all’interno di una finestra:

Zenity Text 1

L’opzione --filename permette di specificare un file da mostrare. Se si vuole poter cambiare il contenuto mostrato si può usare l’opzione --editable che consente modifiche al testo mostrato, con queste limitazioni:

  • Non si ha praticamente nessuna funzione avanzata di editing.
  • Le righe troppo lunghe vengono spezzate solo a video, mandandole a capo. Nell’output rimangono invece integre.
  • Il file non viene modificato. Il testo completo viene stampato sullo standard output, e tocca a noi catturarlo e dirigerlo su un altro file.

Se poi avete esperienza di shell script saprete anche che usando questo comando:


$ zenity --text-info --filename zenity-entry-02 --editable > zenity-entry-02

si giunge ad un risultato ben diverso da quello atteso: il file zenity-entry-02 viene svuotato del contenuto e la finestra di Zenity viene mostrata vuota, perché nella ridirezione dell’output il file zenity-entry-02 viene aperto in scrittura e troncato a zero byte prima di eseguire il comando Zenity.

Il testo mostrato può anche essere fornito dinamicamente:


$ find / -name "*.pdf" | zenity --text-info --title="I file pdf sul tuo disco" 

che produce questo:

Zenity Text 2

con la lista dei file PDF che viene allungata man mano che il comando find ne trova altri.

Per ora chiudiamo qui, ma Zenity ci riserva molte altre sorprese.

Tags: , ,