Traduzione a linea di comando

Questa mattina mi son svegliato, ed ho trovato un post stuzzicante che promette, e mantiene, di aggiungere alla linea di comando un semplice traduttore. Si tratta di uno script di una riga che si appoggia a http://www.wordreference.com per effettuare la traduzione.

Alla fine del post c’è la richiesta di spendere qualche minuto attorno a google ed al suo sistema di traduzioni per poter fare la stessa cosa.

una prima versione la trovate quì sotto, non si tratta più di un one line script perché ho l’ossessione di aggiungere parametri a tutto il codice che posso manipolare. Mi passerà.

Il codice non è elegantissimo ma più che qualche minuto non potevo assolutamente dedicargli.

#!/bin/bash
S='auto'
T='it'
if [ "$2" != "" ]; then
  S="$2"
fi
if [ "$3" != "" ]; the
  T="$3"
fi

lynx "http://translate.google.com/translate_t?sl=$S&tl=$T&text=$1" -dump | \
     grep "Suggest a better translation" -B2 | head --lines=1

I parametri sono semplicissimi da usare, ammesso di chiamare tran il file, basta invocarlo con la parola da tradurre come unico parametro. Google cercherà di individuare la lingua della parola e ne restituirà, se riesce, il corrispettivo in italiano.

In caso sia necessario allora si possono specificare anche il codice iso della parola da tradurre, ed il codice della lingua in cui tradurre, rispettivamente come secondo e terzo parametro.

Le migliorie possibili sono tante, ad esempio usare l’opzione -source invece che -dump in lynx in modo da essere sicuri di individuare la parola tradotta nell’html che google restituisce ma il tempo, per ora, è tiranno.

Bash: redirigere i file descriptor / 2

Ci siamo lasciati con l’amaro in bocca, vi avevo promesso giocolerie impreviste, si magari avrò anche esagerato, e vi ho lasciato dopo aver appena introdotto gli operatori di redirezione in bash.

Sono qui per rimediare e ci mettiamo subito a lavoro. La volta scorsa abbiamo parlato dei tre file descriptor predefiniti ma avevo anche accennato alla possibilità di avere anche altri file descriptor aperti. Per aprirne uno non bisogna far altro che reindirizzarlo dove meglio ci aggrada.

Per esempio provate a digitare il comando

echo “ciao 12” >&12

Che genererà un messaggio di errore e poi provate a digitare un comando simile ma funzionante (anche se apparentemente inutile)

echo “Il comando di prima non funzionava, questo si” 12>&1 >&12; echo “anche questo non funziona” >&12

Non è inutile perché ci fa capire due cose: che i reindirizzamenti procedono da sinistra a destra e che i file descriptor non standard vengono chiusi al termine dell’esecuzione di un comando/script.

Per inciso, è possibile chiudere esplicitamente un file descriptor utilizzando la sintassi n>&- dove n è il file descriptor da chiudere.

Possiamo anche rendere permanente una redirezione, o l’apertura di un file descriptor utilizzando exec. Questo comando non fa altro che lanciare una shell che sostituisce quella attuale e, nel fare ciò è possibile aprire dei file descriptor o reindirizzarne altri.

Per provare quest’ultima affermazione proviamo a farci uno scherzo. Apriamo due terminali e lanciamo il comando tty che non fa altro che stampare a video lo pseudofile cui è connesso il terminale. Da X troverete che il risultato sarà qualcosa di simile a  /dev/pts/0 ed a /dev/pts/1 per l’altra finestra.

A questo punto in nella prima delle due invochiamo il comando exec >/dev/pts/X dove X deve essere l’id del terminale della seconda finestra e voilà, l’output della vostra prima console andrà a finire tutto nella seconda (ad eccezione dello standard error che non viene influenzato). Per tornare alla normalità bisognerà semplicemente ridare il comando facendo puntare la redirezione alla console legittima.

Vi lascio con un’ultimo trucco:

command 3>&1 1>&2 2>&3

che non fa altro che scambiare tra di loro i file descriptor di standard output e standard error, nel caso servisse.

