viernes, 30 de diciembre de 2016

Nombres largos de opciones

Muchos de los comandos que hemos estudiado cuentan tanto con nombres cortos como largos para las opciones. Por ejemplo, el comando ls tiene muchas opciones que pueden expresarse tanto en forma corta como larga. Por ejemplo:

[me@linuxbox ~]$ ls -ad

y:

[me@linuxbox ~]$ ls --all --directory

son comandos equivalentes. En interés de reducir la escritura, preferimos las opciones cortas cuando incluimos opciones en la línea de comandos, pero cuando escribimos scripts, las opciones largas pueden proporcionar una legibilidad mejor.

miércoles, 28 de diciembre de 2016

Más trucos de formateado

Uno de los objetivos clave de una escritura seria de scripts es que sean fáciles de mantener; es decir, la facilidad con la que un script puede ser modificado por su autor u otros para adaptarlo a las necesidades cambiantes. Hacer que un script sea fácil de leer y comprender es una forma de facilitar un mantenimiento sencillo.

lunes, 26 de diciembre de 2016

Buenas localizaciones para los scripts

El directorio ~/bin es un buen lugar para colocar scripts destinados a uso personal. Si escribimos un script que todo el mundo en el sistema está autorizado a usar, la localización tradicional es /usr/local/bin. Los scripts destinados al uso del administrador del sistema se localizan a menudo en /usr/local/sbin. En la mayoría de los casos, el software proporcionado localmente, ya sean scripts o programas compilados, debería localizarse en la jerarquía de /usr/local y no en /bin o /usr/bin. Estos directorios son especificados por el Estándar de Jerarquía del Sistema de Archivos de Linux para contener sólo archivos proporcionados y mantenidos por el distribuidor de Linux.

viernes, 23 de diciembre de 2016

Localización del archivo de script

Con los permisos establecidos, ahora podemos ejecutar nuestro script:

[me@linuxbox ~]$ ./hello_world
Hello World!

Para que el script funcione, debemos preceder el nombre del script con una ruta específica. Si no lo hacemos, obtenemos esto:

[me@linuxbox ~]$ hello_world
bash: hello_world: command not found

¿Qué es esto? ¿Qué hace que nuestro script sea diferente de otros programas? Al parecer, nada. Nuestro script está perfecto. El problema es su localización. En el capítulo 11, vimos la variable de entorno PATH y su efecto en cómo busca el sistema los programas ejecutables. Para abreviar, el sistema busca en una lista de directorios cada vez que necesita encontrar un programa ejecutable, si no se especifica una ruta concreta. Así es como el sistema sabe que tiene que ejecutar /bin/ls cuando escribimos ls en la línea de comandos. El directorio /bin es uno de los directorios donde el sistema busca automáticamente. La lista de directorios está contenida en una variable de entorno llamada PATH. La variable PATH contiene una lista de directorios separados por dos puntos donde buscar. Podemos ver el contenido de PATH:

[me@linuxbox ~]$ echo $PATH
/home/me/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

Aquí vemos nuestra lista de directorios. Si nuestro script estuviera localizado en cualquiera de los directorios de la lista, nuestro problema estaría solucionado. Fíjate en el primer directorio de la lista, /home/me/bin. La mayoría de las distribuciones Linux configuran la variable PATH para contener un directorio bin en el directorio home del usuario, para permitir a los usuarios ejecutar sus propios programas. Así que si creamos el directorio bin y colocamos nuestro script en él, debería comenzar a funcionar como otros programas:

[me@linuxbox ~]$ mkdir bin
[me@linuxbox ~]$ mv hello_world bin
[me@linuxbox ~]$ hello_world
Hello World!

Y así lo hace.

Si la variable PATH no contiene el directorio, podemos añadirlo fácilmente incluyendo esta línea en nuestro archivo .bashrc:

export PATH=~/bin:"$PATH"

Después de hacer este cambio, tendrá efecto en cada nueva sesión de terminal. Para aplicar el cambio a la sesión de terminal actual, tenemos que hacer que el shell relea el archivo .bashrc. Esto puede hacerse mediante el comando source:

[me@linuxbox ~]$ . .bashrc

El comando punto ( . ) es un sinónimo del comando source, un shell builtin que lee un archivo específico de comandos de shell y lo trata como entrada del teclado.

Nota: Ubuntu añade automáticamente el directorio ~/bin a la variable PATH si el directorio ~/bin existe cuando el archivo .bashrc del usuario se ejecute. Así que, en sistemas Ubuntu, si creamos el directorio ~/bin y luego salimos y volvemos a entrar, todo funciona.

miércoles, 21 de diciembre de 2016

Permisos de ejecución

Lo siguiente que tenemos que hacer es hacer nuestro script ejecutable. Esto se hace fácilmente usando chmod:

[me@linuxbox ~]$ ls -l hello_world
-rw-r--r-- 1 me me 63 2009-03-07 10:10 hello_world
[me@linuxbox ~]$ chmod 755 hello_world
[me@linuxbox ~]$ ls -l hello_world
-rwxr-xr-x 1 me me 63 2009-03-07 10:10 hello_world

Hay dos configuraciones comunes de permisos para los scripts; 755 para scripts que puede ejecutar todo el mundo, y 700 para scripts que sólo puede ejecutar el propietario. Fíjate que los scripts deben ser legibles para ser ejecutados.

lunes, 19 de diciembre de 2016

Formato del archivo de script

Siguiendo la tradición de la programación, crearemos un programa "hola mundo" para demostrar un script tremendamente simple. Así que arranquemos nuestros editores de texto e introduzcamos el siguiente script:

