Capítulo 12. Programación

Tabla de contenidos

12.1. Los archivos de órdenes
12.1.1. Compatibilidad del intérprete de órdenes POSIX
12.1.2. Parámetros del intérprete de órdenes
12.1.3. Condiciones del intérprete de órdenes
12.1.4. Bucles del intérprete de órdenes
12.1.5. Variables del entorno de shell
12.1.6. La secuencia de procesamiento de la línea de órdenes
12.1.7. Programas útiles para los archivos de órdenes
12.2. Programación en lenguajes interpretados
12.2.1. Depuración de los códigos del lenguaje interpretado
12.2.2. Programa GUI con el script de shell
12.2.3. Acciones personalizadas para el archivador GUI
12.2.4. Locura de pequeños archivos de órdenes en Perl
12.3. Codificación en lenguajes compilados
12.3.1. C
12.3.2. Programa sencillo en C (gcc)
12.3.3. Flex — una mejora de Lex
12.3.4. Bison — una mejora de Yacc
12.4. Herramientas de análisis estático de memoria
12.5. Depuración
12.5.1. Fundamentos de gdb
12.5.2. Depurando un paquete Debian
12.5.3. Obteniendo trazas
12.5.4. Órdenes avanzadas de gdb
12.5.5. Comprobar las dependencias de las bibliotecas
12.5.6. Herramientas de rastreo dinámico de llamadas
12.5.7. Errores de depuración X
12.5.8. Herramientas de detección de fugas de memoria
12.5.9. Desemsamblado de binarios
12.6. Herramientas de construcción
12.6.1. Make
12.6.2. Autotools (Autoherramientas) (herramientas de automatización)
12.6.2.1. Compilando e instalando un programa
12.6.2.2. Desinstalando programas
12.6.3. Meson
12.7. Web
12.8. La traducción de código fuente
12.9. Haciendo un paquete Debian

Algunos consejos para quién quiera aprender a programar en el sistema Debian para trazar el código fuente. Aquí están los paquetes más importantes y los paquetes de documentación más importantes para la programación.

Las referencia en línea está disponible escribiendo «man nombre» tras instalar los paquetes manpages y manpages-dev. La referencia en línea para las herramientas GNU están disponibles escribiendo «info nombre_de_programa» después de instalar los paquetes correspondientes de documentación. Pude necesitar incluir los repositorios contrib y non-free además del repositorio main ya que una parte de la documentación GFDL no se considera que cumple con DFSG.

Por favor, considera la posibilidad de utilizar las herramientas del sistema de control de versiones. Ver Sección 10.5, “Git”.

[Aviso] Aviso

No use«test» como nombre de un archivo ejecutable. «test» es una orden interna del intérprete de órdenes.

[Atención] Atención

Puede instalar programas de software directametne compilado de la fuente en «/usr/local» o «/opt» para evitar la colisión con los programas del sistema.

[Sugerencia] Sugerencia

Los ejemplos de código para crear «La canción de 99 botellas de Cerveza« le aportará buenas ideas para prácticamente cualquier lenguaje de programación.

Un archivo de órdenes es un archivo de texto con el bit de ejecución activado y contiene órdenes con el formato siguiente.

#!/bin/sh
 ... command lines

La primera línea determina cuál es el intérprete de órdenes que se encarga de leer y ejecutar el contenido del archivo.