E questo è tutto, per ora

bash: Redirigere i file descriptor

In un precedente pezzo ho scritto qualche parola su tee e vi raccontavo che la sua principale limitazione era l’impossibilità di registrare quanto stava accadendo sullo standard error. Vista la natura introduttiva del pezzo ho preferito non tediarvi con le giocolerie possibili con i file descriptor e restare concentrato sul comando.

Ora è venuto il momento, nel frattempo ho studiato, di affrontare il problema e fare qualche gioco di prestigio con stderr e stdout. Tutto questo senza l’ausilio di altro che le nostre dita, una tastiera e la nostra amata, odiata,mai indifferente, bash. La carne al fuoco è tanta e quindi ho deciso di dividere il tutto in più parti.

Partiamo con un minuscolo script d’esempio per introdurre i file descriptor ed i comandi di redirezione.

#!/bin/bash
echo "Questo è un messaggio indirizzato allo standard error" 1>&2
echo "Questo è un messaggio indirizzato allo standard output" 1>&1

Non penso ci sia davvero bisogno di spiegare quello che fa 🙂 . Mi preme solamente illustrare la sintassi >&d, ma, prima, parliamo dei file descriptor, che non sono altro che delle astrazioni, dei segnaposto, di file aperti in lettura o scrittura.

Ad ogni programma, anche agli script bash, a voler essere precisi alla shell che esegue lo script, sono associati almeno tre file descriptor: uno per l’input, stdin, uno per l’output, stdout, ed uno, stderr, per la segnalazione degli errori. Hanno, nell’ordine, indice 0, 1 e 2.

Bash mette a disposizione due operatori, il man li chiama metacaratteri, per la redirezione: < e >.

Il loro uso è semplice e lo conoscerete già, ad esempio

echo 'hello file!' > nome di un file

fa in modo che la shell invii ‘hello file!’ ad un file piuttosto che al mondo intero. Abbiamo appena rediretto l’output su file. Analogamente l’operatore < permette di redirigere l’input e quindi possiamo restituire al mondo il saluto che gli dobbiamo con cat < file.

Se pensate che sia sconveniente salutare il mondo apostrofandolo file provate con

sed “s/file/world/” file | cat 🙂

Utilizzando >& al posto del semplice > è possibile redirigere i file descriptor tra di loro piuttosto che su file presenti sul filesystem (non ho scritto disco perché sarebbe inesatto).

A questo punto possiamo interpretare il comando

echo "Questo è un messaggio indirizzato allo standard error" 1>&2

come invia allo standard outpu (è quello che fa echo) la stringa “Questo è un messaggio indirizzato allo standard error” e subito dopo aggiungiamo: a proposito, invia i dati che passano per standard output (1)  dove vanno a finire i dati destinati allo standard error (2). Solitamente si usa la versione >&2 che sottintende 1 (stdout) come file descriptor di partenza. Il secondo comando presente nello script poteva tranquillamente limitarsi a

echo "Questo è un messaggio indirizzato allo standard output"

ma se non sono prolisso non mi diverto.

La redirezione finisce qui, per ora, abbiamo tutti i concetti base per proseguire con i nostri esperimenti ma ho ormai scritto troppo e vi rimando ad un prossimo approfondimento.

Bash tee

No, il te (preferisco il lapsang souchong) non c’entra nulla. Il protagonista di questo rapido post è il comando tee che permette di duplicare il suo standard input e salvarne una copia su file mentre l’altra viene reindirizzata allo standard output.

Attenzione: tee si disinteressa dello standard error (che magari è più interessante da tracciare) perché |, il simbolo di pipe, inoltra un solo flusso alla volta, di solito stdout.

I parametri utili sono solamente due,

  • -a che permette di aprire in modalità append il file su cui riportare il flusso di dati che passa attraverso tee
  • -i che istruisce tee ad ignorare gli interrupt che dovessero raggiungerlo.