#!/bin/bash
# This is our first script.
echo 'Hello World!'

La última línea de nuestro script es muy familiar, sólo un comando echo con un argumento de cadena. La segunda línea también es familiar. Parece un comentario que hemos visto usado en muchos de los archivos de configuración que hemos examinado y editado. Una cosa sobre los comentarios en scripts de shell es que también pueden aparecer al final de las líneas, así:

echo 'Hello World!' # This is a comment too

Todo lo que hay desde el símbolo # en adelante en la línea es ignorado.

Como muchas cosas, esto también funciona en la línea de comandos:

[me@linuxbox ~]$ echo 'Hello World!' # This is a comment too
Hello World!

Aunque los comentarios son poco útiles en la línea de comandos, también funcionan.

La primera línea de nuestro script es un poco misteriosa. Parece como si fuera un comentario, ya que comienza con #, pero parece demasiado significativa para ser sólo eso. La secuencia de caracteres #! es, de hecho, una construcción especial llamada shebang. El shebang se usa para decirle al sistema el nombre del intérprete que debería usarse para interpretar el script que sigue. Cada script de shell debería incluirlo en su primera línea.

Guardemos nuestro archivo de script como hello_world.

viernes, 16 de diciembre de 2016

Cómo escribir un script de shell

Para crear y ejecutar con éxito un script de shell, necesitamos hacer tres cosas:
  1. Escribir un script. Los scripts de shell son comunes archivos de texto. Así que necesitamos un editor de texto para escribirlos. Los mejores editores de texto proporcionarán destacado sintáctico, permitiéndonos ver una vista codificada por colores de los elementos del script. El destacado sintáctico nos ayudará a encontrar algunos tipos de errores comunes. vim, gedit, kate y muchos otros editores son buenos candidatos para escribir scripts.
  2. Hacer el script ejecutable. El sistema es un poco quisquilloso en no dejar que ningún archivo de texto antiguo sea tratado como un programa, ¡y es por una buena razón! Necesitamos configurar los permisos del archivo del script para que se permita la ejecución.
  3. Poner el script en algún lugar donde el shell pueda encontrarlo. El shell busca automáticamente archivos ejecutables en ciertos directorios cuando no se especifica una ruta concreta. Para mayor comodidad, colocaremos nuestros scripts en dichos directorios.

miércoles, 14 de diciembre de 2016

¿Qué son scripts de shell?

En términos sencillos, un script de shell es un archivo que contiene una serie de comandos. El shell lee este archivo y ejecuta los comandos como si se hubieran introducido directamente en la línea de comandos.

El shell es algo único, en cuanto a que es una interfaz poderosa de línea de comandos para el sistema y un intérprete de lenguaje de script. Como veremos, la mayoría de las cosas que pueden hacerse con la línea de comandos puede hacerse con scripts, y la mayoría de las cosas que pueden hacerse con scripts pueden hacerse en la línea de comandos.

Hemos visto muchas características del shell, pero nos hemos enfocado en aquellas más usadas directamente en la línea de comandos. El shell también proporciona una serie de características que normalmente (aunque no siempre) se usan cuando escribimos programas.

lunes, 12 de diciembre de 2016

Escribiendo tu primer script

En los capítulos anteriores, hemos reunido un arsenal de herramientas de línea de comandos. Aunque estas herramientas pueden resolver muchos tipos de problemas de computación, todavía estamos limitados a usarlas manualmente una a una en la línea de comandos. ¿No sería genial si pudiéramos usar el shell para hacer la mayoría del trabajo? Podemos. Poniendo nuestras herramientas juntas en programas diseñados por nosotros, el shell puede ejecutar secuencias completas de tareas por sí mismo. Podemos habilitarlo para hacerlo escribiendo scripts de shell.

miércoles, 7 de diciembre de 2016

Resumiendo

En este capítulo, hemos visto cómo tres simples comandos:

./configure
make
make install

pueden usarse para construir muchos paquetes de código fuente. También hemos visto el importante rol que make juega en el mantenimiento de programas. El programa make puede usarse para cualquier tarea que necesite mantener una relación objetivo/dependencia, no sólo para compilar código fuente.

lunes, 5 de diciembre de 2016

Instalando el programa

El código fuente bien empaquetado a menudo incluirá un objetivo especial de make llamado install. Este objetivo instalará el producto final en un directorio del sistema para su uso. Generalmente, este directorio es /usr/local/bin, la localización tradicional para el software construido localmente. Sin embargo, este directorio normalmente no es modificable por los usuarios normales, así que tenemos que ser superusuario para realizar la instalación:

[me@linuxbox diction-1.11]$ sudo make install

Después de realizar la instalación, podemos comprobar que el programa está listo:

[me@linuxbox diction-1.11]$ which diction
/usr/local/bin/diction
[me@linuxbox diction-1.11]$ man diction

¡Y ahí lo tenemos!

viernes, 2 de diciembre de 2016

Construyendo el programa

La mayoría de los programas se construyen con una secuencia simple de dos comandos:

./configure
make

El programa configure es un script de shell que es proporcionado por el árbol de código fuente. Su trabajo es analizar el entorno de construcción. La mayoría del código fuente se diseña para ser portable. Es decir, se diseña para construirse en más de un tipo de sistema tipo Unix. Pero para hacer eso, el código fuente puede necesitar someterse a leves ajustes durante la construcción para adaptarse a las diferencias entre sistemas. configure también comprueba que se instalen las herramientas externas y los componentes necesarios. Ejecutemos configure. Como configure no está localizado donde el shell espera normalmente que estén almacenados los programas, tenemos que decirle al shell explícitamente su localización precediendo el comando con ./ para indicar que el programa se localiza en el directorio de trabajo actual:

