Capitolo 12. Programmazione

Indice

12.1. Script shell
12.1.1. Compatibilità con la shell POSIX
12.1.2. Parametri di shell
12.1.3. Costrutti condizionali della shell
12.1.4. Cicli di shell
12.1.5. La sequenza di elaborazione della riga di comando di shell
12.1.6. Programmi di utilità per script di shell
12.1.7. Dialogo di script di shell
12.1.8. Esempio di script di shell con zenity
12.2. Make
12.3. C
12.3.1. Semplice programma in C (gcc)
12.4. Debug
12.4.1. Esecuzione base di gdb
12.4.2. Fare il debug di pacchetti Debian
12.4.3. Ottenere un backtrace
12.4.4. Comandi gdb avanzati
12.4.5. Fare il debug di errori X
12.4.6. Controllare le dipendenze dalle librerie
12.4.7. Strumenti per rilevazione di memory leak
12.4.8. Strumenti di analisi statica del codice
12.4.9. Disassemblatore di binari
12.5. Flex - un Lex migliorato
12.6. Bison - Yacc migliorato
12.7. Autoconf
12.7.1. Compilare ed installare un programma
12.7.2. Disinstallare un programma
12.8. Pazzie con corti script Perl
12.9. Web
12.10. Traduzione di codice sorgente
12.11. Creare pacchetti Debian

Vengono forniti in questo capitolo alcune informazioni base da cui partire per imparare a programmare su un sistema Debian abbastanza da seguire il codice sorgente impacchettato. Quello che segue è un elenco dei pacchetti importanti per la programmazione e dei corrispettivi pacchetti di documentazione.

Tabella 12.1. Elenco di pacchetti di aiuto per la programmazione

pacchetto popcon dimensione documentazione
autoconf V:38, I:269 1868 "info autoconf" fornito da autoconf-doc
automake V:37, I:265 1784 "info automake" fornito da automake1.10-doc
bash V:826, I:999 6462 "info bash" fornito da bash-doc
bison V:11, I:109 2253 "info bison" fornito da bison-doc
cpp V:389, I:790 42 "info cpp" fornito da cpp-doc
ddd V:0, I:12 3929 "info ddd" fornito da ddd-doc
exuberant-ctags V:7, I:42 333 exuberant-ctags(1)
flex V:10, I:98 1225 "info flex" fornito da flex-doc
gawk V:443, I:535 2412 "info gawk" fornito da gawk-doc
gcc V:173, I:598 45 "info gcc" fornito da gcc-doc
gdb V:17, I:124 8989 "info gdb" fornito da gdb-doc
gettext V:52, I:345 6594 "info gettext" fornito da gettext-doc
gfortran V:8, I:79 16 "info gfortran" fornito da gfortran-doc (Fortran 95)
fpc I:4 120 fpc(1) e html forniti da fp-docs (Pascal)
glade V:1, I:9 2306 aiuto fornito attraverso menu (compilatore UI)
libc6 V:937, I:999 12333 "info libc" fornito da glibc-doc e glibc-doc-reference
make V:169, I:604 1296 "info make" fornito da make-doc
xutils-dev V:1, I:14 1466 imake(1), xmkmf(1), ecc.
mawk V:342, I:998 183 mawk(1)
perl V:618, I:994 575 perl(1) e pagine html forniti da perl-doc e perl-doc-html
python V:578, I:986 68 python(1) e pagine html forniti da python-doc
tcl V:30, I:442 22 tcl(3) e pagine dettagliate del manuale forniti da tcl-doc
tk V:31, I:433 22 tk(3) e pagine dettagliate di manuale forniti da tk-doc
ruby V:173, I:341 37 ruby(1) e guida interattiva di riferimento forniti da ri
vim V:119, I:395 2799 menu di aiuto(F1) fornito da vim-doc
susv2 I:0 16 scarica le specifiche "The Single UNIX Specifications v2"
susv3 I:0 16 scarica le specifiche "The Single UNIX Specifications v3"

Guide di riferimento in linea sono disponibili digitando "man nome" dopo aver installato i pacchetti manpages e manpages-dev. Le guide di riferimento in linea per gli strumenti GNU sono disponibili digitando "info nome_programma", dopo aver installato i pertinenti pacchetti di documentazione. Può essere necessario includere gli archivi "contrib e non-free, oltre all'archivio main, dato che alcune documentazioni GFDL non sono considerate conformi alle DFSG.