A cosa può servire un comando del genere? Per prima cosa è utile per salvare un risultato intermedio dall’interno di una pipe, ad esempio per fare debug o per manipolare successivamente, in maniera differente da quella implementata nella pipe gli stessi dati.
Una obiezione naturale potrebbe essere: peché non dividere l’elaborazione in due parti, salvare i dati in un file ed utilizzarli successivamente per far partire due elaborazioni parallele? Essenzialmente si tratta si gusti personale, o per dirla come Larry Wall, esistono più modi di scuoiare un gatto. Un’altro motivo che rende controindicato spezzare l’elaborazione in più parti, specie in caso di test, può essere l’alta variabilità dell’output di un comando, se l’output varia ogni volta che lanciamo un comando, lanciarlo e per vedere l’output che produce non basta perché un eventuale errore non sarebbe così individuabile.
Un altro utilizzo che se ne può fare è utilizzarlo come un semplice registratore dell’output di uno script (basta piazzare in fondo alla riga di comando un | tee file.log) .

Una cosa importante da valutare è che tee nasconde il codice d’uscita del programma di cui replica l’output. Verifichamolo:

Costruiamo un semplice script (test):

#!/bin/bash
echo Ciao
exit 1

e digitiamo a linea di comando:

./test ; echo $?

$? non è altro che l’exitlevel della pipe al momento della valutazione (nel nostro caso il valore restituito dallo script test) e, correttamente la linea di comando stampa 1. Non c’è bisogno che ve lo dica, la pipe

./test|tee ; echo $?

stampa 0. Si tratta dell’exitlevel di tee che, ovviamente, è zero.

Questo significa che uno script che fa affidamento sull’exit code di un comando precedente potrebbe non funzionare.

Bash: Trasposizioni

Venerdì mattina, tempo soleggiato e pomeriggio in permesso, domani è sabato, nulla potrà rovinarmi la giornata. A parte un mio collega che entra in ufficio e se ne esce con una frase che comincia con – abbiamo trovato il baco ieri sera – e finisce con – dobbiamo trasporre le cinquecento matrici che usiamo per i test. L’entusiasmo per la prima parte della frase si smorza subito non appena realizzo cosa sottindende la seconda: niente pausa caffè (tè nel mio caso) prima di aver modificato i file visto che con i test fermi nessun altro potrà lavorare.

Mi metto subito al lavoro ed inizialmente penso di fare un programmino in C. Quando mi ricordo (shame on me) che non ho tanto fresca la sintassi del linguaggio penso che farei prima con un paio di righe di bash piuttosto che rinfrescarmi la memoria.

Il problema che devo risolvere consiste nel manipolare i file che contengono le matrici (una per file) e salvare al loro posto la matrice trasposta.

La trasposta di una matrice è una matrice in cui sono state scambiate le righe con le colonne (in pratica la prima riga della matrice diventa la prima colonna della trasposta, la seconda riga rappresenta la seconda colonna e così via…)

La soluzione è più semplice del previsto e la riporto subito prima di commentarla. In particolare mi è piaciuto (perché risalito d’un tratto dagli abissi della memoria) l’uso di xargs che forse potrebbe lasciare qualcuno interdetto

#!/bin/bash
for i in $(seq 1 $3)
do
  head $1 -n $2 | cut -d \  -f $i | xargs
done

Lo script non fa altro che predere una matrice, rivoltarla e stamparla in standard output. A questo punto redirigere l’output in un nuovo file diventa banale, come è banale processare tutti i file in automatico. Torniamo allo script ed al suo commento, Chiamiamolo ad esempio trasponi.

Viene invocato come trasponi file 4 3 per trasporre una matrice, file, di 4 righe per 3 colonne. Sull’output avremo una matrice di 3 righe per 4 colonne. Nel file le matrici sono memorizzate per righe ed in ogni riga un elemento è separato dagli altri da uno spazio.

La riga di codice che crea le diverse righe è head $1 -n $2 | cut -d \ -f $i | xargs e potrebbe anche essersi ridotta in cut $1 -d \ -f $i | xargs ma l’ho scritta in quel modo per evidenziare il numero di righe della matrice da elaborare.