[me@linuxbox diction-1.11]$ ./configure

configure producirá un montón de mensajes a medida que prueba y configura la construcción. Cuando termina, tendrá un aspecto como este:

checking libintl.h presence... yes
checking for libintl.h... yes
checking for library containing gettext... none required
configure: creating ./config.status
config.status: creating Makefile
config.status: creating diction.1
config.status: creating diction.texi
config.status: creating diction.spec
config.status: creating style.1
config.status: creating test/rundiction
config.status: creating config.h
[me@linuxbox diction-1.11]$

Lo importante aquí es que no hay mensajes de error. Si los hubo, la configuración falló, y el programa no se construirá hasta que se corrijan los errores.

Vemos que configure ha creado varios archivos nuevos en nuestro directorio fuente. El más importante es Makefile. Makefile es un archivo de configuración que indica al programa make cómo construir exactamente el programa. Sin él, make no funcionará. Makefile es un archivo de texto ordinario, así que podemos verlo:

[me@linuxbox diction-1.11]$ less Makefile

El programa make toma como entrada un makefile (que normalmente se llama Makefile), que describe las relaciones y dependencias entre los componentes que componen el programa finalizado.

La primera parte de makefile define variables que son sustituidas en secciones posteriores del makefile. Por ejemplo vemos la línea:

CC=           gcc

que define que el compilador C será gcc. Más adelante en el makefile, vemos una instancia donde se usa:

diction: diction.o sentence.o misc.o getopt.o getopt1.o $(CC) -o $@ $(LDFLAGS) diction.o sentence.o misc.o \ getopt.o getopt1.o $(LIBS)

Aquí se realiza una sustitución, y el valor $(CC) se reemplaza por gcc en el momento de la ejecución.

La mayoría del makefile consiste en líneas, que definen un objetivo, en este caso el archivo ejecutable diction, y los archivos de los que depende. Las líneas restantes describen el/los comando/s necesarios para crear el objetivo desde sus componentes. Vemos en este ejemplo que el archivo ejecutable diction (uno de los productos finales) depende de la existencia de diction.o, sentence.o, misc.o, getop.o y gestopt1.o. Más adelante aún, en el makefile, vemos las definiciones de cada uno de estos objetivos:

diction.o:  diction.c config.h getopt.h misc.h sentence.h
getopt.o:   getopt.c getopt.h getopt_int.h
getopt1.o:  getopt1.c getopt.h getopt_int.h
misc.o:     misc.c config.h misc.h
sentence.o: sentence.c config.h misc.h sentence.h
style.o:    style.c config.h getopt.h misc.h sentence.h

Sin embargo, no vemos ningún comando especificado para ellos. Esto es gestionado por un objetivo general, anteriormente en el archivo, que describe el comando usado para compilar cualquier archivo .c en un archivo .o:

.c.o:
            $(CC) -c $(CPPFLAGS) $(CFLAGS) $<

Todo esto parece muy complicado. ¿Por qué no listamos simplemente todos los pasos para compilar las partes y terminamos? La respuesta a esto se aclarará en un momento. Mientras tanto, ejecutemos make y construyamos nuestros programas:

[me@linuxbox diction-1.11]$ make

El programa make se ejecutará, usando los contenidos de Makefile para guiar sus acciones. Producirá un montón de mensajes.

Cuando termine, veremos que todos los objetivos están presentes ahora en nuestro directorio:

[me@linuxbox diction-1.11]$ ls
config.guess  de.po           en           install-sh  sentence.c
config.h      diction         en_GB        Makefile    sentence.h
config.h.in   diction.1       en_GB.mo     Makefile.in sentence.o
config.log    diction.1.in    en_GB.po     misc.c      style
config.status diction.c       getopt1.c    misc.h      style.1
config.sub    diction.o       getopt1.o    misc.o      style.1.in
configure     diction.pot     getopt.c     NEWS        style.c
configure.in  diction.spec    getopt.h     nl          style.o
COPYING       diction.spec.in getopt_int.h nl.mo       test
de            diction.texi    getopt.o     nl.po
de.mo         diction.texi.in INSTALL      README

Entre los archivos, vemos diction y style, los programas que elegimos construir. ¡Tengo que darte la enhorabuena! ¡Acabamos de compilar nuestros primeros programas desde código fuente!

Pero sólo por curiosidad, ejecutemos make de nuevo:

