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. Shell environment variables
12.1.6. La sequenza di elaborazione della riga di comando di shell
12.1.7. Programmi di utilità per script di shell
12.2. Scripting in interpreted languages
12.2.1. Debugging interpreted language codes
12.2.2. GUI program with the shell script
12.2.3. Pazzie con corti script Perl
12.3. Coding in compiled languages
12.3.1. C
12.3.2. Semplice programma in C (gcc)
12.3.3. Flex - un Lex migliorato
12.3.4. Bison - Yacc migliorato
12.4. Strumenti di analisi statica del codice
12.5. Debug
12.5.1. Esecuzione base di gdb
12.5.2. Fare il debug di pacchetti Debian
12.5.3. Ottenere un backtrace
12.5.4. Comandi gdb avanzati
12.5.5. Controllare le dipendenze dalle librerie
12.5.6. Dynamic call tracing tools
12.5.7. Fare il debug di errori X
12.5.8. Strumenti per rilevazione di memory leak
12.5.9. Disassemblatore di binari
12.6. Build tools
12.6.1. Make
12.6.2. Autotools
12.6.2.1. Compilare ed installare un programma
12.6.2.2. Disinstallare un programma
12.6.3. Meson
12.7. Web
12.8. Traduzione di codice sorgente
12.9. 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.

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.

Please consider to use version control system tools. See Sezione 10.5, «Git».

[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
 ... command lines

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 [ conditional_expression ]; then
 if_success_run_this_command
else
 if_not_success_run_this_command
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>

    • Metacharacters: | ; & ( )

  • 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.


When you wish to automate a task on Debian, you should script it with an interpreted language first. The guide line for the choice of the interpreted language is:

  • Use dash, if the task is a simple one which combines CLI programs with a shell program.

  • Use python3, if the task isn't a simple one and you are writing it from scratch.

  • Use perl, tcl, ruby, ... if there is an existing code using one of these languages on Debian which needs to be touched up to do the task.

If the resulting code is too slow, you can rewrite only the critical portion for the execution speed in a compiled language and call it from the interpreted language.

The shell script can be improved to create an attractive GUI program. The trick is to use one of so-called dialog programs instead of dull interaction using echo and read commands.


Here is an example of GUI program to demonstrate how easy it is just with a shell script.

This script uses zenity to select a file (default /etc/motd) and display it.

GUI launcher for this script can be created following Sezione 9.4.10, «Avviare un programma dalla GUI».

#!/bin/sh -e
# Copyright (C) 2021 Osamu Aoki <osamu@debian.org>, Public Domain
# vim:set sw=2 sts=2 et:
DATA_FILE=$(zenity --file-selection --filename="/etc/motd" --title="Select a file to check") || \
  ( echo "E: File selection error" >&2 ; exit 1 )
# Check size of archive
if ( file -ib "$DATA_FILE" | grep -qe '^text/' ) ; then
  zenity --info --title="Check file: $DATA_FILE" --width 640  --height 400 \
    --text="$(head -n 20 "$DATA_FILE")"
else
  zenity --info --title="Check file: $DATA_FILE" --width 640  --height 400 \
    --text="The data is MIME=$(file -ib "$DATA_FILE")"
fi

This kind of approach to GUI program with the shell script is useful only for simple choice cases. If you are to write any program with complexities, please consider writing it on more capable platform.


Here, Sezione 12.3.3, «Flex - un Lex migliorato» and Sezione 12.3.4, «Bison - Yacc migliorato» are included to indicate how compiler-like program can be written in C language by compiling higher level description into C language.

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)

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".

Lint like tools can help automatic static code analysis.

Indent like tools can help human code reviews by reformatting source codes consistently.

Ctags like tools can help human code reviews by generating an index (or tag) file of names found in source codes.

[Suggerimento] Suggerimento

Configuring your favorite editor (emacs or vim) to use asynchronous lint engine plugins helps your code writing. These plugins are getting very powerful by taking advantage of Language Server Protocol. Since they are moving fast, using their upstream code instead of Debian package may be a good option.


Debug is important part of programming activities. Knowing how to debug programs makes you a good Debian user who can produce meaningful bug reports.


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

Good tutorial of gdb can be found:

  • info gdb

  • “Debugging with GDB” in /usr/share/doc/gdb-doc/html/gdb/index.html

  • tutorial on the web

Here is a simple example of using gdb(1) on a "program" compiled with the "-g" option to produce debugging information.

$ gdb program
(gdb) b 1                # set break point at line 1
(gdb) run args           # run program with args
(gdb) next               # next line
...
(gdb) step               # step forward
...
(gdb) p parm             # print parm
...
(gdb) p parm=12          # set value to 12
...
(gdb) quit
[Suggerimento] Suggerimento

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

Since all installed binaries should be stripped on the Debian system by default, most debugging symbols are removed in the normal package. In order to debug Debian packages with gdb(1), *-dbgsym packages need to be installed (e.g. coreutils-dbgsym in the case of coreutils). The source packages generate *-dbgsym packages automatically along with normal binary packages and those debug packages are placed separately in debian-debug archive. Please refer to articles on Debian Wiki for more information.

If a package to be debugged does not provide its *-dbgsym package, you need to install it after rebuilding it by the following.

$ mkdir /path/new ; cd /path/new
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo apt-get install fakeroot devscripts build-essential
$ apt-get source package_name
$ cd package_name*
$ 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 package_name*.changes

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

Quando un programma va in crash, è una buona idea inviare un segnalazione di bug riportando le informazioni di backtrace.

The backtrace can be obtained by gdb(1) using one of the following approaches:

For infinite loop or frozen keyboard situation, you can force to crash the program by pressing Ctrl-\ or Ctrl-C or executing “kill -ABRT PID”. (See Sezione 9.4.12, «Uccidere un processo»)

[Suggerimento] Suggerimento

Spesso si vede un backtrace in cui una o più delle prime righe sono in "malloc()" o "g_malloc()". Quando ciò accade è probabile che il backtrace non sia molto utile. Il metodo più semplice per trovare informazioni utili è di impostare la variabile d'ambiente "$MALLOC_CHECK_" al valore 2 (malloc(3)). Lo si può fare mentre si esegue gdb nel modo seguente.

 $ MALLOC_CHECK_=2 gdb hello

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: [ prerequisites ... ]
 [TAB]  command1
 [TAB]  -command2 # ignore errors
 [TAB]  @command3 # suppress echoing

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.

Autotools is a suite of programming tools designed to assist in making source code packages portable to many Unix-like systems.

  • Autoconf is a tool to produce a shell script "configure" from "configure.ac".

    • "configure" is used later to produce "Makefile" from "Makefile.in" template.

  • Automake is a tool to produce "Makefile.in" from "Makefile.am".

  • Libtool is a shell script to address the software portability problem when compiling shared libraries from source code.

The software build system has been evolving:

  • Autotools on the top of Make has been the de facto standard for the portable build infrastructure since 1990s. This is extremely slow.

  • CMake initially released in 2000 improved speed significantly but was still build on the top of inherently slow Make.

  • Ninja initially released in 2012 is meant to replace Make for the further improved build speed but is also designed to have its input files generated by a higher-level build system.

  • Meson initially released in 2013 is the new popular and fast higher-level build system which uses Ninja as its backend.

See documents found at "The Meson Build system" and "The Ninja build system".

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.