La prima parte del pipe seleziona la colonna da trasformare, l’ultima si occupa di assemblarla come riga.

xargs, di solito, si usa per leggere dallo standard input dei parametri da passare ad un comando. Se non viene specificato si assume che il comando a cui passare i parametri sia echo.
Un altro utilizzo di xargs, magari non così convenzionale, è mettere tutti su una riga i parametri che gli vengono passati in input uno per volta (sfruttando il fatto che il parametro predefinito sia echo).

Le potenzialità di xargs, tra l’altro, non si esaurscono qui, ci sono altre funzioni a cui può assolvere oltre quella canonicamente riconosciuta di “fornitore di parametri”. Potenzialità che saranno oggetto di un ulteriore post.

E questo, per ora, è tutto

convertire un file immagine nrg (Nero) in iso

Questa mattina l’ho passata cercando di montare in loop un file immagine fatto con Nero.

Dopo una serie di peripezie (sto lavorando con un cd live e non posso installare nulla sulla macchina ospite) sono arrivato ad una soluzione che è banale nella sua semplicità. Se dovesse servirvi, e se volete risparmiarvi qualche ora di navigazione internet, sempre che siate arrivati fin qui, la trovate qui sotto.

La maggior parte delle ricerche che ho fatto puntavano ad AcetoneIso, un programma per la manipolazione di cd e dvd che pur se molto utile aveva un problema per me insormontabile: kde

Continuando le ricerche arrivo ad un programma che sembra fare proprio al caso mio: nrg2iso

.

La descrizione è chiarissima e concisa ma, purtroppo devo fermarmi ancora una volta. Le ultime parole recitano “Run on any windows versions later than Win98” (Gira su qualunque versione di windows superiore a windows 98) :(.

Per fortuna è rilasciato sotto licenza GPL. Scarico il programma, apro il sorgente (si nota che ero alquanto disperato?) e, sorpresa, scopro che la differenza tra i due formati sta in un header di 300 kbyte che nel file nrg precede l’immagine iso.

Pensavo di doverlo ricompilare ed invece in linux è un attimo replicarne il comportamento:

dd if=file.nrg of=file.iso bs=1k skip=300

A questo punto è un altro attimo montare l’immagine iso in loop

mkdir /mnt/temp
mount -t iso9660 -o loop file.iso /mnt/temp

E questo è tutto

Nota: Il sistema funziona solamente (egregiamente) con i dischi DAO.
Sto continuando ad esplorare la questione, stay tuned.

DAO = Disk At Once (so che non c’era bisogno di scriverlo ma perché arrovellarsi? ;))

bash: head tail VS tail head

In questi giorni ho dovuto selezionare delle stringhe all’interno di alcuni file in bash.

Sono sempre stato solito usare i comandi head e tail concatenati nel modo seguente:

head <nomefile> --lines <linea da selezionare> | tail 1

ma scartabellando in internet nel tentativo di trovare un comando simile mi sono trovato di fronte ad una versione diversa della mia solita pipe:

tail <nomefile> --lines +<linea da selezionare> | head 1

La domanda che mi sono posto è stata: quale delle due versioni è più efficiente?

Su Diecimila iterazioni su diecimila file diversi la media misurata con time su 50 prove è stata di 39,74 secondi a prova per la prima versioe e di 40,97 per la seconda versione.

Diecimila iterazioni del comando sullo stesso file, in modo da testare il comportamento anche con il supporto della cache ha mostrato uno scarto ancora più ridotto, 26,984 contro 27,183 secondi e quindi considererei le due versioni sostanzialmente equivalenti.

Un’altra valutazione da fare riguarda i tempi, paragonabili nel caso in cui la riga da selezionare si trovi in testa o in coda al file.
Mi sarei aspettato uno sbilanciamento a favore di una delle due soluzioni in un caso controbilanciato da una situazione contraria nel caso opposto ma così non è stato. Qualcuno ha idea del perché?

E questo, per ora, è tutto