[me@linuxbox diction-1.11]$ make
make: Nothing to be done for `all'.

Sólo produce un extraño mensaje. ¿Qué está pasando? ¿Por qué no ha construido el programa de nuevo? Ah, esta es la magia de make. En lugar de simplemente construirlo todo de nuevo, make sólo construye lo que necesita construirse. Con todos los objetivos presentes, make ha determinado que no hay nada que hacer. Podemos demostrar esto eliminando uno de los objetivos y ejecutando make de nuevo para ver qué hace. Deshagámonos de uno de los objetivos intermedios:

[me@linuxbox diction-1.11]$ rm getopt.o
[me@linuxbox diction-1.11]$ make

Vemos que make reconstruye y reenlaza los programas diction y style, ya que dependen del módulo perdido. Este comportamiento también indica otra característica importante de make: mantiene los objetivos actualizados. make insiste en que los objetivos sean más nuevos que sus dependencias. Esto tiene mucho sentido, como programador a menudo actualizarás algo de código fuente y luego usarás make para construir una nueva versión del producto finalizado. make se asegura que se construya todo lo que se necesita construir basándose en el código actualizado. Si usamos el programa touch para "actualizar" uno de los archivos de código fuente, podemos ver lo que ocurre:

[me@linuxbox diction-1.11]$ ls -l diction getopt.c
-rwxr-xr-x 1 me me 37164 2009-03-05 06:14 diction
-rw-r--r-- 1 me me 33125 2007-03-30 17:45 getopt.c
[me@linuxbox diction-1.11]$ touch getopt.c
[me@linuxbox diction-1.11]$ ls -l diction getopt.c
-rwxr-xr-x 1 me me 37164 2009-03-05 06:14 diction
-rw-r--r-- 1 me me 33125 2009-03-05 06:23 getopt.c
[me@linuxbox diction-1.11]$ make

Después de que make se ejecute, vemos que ha restaurado el objetivo para que sea más nuevo que la dependencia:

[me@linuxbox diction-1.11]$ ls -l diction getopt.c
-rwxr-xr-x 1 me me 37164 2009-03-05 06:24 diction
-rw-r--r-- 1 me me 33125 2009-03-05 06:23 getopt.c

La capacidad de make de construir inteligentemente sólo lo que necesita ser construido es un gran beneficio para los programadores. Aunque el ahorro de tiempo no es muy evidente en nuestro pequeño proyecto, es muy significativo para proyectos más grandes. Recuerda, el kernel Linux (un programa sometido a modificaciones y mejoras constantes) contiene varios millones de líneas de código.

viernes, 25 de noviembre de 2016

Examinando el árbol de código fuente

Desempaquetar el archivo tar da como resultado la creación de un nuevo directorio, llamado diction-1.11. Este directorio contiene el árbol de código fuente. Miremos dentro:

[me@linuxbox src]$ cd diction-1.11
[me@linuxbox diction-1.11]$ ls
config.guess diction.c       getopt.c     nl
config.h.in  diction.pot     getopt.h     nl.po
config.sub   diction.spec    getopt_int.h README
configure    diction.spec.in INSTALL      sentence.c
configure.in diction.texi.in install-sh   sentence.h
COPYING      en              Makefile.in  style.1.in
de           en_GB           misc.c       style.c
de.po        en_GB.po        misc.h       test
diction.1.in getopt1.c       NEWS

En él, vemos una cantidad de archivos. Los programas pertenecientes al Proyecto GNU, así como muchos otros, proporcionarán los archivos de documentación README, INSTALL, NEWS y COPYING. Estos archivos contienen la descripción del programa, información de cómo construirlo e instalarlo, y sus términos de licencia. Siempre es una buena idea leer los archivos README e INSTALL antes de intentar construir el programa.

Los otros archivos interesantes en este directorio son los que terminan en .c y .h:

[me@linuxbox diction-1.11]$ ls *.c
diction.c getopt1.c getopt.c misc.c sentence.c style.c
[me@linuxbox diction-1.11]$ ls *.h
getopt.h getopt_int.h misc.h sentence.h

Los archivos .c contienen los dos programas C proporcionados por el paquete (style y diction), divididos en módulos. Es una práctica común para programas grandes dividirlos en trozos más pequeños y fáciles de manejar. Los archivos de código fuente son de texto ordinario y pueden examinarse con less:

[me@linuxbox diction-1.11]$ less diction.c

Los archivos .h se conocen como archivos de cabecera (header files). Estos, también, son texto ordinario. Los archivos de cabecera contienen descripciones de las rutinas incluidas en un archivo de código fuente o biblioteca. Para que el compilador pueda conectar los módulos, debe recibir una descripción de los módulos necesarios para completar todo el programa. Al principio del archivo diction.c, vemos esta línea:

#include "getopt.h"

Esto ordena al compilador que lea el archivo getopt.h mientras lee el código fuente de diction.c para poder "saber" qué hay en getopt.c. El archivo getopt.c proporciona rutinas que son compartidas por los programas style y diction.

Encima de la declaración include de getopt.h, vemos otras declaraciones include como estas:

#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

Estas se refieren también a archivos de cabecera, pero se refieren a archivos de cabecera que se encuentran fuera del árbol de código fuente actual. Son proporcionados por el sistema para soportar la compilación de cada programa. Si miramos en /usr/include, podemos verlos:

[me@linuxbox diction-1.11]$ ls /usr/include

Los archivos de cabecera en este directorio se instalaron cuando instalamos el compilador.

miércoles, 23 de noviembre de 2016

Obteniendo el código fuente

Para nuestro ejercicio de compilación, vamos a compilar un programa del Proyecto GNU llamado diction. Es un pequeño pero práctico programa que comprueba la calidad de escritura y edición de los archivos de texto. Como programa, es bastante pequeño y fácil de construir.

Siguiendo la norma, primero vamos a crear un directorio para nuestro código fuente llamado src y luego descargaremos el código fuente en él usando ftp:

[me@linuxbox ~]$ mkdir src
[me@linuxbox ~]$ cd src
[me@linuxbox src]$ ftp ftp.gnu.org
Connected to ftp.gnu.org.
220 GNU FTP server ready.
Name (ftp.gnu.org:me): anonymous
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd gnu/diction
250 Directory successfully changed.
ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-rw-r--r-- 1 1003 65534  68940 Aug 28 1998 diction-0.7.tar.gz
-rw-r--r-- 1 1003 65534  90957 Mar 04 2002 diction-1.02.tar.gz
-rw-r--r-- 1 1003 65534 141062 Sep 17 2007 diction-1.11.tar.gz
226 Directory send OK.
ftp> get diction-1.11.tar.gz
local: diction-1.11.tar.gz remote: diction-1.11.tar.gz
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for diction-1.11.tar.gz
(141062 bytes).
226 File send OK.
141062 bytes received in 0.16 secs (847.4 kB/s)
ftp> bye
221 Goodbye.
[me@linuxbox src]$ ls
diction-1.11.tar.gz

Nota: Como somos los "mantenedores" de este código fuente mientras lo compilamos, lo guardaremos en ~/src. El código fuente instalado por nuestra distribución se instalará en /usr/src, mientras que nuestro código fuente destinado al uso de múltiples usuarios se instala a menudo en /usr/local/src.

Como podemos ver, el código fuente se proporciona normalmente en forma de archivo tar comprimido. Algunas veces se llama tarball, este archivo contiene el source tree (árbol de código fuente), o jerarquía de directorios y archivos que componen el código fuente. Tras llegar al sitio ftp, examinamos la lista de archivos tar disponibles y seleccionamos la versión más reciente para descargar. Usando el comando get contenido en ftp, copiamos el archivo del servidor ftp a la máquina local.

Una vez que se ha descargado el archivo tar, tiene que ser desempaquetado. Esto se hace con el programa tar:

[me@linuxbox src]$ tar xzf diction-1.11.tar.gz
[me@linuxbox src]$ ls
diction-1.11        diction-1.11.tar.gz

Consejo: El programa diction, como todo el software del Proyecto GNU, sigue ciertos estándares para el empaquetado de código fuente. La mayoría del resto de código fuente disponible en el ecosistema Linux también sigue este estándar. Un elemento del estándar es que cuando el código fuente del archivo tar es desempaquetado, se creará un directorio que contiene el árbol de código fuente, y que este directorio se llamará project-x.xx, conteniendo así tanto el nombre del proyecto como el número de versión. Este esquema también permite una fácil instalación de múltiples versiones del mismo programa. Sin embargo, a menudo es una buena idea examinar la disposición del árbol antes de desempaquetarlo. Algunos proyectos no crearán el directorio, pero en su lugar colocarán los archivos directamente en el directorio actual. Esto provocará un desorden en tu, de otra forma, bien organizado directorio src. Para evitar esto, usa el siguiente comando para examinar el contenido del archivo tar:

tar tzvf archivotar | head

lunes, 21 de noviembre de 2016

Compilando un programa en C

Compilemos algo. Sin embargo, antes de hacerlo, vamos a necesitar algunas herramientas como el compilador, el enlazador y make. El compilador C usado casi universalmente en el entorno Linux se llama gcc (GNU C Compiler - Compilador GNU de C), escrito originalmente por Richard Stallman. La mayoría de las distribuciones no instalan gcc por defecto. Podemos comprobar si el compilador esta presente así:

[me@linuxbox ~]$ which gcc
/usr/bin/gcc

El resultado de este ejemplo indica que el compilador está instalado.

Consejo: Tu distribución puede tener un meta-paquete (una colección de paquetes) para el desarrollo de software. Si es así, considera instalarlo si quieres compilar programas en tu sistema. Si tu sistema no ofrece un meta-paquete, prueba a instalar los paquetes gcc y make. En muchas distribuciones, esto es suficiente para realizar el siguiente ejercicio.

viernes, 18 de noviembre de 2016

¿Todos los programas se compilan?

No. Como hemos visto, hay programas como los scripts de shell que no requieren compilación. Se ejecutan directamente. Están escritos en lo que se conoce como lenguajes de script o interpretados. Estos lenguajes han ganado popularidad en los últimos años e incluyen Perl, Python, PHP, Ruby y muchos otros.

Los lenguajes de script se ejecutan por un programa especial llamado intérprete. Un intérprete toma el archivo del programa y lee y ejecuta cada instrucción contenida dentro de él. En general, los programas interpretados se ejecutan mucho más lentamente que los programas compilados. Esto es porque cada instrucción de código en un programa interpretado se traduce cada vez que se ejecuta, mientras que en un programa compilado, una instrucción de código fuente se traduce sólo una vez, y esta traducción se graba permanentemente en el archivo ejecutable final.

Entonces ¿por qué son tan populares los programas interpretados? Para muchas tareas de programación, los resultados son "suficientemente rápidos", pero la ventaja real es que generalmente es más rápido y fácil desarrollar programas interpretados que programas compilados. Los programas se desarrollan a menudo en un ciclo repetitivo de código, compilación, prueba. A medida que un programa crece en tamaño, la fase de compilación del ciclo puede llegar a ser muy larga. Los lenguajes interpretados eliminan el paso de la compilación y por lo tanto aumentan la velocidad de desarrollo del programa.

miércoles, 16 de noviembre de 2016

¿Qué es compilar?

Digamos que, compilar es el proceso de traducir código fuente (la descripción legible por humanos de un programa escrito por un programador) al lenguaje nativo del procesador del ordenador.

El procesador del ordenador (o CPU) trabaja a un nivel muy elemental, ejecutando programas en lo que se llama lenguaje máquina. Este es un código numérico que describe operaciones muy pequeñas, como "añade este byte", "apunta a esta localización en la memoria" o "copia este byte."

Cada una de esas instrucciones se expresan en binario (unos y ceros). Los primeros programas de ordenador se escribieron usando este código numérico, que explicaría por qué los que los escribieron se dice que fumaban mucho, bebían litros de café y usaban gafas con cristales gordos.

El problema se solucionó con la llegada del lenguaje ensamblador, que reemplazaba los códigos numéricos con caracteres mnemotécnicos (ligeramente) más fáciles de usar como CPY (para copiar) y MOV (para mover). Los programas escritos en lenguaje ensamblador se procesan a lenguaje máquina por un programa llamado ensamblador. El lenguaje ensamblador aún se usa en la actualidad para ciertas tareas especializadas de programación, como controladores de dispositivos y sistemas embebidos.

A continuación llegamos a lo que se llama lenguajes de programación de alto nivel. Se llaman así porque permiten que el programador esté menos preocupado con los detalles de lo que está haciendo el procesador y más con resolver los problemas que tiene entre manos. Los primeros (desarrollados durante los años cincuenta) incluyen FORTRAN (diseñado para tareas científicas y técnicas) y COBOL (diseñado para aplicaciones comerciales). Ambos tienen un uso limitado en la actualidad.

Aunque hay muchos lenguajes de programación populares, dos predominan. La mayoría de programas escritos para sistemas modernos están escritos en C o C++. En los ejemplos que siguen, compilaremos un programa en C.

Los programas escritos en lenguajes de programación de alto nivel son convertidos a lenguaje máquina procesándolos en otro programa, llamado compilador. Algunos compiladores traducen las instrucciones de alto nivel en lenguaje ensamblador y luego usan un ensamblador para realizar el último paso de traducirlo a lenguaje máquina.

Un proceso usado a menudo junto con el compilado es el llamado enlazado. Hay muchas tareas comunes realizadas por programas. Tomemos, por ejemplo, abrir un archivo. Muchos programas realizan esta tarea, pero sería un despilfarro que cada programa implemente su propia rutina para abrir archivos. Tiene más sentido tener una única pieza de programación que sepa cómo abrir archivos y permitir a todos los programas que lo necesiten compartirla. Dar soporte a tareas comunes se logra mediante lo que se llaman bibliotecas. Contienen múltiples rutinas, cada una realiza alguna tarea común que puede ser compartida por múltiples programas. Si miramos en los directorios /lib y /usr/lib, podemos ver dónde están muchas de ellas. Un programa llamado enlazador se usa para realizar las conexiones entre la salida del compilador y las bibliotecas que el programa compilado requiere. El resultado final de este proceso es el archivo ejecutable del programa, listo para ser usado.

lunes, 14 de noviembre de 2016

Compilando programas

En este capítulo, veremos cómo construir programas compilando código fuente. La disponibilidad del código fuente es la libertad fundamental que hace que Linux sea posible. Todo el ecosistema de desarrollo Linux se basa en el libre intercambio entre desarrolladores. Para muchos usuarios de escritorio, compilar es un arte perdido. Solía ser bastante común, pero hoy en día, los proveedores de distribuciones mantienen amplios repositorios de binarios precompilados, listos para descargar y usar. En el momento de la escritura de este libro, el repositorio Debian (uno de los más grandes de todas las distribuciones) contiene casi 23.000 paquetes.

Entonces ¿por qué compilar software? Hay dos razones:
  1. Disponibilidad. A pesar del número de programas precompilados en los repositorios de las distribuciones, algunas distribuciones pueden no incluir todas las aplicaciones deseadas. En este caso, la única forma de obtener el programa deseado es compilarlo de su fuente.
  2. Puntualidad. Mientras que algunas distribuciones se especializan en las últimas versiones de los programas, muchas no lo hacen. Esto significa que para tener la última versión de un programa es necesario compilarlo.
Compilar software desde el código fuente puede llegar a ser muy complejo y técnico; mucho más allá del alcance de muchos usuarios. Sin embargo, muchas tareas de compilación son bastante fáciles y sólo necesitan unos pocos pasos. Todo depende del paquete. Veremos un caso muy simple para proporcionar una visión general del proceso y como punto de partida para aquellos que quieran emprender un estudio más a fondo.

Presentaremos un nuevo comando:
  • make - Utilidad para mantener programas

viernes, 11 de noviembre de 2016

lunes, 7 de noviembre de 2016

Resumiendo

En este capítulo, hemos visto cómo las impresoras del pasado han influido en el diseño de los sistemas de impresión de las máquinas tipo Unix, y cuánto control tenemos en la línea de comandos para controlar no sólo la programación y ejecución de trabajos de impresión, sino también la variedad de opciones de salida.

viernes, 4 de noviembre de 2016

lprm/cancel - Cancela trabajos de impresión

CUPS aporta dos programas usados para terminar trabajos de impresión y eliminarlos de la cola de impresión. Uno es estilo Berkeley (lprm) y el otro es System V (cancel). Difieren ligeramente en las opciones que soportan, pero básicamente hacen lo mismo. Usando nuestro trabajo de impresión anterior como ejemplo, podríamos parar el trabajo y eliminarlo de la siguiente forma:

[me@linuxbox ~]$ cancel 603
[me@linuxbox ~]$ lpq
printer is ready
no entries

Cada comando tiene opciones para eliminar todos los trabajos pertenecientes a un usuario en particular, una impresora en particular y múltiples números de trabajos. Sus respectivas man pages tienen todos los detalles.

miércoles, 2 de noviembre de 2016

lpq - Muestra el estado de la cola de impresión

Para ver el estado de una cola de impresión, se utiliza el programa lpq. Esto nos permite ver el estado de la cola y de los trabajos de impresión que contiene. Aquí tenemos un ejemplo de una cola vacía de una impresora por defecto del sistema llamada "printer":

[me@linuxbox ~]$ lpq
printer is ready
no entries

Si no especificamos una impresora (usando la opción -P), se muestra la impresora por defecto del sistema. Si enviamos un trabajo a la impresora y luego vemos la cola, lo veremos listado:

[me@linuxbox ~]$ ls *.txt | pr -3 | lp
request id is printer-603 (1 file(s))
[me@linuxbox ~]$ lpq
printer is ready and printing
Rank    Owner  Job  File(s)          Total Size
active  me     603  (stdin)          1024 bytes

lunes, 31 de octubre de 2016

lpstat - Muestra el estado del sistema de impresión

El programa lpstat es útil para determinar los nombres y la disponibilidad de las impresoras en el sistema. Por ejemplo, si tuviéramos un sistema con una impresora física (llamada "printer") y una impresora virtual (llamada "PDF"), podríamos comprobar su estado así:

[me@linuxbox ~]$ lpstat -a
PDF accepting requests since Mon 08 Dec 2008 03:05:59 PM EST
printer accepting requests since Tue 24 Feb 2009 08:43:22 AM EST

Además, podríamos determinar una descripción más detallada de la configuración del sistema de impresión de esta forma:

[me@linuxbox ~]$ lpstat -s
system default destination: printer
device for PDF: cups-pdf:/
device for printer: ipp://print-server:631/printers/printer

En este ejemplo, vemos que "printer" es la impresora por defecto del sistema y que es una impresora de red que usa el Protocolo de Impresión de Internet - Internet Printing Protocol (ipp://) que está conectada a un sistema llamado "print-server".

Las opciones útiles usadas más habitualmente incluyen:

Tabla 22-5: Opciones comunes de lpstat
Opción Descripción
-a [impresora...] Muestra el estado de la cola de impresión de impresora. Fíjate que es el estado de la capacidad de la cola de la impresora para aceptar trabajos, no el estado de las impresoras físicas. Si no se especifican impresoras, se muestran todas las colas de impresión.
-d Muestra el nombre de la impresora por defecto del sistema.
-p [impresora...] Muestra el estado de la impresora especificada. Si no se especifican impresoras, se muestran todas.
-r Muestra el estado del servidor de impresión.
-s Muestra un resumen de estado.
-t Muestra un informe de estado completo.

viernes, 28 de octubre de 2016

Monitorizando y controlando trabajos de impresión

Como los sistemas de impresión Unix están diseñados para manejar múltiples trabajos de impresión de múltiples usuarios, CUPS está diseñado para hacer lo mismo. A cada impresora se le da una cola de impresión, donde se aparcan los trabajos hasta que pueden ser puestos en cola hacia la impresora. CUPS proporciona varios programas de línea de comandos que se usan para gestionar el estado de la impresora y las colas de impresión. Al igual que los programas lpr y lp, estos programas de gestión siguen el modelo de los sistemas de impresión Berkeley y System V.

miércoles, 26 de octubre de 2016

Otra opción: a2ps

El programa a2ps es interesante. Como podemos suponer por su nombre, es un programa de conversión de formatos, pero también es algo más. Su nombre originalmente significa "ASCII to PostScript - ASCII a PostScript" y se usaba para preparar archivos de texto para imprimir en impresoras PostScript. A lo largo de los años, sin embargo, las capacidades del programa han crecido, y ahora su nombre significa "Cualquier cosa a PostScript". Aunque su nombre nos sugiere un programa de conversión de formatos, es realmente un programa de impresión. Manda su salida por defecto a la impresora predeterminada del sistema en lugar de a la salida estándar. El comportamiento del programa por defecto es como el de una "impresora bonita", lo que significa que mejora la apariencia de la salida. Si usamos el programa para crear un archivo PostScript en nuestro escritorio:

[me@linuxbox ~]$ ls /usr/bin | pr -3 -t | a2ps -o ~/Desktop/ls.ps -L 66
[stdin (plain): 11 pages on 6 sheets]
[Total: 11 pages on 6 sheets] saved into the file `/home/me/Desktop/ls.ps'

