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.

L’importanza di chiamarsi Vim

Riprendo, dopo un lungo silenzio, a riportare qualche appunto su queste pagine.

Ovviamente lo faccio con una annotazione di scarsa utilità :).

Chi  usa vi/vim si sarà trovato, voglio sperare di non essere il solo, ad avere alcuni problemi con lo spostamento del cursore in modalità editing. Se, per distrazione, si usano le frecce di direzione per spostarlo prima di aver premuto il nostro fidato <esc>, ci si trova con una nuova riga contenente un solo carattere che cambia a seconda della direzione che volevamo far prendere al nostro cursore. E più volte lo si preme (repetita juvant) più righe appaiono.

Se il problema si verifica, non è detto, infatti, che la vostra installazione lo evidenzi, la soluzione è tanto breve quanto banale: invocare vim con il suo nome esteso, vim appunto, piuttosto che con il più tradizionale e conciso vi (che mette vim in modalità compatibile con vi e genera il problema).

Se volete continuare ad invocare vi ma non volete che il problema si ripresenti non vi serve altro che un semplice alias per risolvere il tutto: alias vi=’vim’

E questo e tutto (che per un post che può essere riassunto in – chiamate vim con il suo nome invece che vi – mi sembra abbastanza)

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

Problemi con Hardy Heron ed i driver nvidia 169.12

Un post veloce in attesa che arrivi il dissipatore per il nothbridge del mio computer. Pare che ci siano problemi con l’upgrade di hardy haron in caso si utilizzi una scheda grafica nvidia.

E’ qualche tempo che utilizzo H. H. e, con l’ultimo aggiornamento mi sono trovato il driver nvidia non utilizzabile. Pensavo si trattasse di un mio problema dovuto a strane manipolazioni con i file di configurazione. Altri mi hanno segnalato lo stesso problema ed allora mi sono risolto a scrivere quaste poche righe nel caso qualcuno si trovasse con il mio stesso problema.

Il sintomo principale lo si rileva utilizzando dmseg, al momento di caricare il modulo nvidia si evidenzia una incongruenza di interfaccia ed il modulo non viene caricato.

La faccio breve perché a momenti si surriscalderà la piastra madre ed il computer andrà in tilt.

Dopo diverse prove (ho anche scaricato i driver dal sito del produttore) mi sono accorto che in
/etc/default/linux-restricted-modules-common non c’erano più tra i moduli da mettere in blacklist, nv ed nvidia_new, dopo averli ripristinati il problema si è risolto.

In pratica basta inserire

DISABLED_MODULES="nv nvidia_new"

nel file in questione (o modificare l’istruzione già presente in caso siano già presenti altri moduli).

Altro file da controllare è /etc/modules in cui va aggiunta (se non è già presente) la riga

nvidia -i

E questo, per ora, è tutto

P. S.
Ovviamente questo post non ha alcuna pretesa di autorità, descrive un problema in cui mi sono trovato ed il modo, personalissimo, in cui l’ho risolto. Questo non toglie che possa funzionare anche per voi e quindi ho deciso di postarlo come case history.

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? ;))

vim, putty e il tastierino numerico

impostazioni puttyMi capita spesso di collegarmi a macchine linux tramite putty, e mi sono spesso trovato nella spiacevole situazione di non poter utilizzare il tastierino numerico per inserire i numeri in vim.

Ogni volta che ci provavo ottenevo dei caratteri ed un ritorno a capo, per non parlare dell’impossibilità di cambiare riga in modalità inserimento.

La soluzione è rapida tanto quanto è breve il post: basta settare una delle impostazioni del client ssh:

Change settings > Terminal > Features > Disable application keypad mode

E questo è tutto

Split di una stringa in bash

Direi che ormai è arrivato il momento di un articolo che abbia una certa utilità (e quindi si merita la lettera iniziale maiuscola :))

Normalmente preferisco usare cut o awk per separare i diversi token contenuti in una stringa.

Stamattina devo sostituire alcune regole di accesso su un firewall (iptables), ho bisogno di individuarle con delle regole ad hoc contenute in un array, la prima riga individua le regole e crea una lista con le loro posizioni.

NEWRULE[1]='iptables -R %s -i eth0 -t tcp --dport 443 --jump HTTPS_ENABLED'
NEWRULE[2]='iptables -R %s -i eth2 -t tcp --dport 443 --jump HTTPS_ENABLED'
 RULES_POSITION=`$IPT -vn --line-number -L INPUT | grep 443 | grep -n . | cut -f1 -d\ `
for INPOINT in $RULES_POSITION; do
   INDICE=${INPOINT%:*}
   POSITION=${INPOINT#*:}
   printf "${NEWRULE[INDICE]}" $POSITION
done


tra il codice riportato ci interessano in particolare le righe

   INDICE=${INPOINT%:*}
   POSITION=${INPOINT#*:}

ci aspettiamo in input delle stringhe del tipo formate da due numeri separate dai due punti, ad esempio 1:24.

Nel nostro caso il separatore è il simbolo di due punti

L’espansione si attiva con ${}, all’interno delle parentesi graffe va inserito il nome della variabile da separare in due. %:* prende la parte della variabile che va dall’inizio alla prima occorrenza del separatore (in questo caso :). Una nota. Questo comportamento è detto lazy , se si vuole utilizzare una selezione greedy bisogna usare un doppio simbolo di percentuale.

Cercherò di spiegarmi meglio:

  • La selezione greedy (=avida) va a cercare il separatore più lontano dall’inizio della stringa
  • La selezione lazy (=svogliata) invece si ferma non appena trova il primo separatore utile

Il comportamento è analogo per l’operatore # (## in versione greedy) che invece seleziona a partire dall’ultimo carattere della string.