\

Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

Você está aqui: TWikiBar > BatePapos > TWikiBarChiacchiere011
Controles: EDITAR ANEXAR MAIS MAIS ALTERACOES IMPRIMIR - Última Atualização: [24 Apr 2013 - V.3]

Chiacchiere da Bar Parte XI



     - Allora, Tutto a posto?

     - Ok, ma se ricordi mi hai chiesto di scrivere un programma tale che, al variare della grandezza dello schermo, al centro compaia dinamicamente, con carattere inverso, il numero di righe e di colonne come Linux fa normalmente. Io l'ho scritto ma l'aspetto non è esattamente lo stesso.

     - Non mi importa dell'aspetto, volevo solo che tu ti esercitassi per imparare ciç che abbiamo visto. Fammi veder cos'hai combinato.

$ cat grandezzaschermo.sh #!/bin/bash # # Mostra al centro dello schermo, con carattere inverso, # il numero di righe e colonne ogni volta # che la grandezza dello schermo è alterata. # trap Cambia 28 # 28 = segnale generato dalla variazione della grandezza # dello schermo e Cambia è la funzione che farà tutto ciò.

Bold=$(tput bold) # Grassetto Rev=$(tput rev) # Carattere inverso Norm=$(tput sgr0) # Riporta lo schermo alle impostazioni di default