Aquí filtramos la secuencia con pr, usando la opción -t (que omite encabezados y pies de página) y luego con a2ps, especificamos un archivo de salida (opción -o) y 66 líneas por página (opción -L) para que coincida con la paginación de salida de pr. Si vemos el archivo resultante con un visor de archivos adecuado, veremos esto:
Figura 6: Viendo la salida de a2ps

Como podemos ver, la plantilla de salida por defecto es en formato de dos páginas por hoja. Esto provoca que el contenido de dos pagínas se impriman en cada hoja de papel. a2ps también aplica bonitos encabezados y pies de página.

a2ps tiene muchas opciones. Aquí tenemos un resumen:

Tabla 22-4: Opciones de a2ps
Opción Descripción
--center-title texto Establece el título del centro de la página a texto.
--columns número Ordena páginas en número columnas. La opción por defecto 2.
--footer texto Establece el pie de página a texto.
--guess Muestra los tipos de archivos dados como argumentos. Como a2ps trata de convertir y formatear todos los tipos de datos, esta opción puede ser útil para predecir qué hará a2ps cuando se le dé un archivo en particular.
--left-footer texto Establece el pie de página izquierdo a texto.
--left-title texto Establece el título izquierdo de la página a texto.
--line-numbers=intervalo Numera las líneas de salida cada intervalo de líneas.
--list=defaults Muestra la configuración por defecto.
--list=tema Muestra las configuraciónes de tema, donde tema es uno de los siguientes: delegaciones (programas externos que se usarán para convertir datos), codificación, características, variables, medios (tamaño de papel y similares), ppd (descripciones de impresora PostScript), impresoras, prólogos (porciones de código que están prefijados a la salida normal), hojas de estilo y opciones de usuario.
--pages rango Imprime páginas incluidas en rango.
--right-footer texto Establece el pie de página derecho a texto.
--right-title texto Establece el título derecho de la página a texto.
--rows número Ordena páginas en número filas. Por defecto es una.
-B Sin encabezados de página.
-b texto Establece el encabezado de página a texto.
-f tamaño Usa fuente de tamaño puntos.
-l número Establece los caracteres por línea a número. Esta y la opción -L (a continuación) pueden usarse para hacer que archivos paginados con otros programas, como pr, encajen correctamente en la página.
-L número Establece las líneas por página a número.
-M nombre Usa el medio nombre. Por ejemplo, "A4"
-n número Imprime número de copias de cada página.
-o archivo Envía la salida a archivo. Si archivo se especifica como "-", usa la salida estándar.
-P impresora Usa impresora. Si no se especifica la impresora, se usa la impresora por defecto del sistema.
-R Orientación vertical.
-r Orientación horizontal.
-T número Establece el tamaño del tabulador a número caracteres.
-u texto Imprime texto (marca de agua) una capa por debajo de la página.