La lectura de archivos de órdenes es la mejor manera de entender como funciona un sistema tipo Unix. Aquí, doy algunos apuntes para la programación de archivos de órdenes. Consulte «Errores con el intérprete de órdenes« (https://www.greenend.org.uk/rjk/2001/04/shell.html) para aprender los errores más comunes.

A diferencia del uso interactivo del intérprete de órdenes (consulte Sección 1.5, “Órdenes simples para el intérprete de órdenes” y Sección 1.6, “Operaciones de texto al estilo de Unix”) en los archivos de órdenes se usan generalmente parámetros, condiciones y bucles.

Frecuentemente son utilizados por el intérprete de órdenes parámetros especiales


Lasexpansiones de parámetros fundamentales que debe recordar son las que se muestran.


Aquí, el símbolo «:» en todos estos operadores es realmente opcional.

  • con «:» el operador = comprueba que existe y no es null

  • sin «:» el operador = comprueba únicamente si existe


Cada comando devuelve un estado de salida que puede usarse para expresiones condicionales.

  • Éxito: 0 («Verdadero«)

  • Error: no 0 («Falso«)

[Nota] Nota

En el contexto del intérprete de órdenes «0« es equivalente a «verdadero«, mientras que en contexto de una condición en C «0« significa «falso«.

[Nota] Nota

«[» es el equivalente a la orden test, la cual determina la expresión condicional de sus parámetros hasta«]».

Algunas expresiones condicionales que es importante recordar son las que se muestran.

  • «orden && si_éxito_ejecuta_esta_orden_además || true»

  • «orden || si_no_tiene_éxito_ejecuta_esta_orden_además || true»

  • Un pequeño archivo de órdenes de varias líneas como se muestra

if [ conditional_expression ]; then
 if_success_run_this_command
else
 if_not_success_run_this_command
fi

Aquí se añade «|| true» para asegurarnos de que el archivo de órdenes no finaliza en esta línea si el intérprete de órdenes se llama con la bandera «-e».



Los operadores aritméticos de comparación de enteros en la expresión original son «-eq», «-ne», «-lt», «-le», «-gt» y «-ge».

El shell procesa un script aproximadamente como la siguiente secuencia.

  • el intérprete de órdenes lee la línea

  • El intérprete de órdenes agrupa como un único elemento la parte de la línea incluida entre «…« o '…'.

  • el intérprete de órdenes divide el resto de la línea en elementos como se muestra.

    • Espacios en blanco: space tab newline

    • Metacaracteres: < > | ; & ( )

  • El intérprete de órdenes comprueba si cada elemento es una palabra reservada para adaptar su comportamiento si no esta incluida entre «…« o '…'.

    • palabras reservadas: si entonces elif else fi para in while a menos que se haga caso esac

  • el intérprete de órdenes expande los alias si no están incluidos entre «…« o '…'

  • El intérprete de órdenes expande las tilde si no están incluidas entre «…« o '…'.

    • «~» → el directorio home del usuario actual

    • «~user» → el directorio home de usuario

  • el intérprete de órdenes expande los parámetros a sus valores si no están incluidos entre '…'

    • parámetro: «$PARAMETER» o «${PARAMETER}»

  • el intérprete de órdenes expande la sustitución de órdenes si no está incluida entre '…'

    • «$( comando )» → la salida de «comando»

    • «` command `» → la salida de «command»

  • el intérprete de órdenes expande las rutas de nombres que encajan con nombres de archivos si no están incluidas entre «…« o '…'

    • * → cualquier carácter

    • ? → un carácter

    • […] → cualquiera de los caracteres en «»

  • el intérprete de órdenes busca las órdenes como se muestra y los ejecuta

    • definición de lafunción

    • orden interna

    • archivo ejecutable en «$PATH»

  • el intérprete de órdenes va a la siguiente línea y repite este proceso de nuevo desde el inicio de la secuencia

Las comillas simples no tienen efecto dentro de comillas dobles.

Si ejecuta «set -x» en el intérprete de órdenes o lo llama con la opción «-x» hace que se impriman todas las órdenes ejecutadas. Esto puede ser muy útil para la depuración.


Cuando deseas automatizar una tarea en Debian, deberías programarla primero con un lenguaje interpretado. La línea guía para la elección del lenguaje interpretado es:

  • Utilizadash, si la tarea es sencilla y combina programas CLI con un programa shell.

  • Utiliza python3, si la tarea no es sencilla y la estás escribiendo desde cero.

  • Usa perl, tcl, ruby, ... si hay un código existente usando uno de estos lenguajes en Debian que necesita ser retocado para hacer la tarea.

Si el código resultante es demasiado lento, puede reescribir sólo la parte crítica para la velocidad de ejecución en un lenguaje compilado y llamarlo desde el lenguaje interpretado.

El script de shell se puede mejorar para crear un programa GUI atractivo. El truco es usar uno de los llamados programas de diálogo en lugar de una interacción ligera usando los comandosecho y read.


Aquí hay un ejemplo del programa GUI para demostrar lo fácil que es sólo con un script de shell.

Este script usa zenity para seleccionar un archivo (predeterminado /etc/motd) y mostrarlo.

El lanzador GUI para este script se puede crear siguiendo Sección 9.4.10, “Iniciar un programa desde el interfaz gráfico de usuario”.

#!/bin/sh -e
# Copyright (C) 2021 Osamu Aoki <[email protected]>, 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

Este tipo de aproximación al programa GUI con el shell script es útil sólo para casos de elección simple. Si va a escribir cualquier programa con complejidades, por favor considera escribirlo en una plataforma más capaz.


Aquí, Sección 12.3.3, “Flex — una mejora de Lex” y Sección 12.3.4, “Bison — una mejora de Yacc” se incluyen para indicar cómo se puede escribir un programa similar a un compilador en lenguaje C compilando una descripción de nivel superior en lenguaje C.

Puede configurar su entorno para la compilación de programas escritos en el lenguaje de programación C como se muestra.

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

El paquete libc6-dev, a saber, la biblioteca GNU C, aporta la biblioteca estándar de C que es un conjunto de archivos de cabecera y rutinas de biblioteca utilizadas por el lenguaje de programación C.

Consulte las referencias siguientes sobre C.

  • «info libc» (referencia de las funciones de la biblioteca de C)

  • gcc(1) y «info gcc»

  • nombre_de_cada_función_de_la_biblioteca_de_C(3)

  • Kernighan & Ritchie, «The C Programming Language«, 2nd edición (Prentice Hall)

Flex es un generador rápido de analizadores léxicos compatible con Lex.

Puede encontrar un tutorial de flex(1) en «info flex».

Se pueden encontrar muchos ejemplos sencillos en "/usr/share/doc/flex/examples/". [7]

Los paquetes que proporcionan un mejor y compatible Yacc son el analizador sintáctico LR o el analizador sintáctico LALR de Debian.


Puede encontrar un tutorial de bison(1) en «info bison».

Puede que sea necesario que proporcione su propio «main()» y «yyerror()». El método «main()» invoca «yyparse()» el cual invoca «yylex()», que normalmente se crea con Flex.

He aquí un ejemplo para crear un sencillo programa de calculadora de terminal.

Vamos a crear example.y:

/* calculator source for bison */
%{
#include <stdio.h>
extern int yylex(void);
extern int yyerror(char *);
%}

/* declare tokens */
%token NUMBER
%token OP_ADD OP_SUB OP_MUL OP_RGT OP_LFT OP_EQU

%%
calc:
 | calc exp OP_EQU    { printf("Y: RESULT = %d\n", $2); }
 ;

exp: factor
 | exp OP_ADD factor  { $$ = $1 + $3; }
 | exp OP_SUB factor  { $$ = $1 - $3; }
 ;

factor: term
 | factor OP_MUL term { $$ = $1 * $3; }
 ;

term: NUMBER
 | OP_LFT exp OP_RGT  { $$ = $2; }
  ;
%%

int main(int argc, char **argv)
{
  yyparse();
}

int yyerror(char *s)
{
  fprintf(stderr, "error: '%s'\n", s);
}

Vamos a crear example.l:

/* calculator source for flex */
%{
#include "example.tab.h"
%}

%%
[0-9]+ { printf("L: NUMBER = %s\n", yytext); yylval = atoi(yytext); return NUMBER; }
"+"    { printf("L: OP_ADD\n"); return OP_ADD; }
"-"    { printf("L: OP_SUB\n"); return OP_SUB; }
"*"    { printf("L: OP_MUL\n"); return OP_MUL; }
"("    { printf("L: OP_LFT\n"); return OP_LFT; }
")"    { printf("L: OP_RGT\n"); return OP_RGT; }
"="    { printf("L: OP_EQU\n"); return OP_EQU; }
"exit" { printf("L: exit\n");   return YYEOF; } /* YYEOF = 0 */
.      { /* ignore all other */ }
%%

A continuación, ejecute lo siguiente desde el símbolo del sistema para probar esto:

$ bison -d example.y
$ flex example.l
$ gcc -lfl example.tab.c lex.yy.c -o example
$ ./example
1 + 2 * ( 3 + 1 ) =
L: NUMBER = 1
L: OP_ADD
L: NUMBER = 2
L: OP_MUL
L: OP_LFT
L: NUMBER = 3
L: OP_ADD
L: NUMBER = 1
L: OP_RGT
L: OP_EQU
Y: RESULT = 9

exit
L: exit

Lint herramientas similares pueden ayudar a análisis de código estático automático.

Indent como herramientas pueden ayudar a las revisiones humanas de código reformateando los códigos fuente de forma coherente.

Ctags como herramientas pueden ayudar a las revisiones humanas de código mediante la generación de un archivo de índice (o etiqueta) de los nombres que se encuentran en los códigos fuente.

[Sugerencia] Sugerencia

Configurar tu editor favorito (emacs o vim) para usar plugins asíncronos del motor lint ayuda a tu escritura de código. Estos plugins se están volviendo muy potentes aprovechando Protocolo de servidor de idiomas. Dado que se están moviendo rápidamente, usar su código upstream en lugar del paquete Debian puede ser una buena opción.

Tabla 12.12. Relación de las herramientas para el análisis de código estático

paquete popularidad tamaño descripción
vim-ale I:0 2591 Motor asíncrono Lint para Vim 8 y NeoVim
vim-syntastic I:3 1379 Trucos de comprobación sintáctica para vim
elpa-flycheck V:0, I:1 808 moderna comprobación sintáctica sobre la marcha para Emacs
elpa-relint V:0, I:0 147 Buscador de errores regexp de Emacs Lisp
cppcheck-gui V:0, I:1 7224 herramienta para el análisis de código C/C++ (GUI)
shellcheck V:2, I:13 18987 herramienta útil para scripts de shell
pyflakes3 V:2, I:15 20 Verificado pasivo de programas Python 3
pylint V:4, I:20 2018 comprobador de código estático Python
perl V:707, I:989 673 intérprete con comprobador de código estático interno: B::Lint(3perl)
rubocop V:0, I:0 3247 Analizador de código estático Ruby
clang-tidy V:2, I:11 21 Herramienta de verificación de formato de regla C++ basada en Clang
splint V:0, I:2 2320 herramienta para la comprobación estática de errores de programación en C
flawfinder V:0, I:0 205 herramienta que examina código fuente en C/C++ para encontrar debilidades de seguridad
black V:3, I:13 660 Formateador de código Python agresivo
perltidy V:0, I:4 2493 Indentador y reformateador de scripts de Perl
indent V:0, I:7 431 Programa de formateo del código fuente en lenguaje C
astyle V:0, I:2 785 Indentador de código fuente para C, C++, Objective-C, C# y Java
bcpp V:0, I:0 111 Embellecer C(++)
xmlindent V:0, I:1 53 reformateador del flujo XML
global V:0, I:2 1908 Herramientas de búsqueda y exploración de código fuente
exuberant-ctags V:2, I:20 341 Crea un índice de archivo de etiquetas de definiciones del código fuente
universal-ctags V:1, I:11 3386 Crea un índice de archivo de etiquetas de definiciones del código fuente

La depuración es una parte importante de las actividades de programación. Saber cómo depurar programas lo convierte en un buen usuario de Debian que puede producir informes de errores significativos.


El principal depurador en Debian es gdb(1) el cual permite inspeccionar un programa mientras se ejecuta.

Instalamos gdb y otros programas relevantes como se muestra.

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

Se puede encontrar un buen tutorial de gdb:

  • info gdb

  • “Depuración con GDB” en /usr/share/doc/gdb-doc/html/gdb/index.html

  • tutorial en la web

Aquí hay un ejemplo simple del uso de gdb(1) en un "programa" compilado con la opción "-g" para generar información de depuración.

$ 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
[Sugerencia] Sugerencia

Existen abreviaturas para la mayor parte de las órdenes de gdb(1). La expansión del tabulador funciona de la misma manera que en el intérprete de órdenes.

Dado que todos los archivos binarios instalados deben eliminarse en el sistema Debian de forma predeterminada, la mayoría de los símbolos de depuración se eliminan en el paquete normal. Para depurar paquetes Debian con gdb(1), se deben instalar paquetes *-dbgsym (p. ej., coreutils-dbgsym en el caso de coreutils). Los paquetes fuente generan automáticamente paquetes *-dbgsym junto con paquetes binarios normales y esos paquetes de depuración se colocan por separado en el archivo debian-debug. Consulte los artículos en Debian Wiki para obtener más información.

Si un paquete que se va a depurar no proporciona su paquete *-dbgsym, debe instalarlo después de reconstruirlo de la siguiente manera.

$ 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 ./

Si lo necesita corrija los errores.

Cuando recompile la publicación de un paquete ya existente elija una que no exista en Debian, p. ej. añadiéndole «+debug1» o añadiéndole «~pre1» como se muestra.

$ dch -i

Compila o instala los paquetes con los símbolos de depuración de la siguiente manera.

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

Necesita comprobar que los archivos de órdenes del paquete y utilizar «CFLAGS=-g -Wall» para la compilación de binarios.

Cuando encuentre un programas que no funciona, es una buena idea al informar del error añadir información sobre las trazas de su ejecución.

El backtrace se puede obtener mediante gdb(1) usando uno de los siguientes enfoques:

Para un bucle infinito o una situación de teclado congelado, puede forzar el bloqueo del programa pulsando Ctrl-\ o Ctrl-C o ejecutando “kill -ABRT PID”. (Ver Sección 9.4.12, “Matando un proceso”)

[Sugerencia] Sugerencia

Frecuentemente encontrará en primeras líneas «malloc()» o «g_malloc()». Cuando esto ocurre disminuyen las posibilidades de que las trazas sean útiles. La forma más fácil de encontrar alguna información útil es asignado a la variable de entorno «$MALLOC_CHECK_» el valor de 2 (malloc(3)). Puede hacer esto a la vez que se ejecuta gdb como se muestra.

 $ MALLOC_CHECK_=2 gdb hello

Make es una utilidad para mantener grupos de programas. La ejecución de make(1) consiste en, la lectura del archivo de reglas «Makefile» por parte de make y la actualización de un objetivo si los archivos que son necesarios han sido modificados desde la última vez o si el objetivo no existe. La ejecución de estas actualizaciones pueden suceder de concurrentemente.

La sintaxis del archivo de reglas es la que se muestra.

target: [ prerequisites ... ]
 [TAB]  command1
 [TAB]  -command2 # ignore errors
 [TAB]  @command3 # suppress echoing

Aquí «[TAB]» es un código TAB. Cada línea es interpretada por el intérprete de órdenes después de la sustitución de las variables. Utilice «\» al final de la línea para continuar el archivo de órdenes. Utilice «$$» para incluir «$» par los valores del entorno para el archivo de órdenes.

Las reglas implícitas y los prerequisitos para un objetivos pueden ser escrito, por ejemplo, como se muestra.

%.o: %.c header.h

Aquí, el objetivo contiene el carácter «%» (únicamente un carácter). El carácter %» encaja con cualquier cadena no vacía que corresponda a los nombres de archivo del objetivo real. Así mismo el prerrequisito utiliza «%» para mostrar como se relaciones sus nombres con los nombres del objetivo real.



Ejecute «make -p -f/dev/null»para ver las reglas internas automática.

Autotools es un conjunto de herramientas de programación diseñadas para ayudar a que los paquetes de código fuente sean portables a muchos sistemas Unix-like.

  • Autoconf es una herramienta para generar scripts de shell "configure" desde "configure.ac".

    • "configure" se usa más adelante para producir "Makefile" a partir de la plantilla "Makefile.in".

  • Automake es una herramienta para producir "Makefile.in" a partir de "Makefile.am".

  • Libtool es un script de shell para abordar el problema de la portabilidad del software al compilar bibliotecas compartidas a partir del código fuente.

El sistema de creación del software ha ido evolucionando:

  • Autotools en la parte superior de Make ha sido el estándar de facto para la infraestructura de compilación portátil desde 1990. Esto es extremadamente lento.

  • CMake lanzado inicialmente en el 2000 mejoró la velocidad significativamente pero fue construido originalmente sobre el inherentemente lento Make. (Ahora Ninja puede ser tu backend).

  • Ninja, lanzado inicialmente en 2012, está destinado a sustituir a Make para mejorar aún más la velocidad de la compilación y está diseñado para que tus archivos sean generados por un sistema de compilación de nivel superior.

  • Meson lanzado inicialmente en 2013 es el nuevo, popular y rápido sistema de compilación de nivel superior que utiliza Ninja como backend.

Consulta los documentos que se encuentran en "El sistema de construcción Meson" y "El sistema de construcción Ninja".

Se pueden crear páginas web dinámicas básicas como se muestra.

  • Las consultas se presentan al navegador del usuario utilizando formularios HTML.

  • Rellenando y pulsado sobre las entradas del formulario se envía la cadena URL con los parámetros codificados desde el navegador al servidor web.

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

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

    • «https://www.foo.dom/program.php?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3»

  • «%nn» en la URL se sustituye por el carácter hexadecimal que tiene el valor nn.

  • Las variables de entorno se asignan como: «QUERY_STRING=«VAR1=VAL1 VAR2=VAL2 VAR3=VAL3«».

  • Un programa CGI (independientemente de su extensión «programa.*») en un servidor web se ejecuta a si mimo con la variable de entorno «$QUERY_STRING».

  • La salida de un programa CGI se envía al servidor web y se representa como una página web dinámica.

Por seguridad es mejor no realizar de forma manual o de otras formas la gestión de análisis de los parámetros CGI. Existen módulos para ello en Perl y Python. PHP tiene dicha funcionalidad. Cuando se necesita almacenar información del usuario, se utilizan las cookies HTTP cookies. Cuando se necesita procesar información en el lado del cliente, normalmente se utiliza Javascript.

Para mayor información, ver Interfaz de Pasarela Común (Common Gateway Interface), La Fundación de Software Apache (The Apache Software Foundation) y JavaScript.

Buscar "tutorial CGI" en Google escribiendo la URL codificada https://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial directamente en la dirección del navegador es una buena forma de ver el script CGI en acción en el servidor de Google.

Existen aplicaciones para convertir código fuente de un lenguaje a otro.


Si quiere hacer un paquete Debian, lea lo siguiente.

Existen paquetes que ayudan al empaquetado como debmake, dh-make, dh-make-perl, etc.



[7] Es posible que se necesiten algunos ajustes para que funcionen con el sistema actual.