Cambia () { clear Colonne=$(tput cols) Righe=$(tput lines) tput cup $(($Righe / 2)) $(((Colonne - 7) / 2)) # Centro dello schermo echo $Bold$Rev$Colonne X $Righe$Norm }

clear read -n1 -p "Varia la grandezza dello schermo o premi un tasto per terminare"

     - Ottimo! Al diavolo l'aspetto, magari poi ti spiego qualche trucco per migliorarlo: l'importante è che il programma funziona ed è pure snello.

     - Diamine, ho perso un sacco di tempo cercando di scoprire come aumentare il corpo del carattere ...

     - Lascia perdere per adesso: piuttosto diamo un'occhiata a cose più interessanti e utili.

Named Pipe

Un altro tipo di pipe è la named pipe, detta anche FIFO. FIFO è l'acronimo di First In First Out che si riferisce alla proprietà secondo la quale l'ordine dei byte che entrano nella pipe è lo stesso di quelli in uscita. Il name in named pipe è, in realtà, il nome di un file. I file di tipo named pipes sono mostrati dal comando ls come tutti gli altri ma con alcune piccole differenze. Osserva:

$ ls -l pipe1 prw-r-r-- 1 julio dipao 0 Jan 22 23:11 pipe1|

La p nella colonna più a sinistra indica che fifo1 è una named pipe. Il resto dei bit di controllo dei permessi di lettura e scrittura nella pipe funzionano allo stesso modo dei file normali. Nei sistemi più moderni una barra verticale (|) collocata alla fine del nome del file è un'altra informazione e nei sistemi LINUX, nei quali l'opzione colore è abilitata, il nome del file è scritto in rosso per default.

Nei sistemi più vecchi, le named pipes sono generati tramite il programma mknod, normalmente situato nella directory /etc.

Nei sistemi più moderni lo stesso compito è svolto da mkfifo. Il programma mkfifo riceve come argomento uno o più nomi e genera pipe con quegli stessi nomi. A esempio, per generare una named pipe con nome pipe1, esegui:

$ mkfifo pipe1

Como sempre, il modo migliore per mostrare il funzionamento di qualcosa è fare esempi. Supponi di aver generato la named pipe mostrata prima. Lavoriamo con due sessioni o due console virtuali, oppure con una di ogni tipo. In una esegui:

$ ls -l > pipe1

e nell'altra:

$ cat < pipe1

Voilà! L'output del comando eseguito nella primea console è mostrato nella seconda. Osserva che l'ordine in cui i comandi sono stati eseguiti non è importante.

Se hai fatto attenzione hai certamente notato che il primo comando eseguito sembrava essersi "congelato". Ciò accade perché l'altra estremità della pipe non era ancora collegata, per cui il sistema operativo ha messo in sospeso il primo processo fino a quando il secondo non “ha aperto” la pipe. Affinché un processo che usa pipe non resti in modalità wait, è necessario che a un'estremità della pipe si trovi un processo "chiacchierone" e dall'altra un programma "ascoltatore": nell'esempio, ls era il "parlatore" e cat il "grande orecchio".

Un impiego molto utile delle named pipes è permettere che programmi senza alcuna relazione tra loro possano comunicare. Le named pipes sono utilizzate anche per sincronizzare processi, poiché puoi collocare, in un punto ben determinato, un processo per "ascoltare" o "parlare" in una certa named pipe e il processo terminerà solo se un altro processo "parlerà" o "ascolterà" in quella pipe.

Hai già visto che l'uso di questo strumento è ottimo per sincronizzare processi e per bloccare l'accesso ai file in modo da evitare perdita e/o corruzione di informazioni dovuto a interventi simultanei (concorrenza). Vediamo alcuni esempi.

Sincronizzazione di processi.

Supponi di lanciare parallelamente due programmi (processi) i diagrammi a blocchi delle cui routine siano come in figura:

Grafico della Named Pipe

I due processi sono lanciati in parallelo e nel BLOCCO1 di Programma1 le tre classificazioni sono lanciate nel modo seguente:

    for File in BigFile1 BigFile2 BigFile3
    do
        if  sort $File 
        then
            Semaforo=vai
        else
            Semaforo=ferma
            break
        fi
    done
    echo $Semaforo > pipe1
    [ $Semaforo = ferma ] &&
        {
        echo Errore nella classificazione dei file
        exit 1
        }
    ...

Il comando if testa ogni classificazione effettuata. Se si verifica un problema, le classificazioni successive sono interrotte, un messaggio contenente la stringa ferma è inviata a pipe1 e programma1 è interrotto in modo anomalo.

Mentre Programma1 eseguiva il suo primo blocco (le classificazioni), Programma2 eseguiva BLOCCO1 mentre processava le sue routine di apertura e i menu parallelamente a Programma1 con un discreto risparmio di tempo.

Il frammento di codice di Programma2 che segue mostra la transizione da BLOCCO1 a BLOCCO2:

    OK=`cat pipe1`
    if  [ $OK = va ]
    then
        ...
        Routine di stampa
        ...
    else    #  Hai ricevuto "ferma" in OK
        exit 1
    fi

Dopo l'esecuzione del suo primo blocco, Programma2 si metterà ad "ascoltare" pipe1 restando in attesa fino a che le classificazioni di Programma1 non terminano, testando poi il messaggio passato da pipe1 per decidere se i file sono integri prima di stampare o se il programma dovrà essere interrotto. È, quindi, possibile lanciare programmi in modo asincrono e poi sincronizzarli quando è necessario risparmiando così un sacco di tempo.

Blocco di file

Supponi di aver scritto una CGI (Common Gateway Interface) in Shell per contare quanti hit riceve un certo URL e che la routine di conteggio sia come segue:

    Hits="$(cat page.hits 2> /dev/null)" || Hits=0
    echo $((Hits=Hits++)) > page.hits

In questo modo, se la pagina riceve due o più accessi contemporanei, uno o più potrebbero essere persi, basta che il secondo accesso sia fatto dopo la lettura del file page.hits e prima della sua scrittura, cioè è sufficiente che il secondo accesso sia fatto dopo che il primo ha eseguito la prima riga dello script e prima di eseguire la seconda.

Come mi sono mosso? Per risolvere o problema di concorrenza utilizziamo una named pipe. Buttiamo giù uno script che sarà il demone qche riceverà tutte le richiesta per incrementare il contatore. Nota che sarà utilizzato da qualsiasi pagina del nostro sito che abbia necessità di un contatore.

$ cat contahits.sh #!/bin/bash

PIPE="/tmp/pipe_contatore" # file named pipe # dir in cui saranno collocati i file contatori di ogni pagina DIR="/var/www/contatore"

[ -p "$PIPE" ] || mkfifo "$PIPE"

while : do for URL in $(cat < $PIPE) do FILE="$DIR/$(echo $URL | sed 's,.*/,,')" # OSS1: nel sed precedente, poiché era necessario trovare # una barra, usiamo una virgola come separatore. # OSS2: quando dovrà funzionare come demone commenta la riga seguente echo "file = $FILE"

n="$(cat $FILE 2> /dev/null)" || n=0 echo $((n=n+1)) > "$FILE" done done

Poiché questo script modifica i file, non ci sono problemi di concorrenza.

Questo script sarà un demone, cioè resterà in esecuzione in background. Quando una pagina riceverà una visita, essa scriverà il suo URL nel file della pipe. Per testare lo script esegui questo comando:

    echo "test_pagina.html" > /tmp/pipe_contatore 

Per evitare errori, aggiungiamo la seguente riga per ogni pagina alla quale vogliamo aggiungere un contatore:

    <!--#exec cmd="echo $REQUEST_URI > /tmp/pipe_contatore"-->

Nota che la variabile $REQUEST_URI contiene il nome del file che il navigatore (browser) ha richiesto.

Quest'ultimo esempio è frutto di uno scambio di idee intercorso con il mio amico, nonché maestro di Shell, Thobias Salazar Trevisan, che ha creato lo script e lo ha messo nel suo eccellente sito. Consiglio a tutti coloro che vogliono imparare la Shell (e che parlano portoghese frown ) di dargli un'occhiata ( E non dimenticate di aggiungerlo ai preferiti).

Ahhh! Non penserai davvero che l'argomento named pipes sia finito qui? Ti sbagli. Adesso te ne mostrerò un uso diverso.

Sostituzione di processi

Ti ho appena dato un sacco di suggerimenti sulle named pipes. Adesso ti farò vedere che anche la Shell usa le named pipes in un modo piuttosto singolare, cioè la sostituzione di processi (process substitution). Una sostituzione di processi si ha quando si inserisce un comando o una pipeline di comandi tra parentesi e un < o un > attaccato alla parentesi di sinistra. A esempio, digitando:

$ cat <(ls -l)

avremo come risultato che il comando ls -l è eseguito, come è normale che sia poiché posto tra parentesi, in una sottoshell : il comando, tuttavia, redirezionerà il suo output in una named pipe temporanea che la Shell genera, nomina e poi rimuove. A questo punto, cat avrà un nome di file valido da leggere (esattamente la named pipe, il cui dispositivo logico associato è /dev/fd/63), e avremo lo stesso output generato dal comando ls -l, tuttavia con alcuni passaggi in più, cioè in modo più oneroso per il computer.

Come possiamo verificarlo? Facile... Osserva il comando seguente:

$ ls -l >(cat) l-wx------ 1 jneves jneves 64 Aug 27 12:26 /dev/fd/63 -> pipe:[7050]

Ecco una vera named pipe.

Di sicuro starai pensando che questa è una stramberia da nerd. Supponi allora di avere due directory (dir e dir.bkp) e di voler sapere se i loro dati sono uguali (il vecchio dubbio sull'aggiornamento del backup ). È sufficiente confrontare i dati dei file delle due directory con il comando cmp:

$ cmp <(cat dir/*) <(cat dir.bkp/*) || echo backup non aggiornato

o ancora meglio:

$ cmp <(cat dir/*) <(cat dir.bkp/*) >/dev/null || echo backup non aggiornato

Come si vede sopra, il confronto è effettuato su tutte le righe di tutti i file di entrambe le directory. Per accelerare il processo potremmo confrontare solamente l'elenco lungo di entrambe le directory, poiché qualsiasi alterazione apportata a un file è mostrata nel giorno/ora di modifica e/o nella grandezza del file. Osserva:

$ cmp <(ls -l dir) <(ls -l dir.bkp) >/dev/null || echo backup non aggiornato

Questo è un esempio puramente didattico, ma sono così tanti i comandi che producono più di una riga di output che esso può servire da guida per altri. Adesso voglio produrre un elenco dei miei file, numerarli e infine ottenere il totale dei file della directory corrente:

    while read file
    do
        ((i++)) # così non serve inizializzare i
        echo "$i: $file"
    done < <(ls)
    echo "Nella directory corrente (`pwd`) ci sono $i file"

Lo so, lo so, ci sono altri modi per svolgere lo stesso compito. Usando il comando while, la maniera più comune di risolvere il problema è la seguente:

    ls | while read file
    do
        ((i++)) # a così non serve inizializzare i
        echo "$i: $file"
    done
    echo "Nella directory corrente (`pwd`) ci sono $i file"

Eseguendo lo script potrebbe sembrare che tutto funzioni a dovere: tuttavia, nel comando echo dopo done, dovresti notare che il valore di $i si perde. Ciò è dovuto al fatto che questa variabile è incrementata all'interno di una sottoshell generata dalla pipe (|) e che si esaurisce con il comando done portandosi via tutte le variabili generate al suo interno e le modifiche apportate a tutte le variabili incluse quelle generate esternamente.

Solo per farti vedere come una variabile creata all'esterno di una sottoshell e e modificata al suo interno perde le modifiche apportate ad essa quando la sottoshell è chiusa, esegui lo script che segue:

    #!/bin/bash
    LIST=""                  # Generata nella shell principale
    ls | while read FILE     # Inizio della sottoshell
    do
        LIST="$FILE $LIST"   # Modificata all'interno della sottoshell
    done                     # Fine della sottoshell
    echo :$LIST:

Al termine dell'esecuzione vedrai che appariranno solo i due-punti (::), ma quando ho introdotto l'esempio ho detto che era puramente didattico perché esistono modi migliori per svolgere lo stesso compito. Osserva:

$ ls | cat -n -

oppure, usando la sostituzione di processi:

$ cat -n <(ls)

Un ultimo esempio: vuoi confrontare file1 e file2 tramite il comando comm, ma esso ha bisogno che i file siano ordinati. In questo caso il modo migliore per farlo è:

$ comm <(sort file1) <(sort file2)

Ciò evita che tu esegua le seguenti operazioni:

$ sort file1 > /tmp/sort1 $ sort file2 > /tmp/sort2 $ comm /tmp/sort1 /tmp/sort2 $ rm -f /tmp/sort1 /tmp/sort2

Signori e signore, le nostre Chiacchiere da Bar sono giunte alla fine frown . Mi sono divertito molto e ho ricevuto numerosi elogi per il lavoro svolto nell'arco di dodici mesi e, cosa assai importante, ho stretto numerose amicizie e ho bevuto un sacco di birre gratis con i lettori «incontrati ai congressi e alle conferenze a cui ho partecipato in ogni angolo del nostro amato Brasile.

Alla nostra salute. Cin cin!.

- Chico, chiudi il mio conto: da adesso si cambia bar.

Ciao a tutti!


Licença Creative Commons - Atribuição e Não Comercial (CC) 2017 Pelos Frequentadores do Bar do Júlio Neves.
Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative Commons License: Atribuição-UsoNãoComercial-PermanênciaDaLicença.