Esto es sólo un resumen. a2ps tiene varias opciones más.

Nota: a2ps está todavía en desarrollo activo. Durante mi prueba, he notado comportamientos diferentes en varias distribuciones. En CentOS 4, la salida siempre va a la salida estándar por defecto. En CentOS 4 y Fedora 10, la salida va por defecto a un medio A4, a pesar de que el programa esté configurado para usar medios de tamaño carta por defecto. Podría superar estos problemas especificando explícitamente la opción deseada. En Ubuntu 8.04, a2ps se comporta como está documentado.

Ten en cuenta también que hay otro formateador de salida útil para convertir texto a PostScript. Se llama enscript, puede realizar muchos de los mismos tipos de trucos de formateo e impresión, pero al contrario que a2ps, sólo acepta entrada de texto.

lunes, 24 de octubre de 2016

lp - Imprime archivos (Estilo System V)

Al igual que lpr, lp acepta tanto archivos como entrada estándar para imprimir. Se diferencia de lpr en que soporta una colección de opciones diferente (y algo más sofisticada). Aquí tenemos las opciones comunes:

Tabla 22-3: Opciones comunes de lp
Opción Descripción
-d impresora Establece el destino (impresora) a impresora. Si no se especifica la opción d, se usa la impresora por defecto del sistema.
-n número Establece el número de copias a número.
-o landscape Establece la salida a orientación horizontal.
-o fitplot Escala el archivo para que se ajuste a la página. Esto es útil cuando imprimimos imágenes, como archivos JPEG.
-o scaling=número Escala el archivo a número. El valor 100 rellena la página. Los valores menores de 100 reducen, mientras que los valores mayores de 100 hacen que el archivo se imprima en varias páginas.
-o cpi=número Establece los caracteres de salida por pulgada a número. La opción por defecto es 10.
-o lpi=número Establece las líneas de salida por pulgada a número. La opción por defecto es 6.
-o page-bottom=puntos
-o page-left=puntos
-o page-right=puntos
-o page-top=puntos
Establece los márgenes de la página. Los valores se expresan en puntos, una unidad de medida tipográfica. Hay 72 puntos en una pulgada.
-P páginas
Especifica la lista de páginas. Las páginas pueden expresarse como una lista separada por comas y/o un rango. Por ejemplo "1,3,5,7-10"