[Avvertimento] Avvertimento

Non usare "test" come nome di un file di prova eseguibile. "test" è un comando interno della shell.

[Attenzione] Attenzione

I programmi software compilati direttamente dai sorgenti andrebbero installati in "/usr/local" o "/opt" per evitare conflitti.

[Suggerimento] Suggerimento

Esempi di codice per creare la "Canzone 99 bottiglie di birra" dovrebbe dare buone idee per praticamente tutti i linguaggi di programmazione.

Uno script di shell è un file di testo con il bit di esecuzione impostato e contiene i comandi nel formato seguente.

#!/bin/sh
 ... righe di comando

La prima riga specifica l'interprete di shell che legge ed esegue il contenuto di questo file.

Leggere script di shell è il modo migliore per capire come funzioni un sistema *nix. In questa sezione vengono forniti alcune nozioni di riferimento e promemoria per la programmazione di shell. Vedere "Errori in shell" (http://www.greenend.org.uk/rjk/2001/04/shell.html) per imparare dagli errori.

A differenza della modalità interattiva della shell (vedere Sezione 1.5, «Il semplice comando di shell» e Sezione 1.6, «Elaborazione di testo stile Unix»), gli script di shell usano spesso parametri, costrutti condizionali e cicli.

Negli script di shell vengono spesso usati parametri speciali.


Le nozioni base da ricordare riguardanti la espansione dei parametri sono le seguenti.


I due punti ":" in tutti gli operatori nell'elenco precedente sono di fatto opzionali.

  • con ":" l'operatore = controlla che il suo operando esista e sia non nullo

  • senza ":" l'operatore = controlla solo che il suo operando esista


Ogni comando restituisce uno stato di uscita che può essere usato in costrutti condizionali.

  • Successo: 0 ("Vero")

  • Errore: non 0 ("Falso")

[Nota] Nota

"0" nel contesto condizionale della shell significa "Vero", mentre "0" nel contesto condizionale in C significa "Falso".

[Nota] Nota

"[" è l'equivalente del comando "test che valuta i propri argomenti sino a "]" come un'espressione condizionale.

Le espressioni condizionali di base che è bene ricordare sono le seguenti.

  • "<comando> && <se_successo_esegue_anche_questo_comando> || true"

  • "<comando> || <se_non_successo_esegue_anche_questo_comando> || true"

  • Una porzione su più righe di script come la seguente

if [ <espressione_condizionale> ]; then
 <se_successo_esegue_questo_comando>
else
 <se_non_successo_esegue_questo_comando>
fi

In questo caso il "|| true" finale era necessario per assicurare che lo script non termini accidentalmente a tale riga quando la shell è invocata con l'opzione "-e".



Gli operatori aritmetici di comparazione di interi nelle espressioni condizionali sono "-eq", "-ne", "-lt", "-le", "-gt" e "-ge".

A grandi linee la shell elabora uno script nel modo seguente.

  • La shell legge una riga.

  • La shell raggruppa parte della riga come un unico elemento se è racchiusa in "…" o '…'.

  • La shell spezza le altre parti della riga in elementi in base ai caratteri seguenti.

    • Spazi bianchi: <spazio> <tabulazione> <a capo>

    • Metacaratteri: < > | ; & ( )

  • La shell controlla, per ciascun elemento non racchiuso tra "…" o '…', la presenza di parole riservate per regolare il proprio comportamento.

    • Parole riservate: if then elif else fi for in while unless do done case esac

  • La shell espande gli alias se non sono racchiusi in "…" o '…'.

  • La shell espande il carattere tilde se non è racchiuso in "…" o '…'.

    • "~" → directory home dell'utente attuale

    • "~<utente>" → directory home di <utente>

  • La shell espande parametri nei loro valori, se non sono racchiusi in '…'.

    • Parametro: "$PARAMETRO" o "${PARAMETRO}"

  • La shell espande sostituzioni di comandi, se non sono racchiuse in '…'.

    • "$( comando )" → output di "comando"

    • "` command `" → output di "comando"

  • La shell espande glob di nomi percorso nei nomi di file corrispondenti, se non sono racchiusi in "…" o '…'.

    • * → qualsiasi carattere

    • ? → un carattere

    • […] → uno qualunque dei caratteri in ""

  • La shell cerca comando tra le cose seguenti e lo esegue.

    • Definizione di funzione

    • comando interno

    • file eseguibile in "$PATH"

  • La shell si sposta alla riga seguente e ripete nuovamente questo processo dall'inizio di questa sequenza.

Virgolette singole all'interno di virgolette doppie non hanno alcun effetto.

L'esecuzione di "set -x" nella shell o l'invocazione della shell con l'opzione "-x" fanno sì che la shell stampi tutti i comandi eseguiti. Ciò è piuttosto utile per il debug.

Questo è un semplice script che crea un'immagine ISO con dati RS02 forniti da dvdisaster(1).

#!/bin/sh -e
# gmkrs02 : Copyright (C) 2007 Osamu Aoki <osamu@debian.org>, Public Domain
#set -x
error_exit()
{
  echo "$1" >&2
  exit 1
}
# Inizializza le variabili
DATA_ISO="$HOME/Desktop/iso-$$.img"
LABEL=$(date +%Y%m%d-%H%M%S-%Z)
if [ $# != 0 ] && [ -d "$1" ]; then
  DATA_SRC="$1"
else
  # Seleziona la directory per creare l'immagine ISO dalla cartella sul desktop
  DATA_SRC=$(zenity --file-selection --directory  \
    --title="Seleziona la radice dell'albero di directory per creare l'immagine ISO") \
    || error_exit "Uscita durante la selezione della directory"
fi
# Controlla la dimensione dell'archivio
xterm -T "Controllo dimensione $DATA_SRC" -e du -s $DATA_SRC/*
SIZE=$(($(du -s $DATA_SRC | awk '{print $1}')/1024))
if [ $SIZE -le 520 ] ; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="La dimensione dei dati va bene per un CD di backup:\\n $SIZE MB"
elif [ $SIZE -le 3500 ]; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="La dimensione dei dati va bene per un DVD di backup:\\n $SIZE MB"
else
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="Dimensione dei dati troppo grande per farne il backup: $SIZE MB"
  error_exit "Dimensione dei dati troppo grande per farne il backup :\\n $SIZE MB"
fi
# solo xterm sicuramente ha un'opzione -e funzionante
# Crea immagine raw ISO
rm -f "$DATA_ISO" || true
xterm -T "genisoimage $DATA_ISO" \
  -e genisoimage -r -J -V "$LABEL" -o "$DATA_ISO" "$DATA_SRC"
# Crea dati ridondanti RS02 supplementari
xterm -T "dvdisaster $DATA_ISO" -e  dvdisaster -i "$DATA_ISO" -mRS02 -c
zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
  --text="dati ISO/RS02 ($SIZE MB) \\n creati per: $DATA_ISO"
# EOF

Si potrebbe voler creare un lanciatore sul desktop con un comando definito in modo simile a "/usr/local/bin/gmkrs02 %d".

Make è un'utilità per mantenere gruppi di programmi. Quando make(1) viene eseguito legge il file di regole, "Makefile" e aggiorna il file target se dipende da file prerequisiti che sono stati modificati dall'ultima volta che esso stesso è stato modificato oppure se il file target non esiste. L'esecuzione di questi aggiornamenti può avvenire in modo concorrente.

La sintassi del file di regole è la seguente.

target: [ prerequisiti ... ]
 [TAB]  comaando1
 [TAB]  -comando2 # ignora errori
 [TAB]  @comando3 # sopprime echo

Qui "[TAB] è il codice di TAB. Ciascuna riga è interpretata dalla shell dopo la sostituzione delle variabili di make. Usare "\" alla fine di una riga per continuare lo script. Usare "$$" per inserire "$" per valori di ambiente per uno script di shell.

Regole implicite per il target ed i prerequisiti possono essere scritte, per esempio, nel modo seguente.

%.o: %.c header.h

In questo caso il target contiene il carattere "%" (esattamente un carattere). Il "%" fa corrispondenza con qualsiasi sottostringa non vuota nei nomi di file dei target effettivi. Similmente i prerequisiti usano "%" per mostrare come i loro nomi trovino corrispondenza nei nomi dei target effettivi.



Eseguire "make -p -f/dev/null" per vedere le regole interne automatiche.

Si può impostare l'ambiente appropriato per compilare programmi scritti nel linguaggio di programmazione C nel modo seguente.

# apt-get install glibc-doc manpages-dev libc6-dev gcc build-essential

Il pacchetto libc6-dev, cioè la libreria GNU C, fornisce la libreria standard C che è una raccolta di file header e routine di libreria usati dal linguaggio di programmazione C.

Vedere come documenti di riferimento per C i seguenti.

  • "info libc" (documento di riferimento per le funzioni della libreria C)

  • gcc(1) e "info gcc"

  • ogni_nome_di_funzione_della_libreria_C(3)

  • Kernighan & Ritchie, "The C Programming Language", 2nd edition (Prentice Hall)

Il debug è un'importante fase del processo di programmazione. Sapere come fare il debug dei programmi rende buoni utenti Debian in grado di creare segnalazioni di bug significative.

Lo strumento di debug principale in Debian è gdb(1) che permette di ispezionare un programma mentre viene eseguito.

Installare gdb e i programmi correlati nel modo seguente.

# apt-get install gdb gdb-doc build-essential devscripts

Un buon tutorial su gdb viene fornito da "info gdb" o lo si può trovare altrove in rete. Quello che segue è un piccolo esempio d'uso di gdb(1) su di un "programma" compilato con l'opzione "-g" per produrre informazioni di debug.

$ gdb program
(gdb) b 1                # imposta un punto di interruzione alla riga 1
(gdb) run argomenti      # esegue programma con argomenti
(gdb) next               # riga successiva
...
(gdb) step               # passo successivo
...
(gdb) p param             # stampa parametro
...
(gdb) p param=12          # imposta il valore a 12
...
(gdb) quit
[Suggerimento] Suggerimento

Molti comandi gdb(1) possono essere abbreviati. L'espansione del tasto di tabulazione funziona come nella shell.

Dato che tutti i binari installati in un sistema Debian dovrebbero essere, in modo predefinito, snelliti con strip, la maggior parte dei simboli di debug non è presente nei normali pacchetti. Per poter fare il debug di pacchetti Debian con gdb(1) devono essere installati i corrispondenti pacchetti *-dbg o *-dbgsym (ad esempio libc6-dbg per libc6, coreutils-dbgsym per coreutils).

I pacchetti nello stile vecchio forniscono il proprio pacchetto *-dbg corrispettivo. È messo direttamente nell'archivio principale Debian insieme al pacchetto originale stesso. I pacchetti più recenti possono generare automaticamente pacchetti *-dbgsym quando compilati e tali pacchetti di debug sono messi separatamente nell'archivio debian-debug. Per maggiori informazioni fare riferimento agliarticoli sul Wiki Debian.

Se un pacchetto di cui si deve fare il debug non fornisce il proprio pacchetto *-dbg o *-dbgsym, è necessario installarlo dopo averlo ricompilato nel modo seguente.

$ mkdir /percorso/nuovo ; cd /percorso/nuovo
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo apt-get install fakeroot devscripts build-essential
$ apt-get source nome_pacchetto
$ cd nome_pacchetto*
$ sudo apt-get build-dep ./

Correggere i bug se necessario.

Spostare la versione del pacchetto ad una che non crei conflitti con le versioni ufficiali di Debian, ad esempio una che termini con "+debug1" quando si ricompilano versioni di cui esiste un pacchetto, o una che termini con "~pre1" quando si ricompilano versioni non ancora rilasciate in pacchetti nel modo seguente.

$ dch -i

Compilare ed installare i pacchetti con i simboli di debug nel modo seguente.

$ export DEB_BUILD_OPTIONS=nostrip noopt
$ debuild
$ cd ..
$ sudo debi nome_pacchetto*.changes

È necessario controllare gli script di compilazione del pacchetto ed assicurarsi di usare "CFLAGS=-g -Wall" per la compilazione di binari.

Flex è un veloce generatore di analizzatori lessicali compatibile con Lex.

Un tutorial per flex(1) viene fornito da "info flex".

È necessario fornire i propri "main()" e "yywrap()". Altrimenti il proprio programma flex dovrebbe apparire così per compilare senza una libreria. Questo è dovuto al fatto che "yywrap" è una macro e "%option main" abilita implicitamente "%option noyywrap".

%option main
%%
.|\n    ECHO ;
%%

In alternativa si può compilare con l'opzione per linker " -lfl" alla fine della propria riga di comando cc(1) (come "-ll" per AT&T-Lex). In questo caso non è necessario usare "%option".

Svariati pacchetti Debian forniscono un generatore di parser LR lookahead o parser LALR combatibile con Yacc.


Un tutorial per bison(1) viene fornito da "info bison".

È necessario fornire i propri "main()" e "yyerror()". "main()" chiama "yyparse()" che a sua volta chiama "yylex()", solitamente creato con Flex.

%%

%%

Autoconf è uno strumento per produrre script shell che configurano automaticamente pacchetti software di codice sorgente, in modo da adattarsi a molti tipi di sistemi *nix usando l'intero sistema di compilazione GNU.

autoconf(1) produce lo script di configurazione "configure". "configure" crea automaticamente un "Makefile" personalizzato usando il modello "Makefile.in".

Benché qualsiasi script AWK possa essere riscritto automaticamente in Perl usando a2p(1), gli script AWK di una sola riga si convertono meglio manualmente in script Perl di una riga.

Si consideri il seguente pezzetto di script AWK.

awk '($2=="1957") { print $3 }' |

Ciò equivale ad una qualsiasi delle righe seguenti.

perl -ne '@f=split; if ($f[1] eq "1957") { print "$f[2]\n"}' |
perl -ne 'if ((@f=split)[1] eq "1957") { print "$f[2]\n"}' |
perl -ne '@f=split; print $f[2] if ( $f[1]==1957 )' |
perl -lane 'print $F[2] if $F[1] eq "1957"' |
perl -lane 'print$F[2]if$F[1]eq+1957' |

L'ultima è una sorta di indovinello; sfrutta le seguenti caratteristiche di Perl.

  • Gli spazi bianchi sono opzionali.

  • Esiste una conversione automatica da numero a stringa.

Per le opzioni per la riga di comando vedere perlrun(1). Per altri script Perl pazzi può essere interessante guardare Perl Golf.

Pagine web dinamiche interattive di base possono essere create nel modo seguente.

  • Le interrogazioni vengono presentate all'utente del browser usando moduli HTML.

  • La compilazione e il cliccare sulle voci nel modulo invia una delle stringhe URL seguenti con i parametri codificati dal browser al web server.

    • "http://www.foo.dom/cgi-bin/program.pl?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

    • "http://www.foo.dom/cgi-bin/program.py?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

    • "http://www.foo.dom/program.php?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

  • "%nn" nell'URL viene sostituito dal carattere con valore esadecimale nn.

  • Viene impostata la variabile d'ambiente: "QUERY_STRING="VAR1=VAL1 VAR2=VAL2 VAR3=VAL3"".

  • Il programma CGI (uno qualsiasi dei "program.*") sul server web è eseguito con la variabile d'ambiente "$QUERY_STRING".

  • Lo stdout del programma CGI viene inviato al browser web ed è presentato come pagina web dinamica interattiva.

Per ragioni di sicurezza è bene non creare a mano nuovi metodi per analizzare i parametri CGI. Per loro esistono moduli Perl e Python comprovati. PHP è fornito con queste funzionalità. Quando è necessaria l'archiviazione dei dati client vengono usati i cookie HTTP. Quando è necessaria l'elaborazione dei dati lato client, viene spesso usato Javascript.

Per maggiori informazioni vedere CGI (Common Gateway Interface), Apache Software Foundation e JavaScript.

Cercare "CGI tutorial" su Google digitando l'URL codificato http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial direttamente nell'indirizzo del browser è un buon modo per vedere lo script CGI in azione sul server di Google.

Esistono programmi per convertire codice sorgente.


Se si desidera creare un pacchetto Debian, leggere i documenti seguenti.

Ci sono pacchetti come debmake, dh-make, dh-make-perl, ecc., che aiutano nella creazione dei pacchetti.