Generaremos nuestro listado de directorio de nuevo, esta vez imprimiendo 12 CPI y 8 LPI con un margen izquierdo de media pulgada. Fijate que hemos ajustado las opciones de pr para que tenga en cuenta el nuevo tamaño de página:

[me@linuxbox ~]$ ls /usr/bin | pr -4 -w 90 -l 88 | lp -o page-left=36 -o cpi=12 -o lpi=8

Esta tubería produce un listado de cuatro columnas usando un tamaño de letra más pequeño que el que viene por defecto. El incremento de número de caracteres por pulgada nos permite ajustar más columnas en la página.

viernes, 21 de octubre de 2016

lpr - Imprime archivos (Estilo Berkeley)

El programa lpr puede usarse para enviar archivos a la impresora. También puede usarse en tuberías, ya que acepta entrada estándar. Por ejemplo, para imprimir resultados de nuestro directorio multicolumna listado antes, podríamos hacer ésto:

[me@linuxbox ~]$ ls /usr/bin | pr -3 | lpr

y el informe será enviado a la impresora por defecto del sistema. Para enviar el archivo a una impresora distinta, puede usarse la opción -P así:

lpr -P nombre_impresora

donde nombre_impresora es el nombre de la impresora que queremos. Para ver una lista de impresoras conocidas por el sistema:

[me@linuxbox ~]$ lpstat -a

Consejo: Muchas distribuciones Linux permiten definir una "impresora" que produce archivos en PDF (Portable Document Format - Formato de Documento Portable), en lugar de imprimir en la impresora física. Esto es muy útil para experimentar con comandos de impresión. Comprueba tu programa de configuración de impresora para ver si soporta esta configuración. En algunas distribuciones, puedes necesitar instalar paquetes adicionales (como cups-pdf) para habilitar esta capacidad.

Aquí tienes algunas de las opciones comunes de lpr:

Tabla 22-2: Opciones comunes de lpr
Opción Descripción
-# número Establece el número de copias a número.
-p Imprime cada página con un encabezado sombreado con la fecha, hora, nombre del trabajo y número de página. Esta opción denominada "impresión bonita" puede usarse cuando imprimimos archivos de texto.
-P impresora Especifica el nombre de la impresora usada para la salida. Si no se especifica una impresora, se usa la impresora por defecto del sistema.
-r Borra archivos tras imprimirlos. Esto sería útil para programas que producen archivos temporales de impresora.