Introducción al intérprete de comandos

El intérprete de comandos permite la comunicación entre el sistema y el usuario. Aunque actualmente las interficies gráficas de usuario (GUI) facilitan el trabajo cotidiano, todavía existen funciones, como la automatización de tareas, que se resuelven mejor desde la línea de comandos (CLI Command Line Interface).

Las ventajas evidentes de la línea de comandos frente a la interfaz gráfica son:

  • Mayor capacidad de expresión semántica
  • Menor consumo de recursos (muy importante si se actúa sobre sistemas remotos mediante una conexión lenta)
  • Posibilidad de programar guiones o scripts.
  • Programas y sintáxis enfocada a conseguir una mayor productividad

Entre los inconvenientes:

  • El usuario precisa de un aprendizaje previo
  • Funcionamiento menos intuitivo


Existen multitud de intérpretes de comandos, en algunos sistemas operativos puede haber varios instalados y cada usuario decide cuál utilizar.

Algunos ejemplos:

Unix
Windows
Korn Shell (ksh)
C Shell (csh)
Bourne Shell (sh)
Bourne Again Shell (bash)
Tcsh
Z Shell (Zsh)
command.com
cmd.exe
MSH Windows PowerShell (Monad)

Entre los shells más destacados, encontramos:

Command.com y cmd.exe
Intérprete de comandos presente en los sistemas operativos Windows 95, 98, 98 SE y Me. Está basado en la sintáxis de MS-DOS y puede funcionar tanto en modo interactivo como ejecutando archivos por lótes (.BAT). La versión de Windows NT, 2000, XP y 2003 es cmd.exe aunque también esté presente la antigua por compatibilidad.
MSH Windows PowerShell (Monad)
Nuevo shell de Microsoft presentado con Windows Vista y también disponible en XP, requiere el marco de trabajo .NET. Entre sus nuevas características se encuentra el autocompletado (al pulsar el tabulador).
Bourne Again Shell (Bash)
Versión de shell que se utiliza por defecto en la mayoría de las distribuciones GNU/Linux, OpenSolaris o en MacOSX. Está basado en el tradicional Bourne Shell y aporta nueva características. Forma parte del proyecto GNU.


Características de Bash

Bash es el intérprete de comandos que se desarrolló en 1987 para el proyecto GNU, su nombre hace referencia al conocido shell Bourne Shell y toma ideas de otros shells como Korn Shell y C Shell.

Bash puede utilizarse como intérprete de comandos en modo interactivo, pero también puede procesar archivos con órdenes. A estos archivos ejecutables se les llama guiones o, en inglés, scripts. Cuando se utiliza un intérprete como Bash no es necesario compilar el código fuente. El intérprete es capaz de interpretar el código fuente sentencia a sentencia. El tiempo de desarrollo es más corto que con los lenguajes compilados pero el tiempo de ejecución es habitualmente mayor.

El espectro de características de Bash es realmente grande. Para una descripción detallada es necesario acudir a la documentación oficial.

Case sensitive

Bash, y en general Unix, distingue entre mayúsculas y minúsculas. No es lo mismo escribir ls que Ls.


Edición con Bash

Utilizado en modo interactivo, Bash ofrece todas las facilidades de la biblioteca Readline. Además, podemos encontrar otras facilidades como el historial de comandos.

Todos los comandos que se ejecutan se guardan en el historial. Es posible ver los últimos comandos ejecutados mediante el comando interno history. Utilizando las flechas de los cursores (arriba y abajo), es posible navegar por dicho historial para reutilizar sentencias ya introducidas.

También es posible:

  • Utilizar !! para repetir la última sentencia
  • Utilizar !200 para repetir el comando que está en la posición 200 del historial
  • Utilizar la variable HISTSIZE para definir el tamaño máximo del historial


Otra de las características de edición de Bash más cómodas, es el autocompletado de nombres de comandos, ficheros, etc... Símplemente es necesario escribir las primeras letras y pulsar la tecla TAB para que el shell complete lo que estamos escribiendo. Si lo que hemos escrito puede completarse de diferentes maneras, al pulsar dos veces la tecla TAB nos mostrará las alternativas.

Expansión de nombres de ficheros

Es posible indicar patrones de coincidencia para los nombres de los ficheros. De este modo, un patrón puede referirse a muchos ficheros diferentes (todos aquellos con un nombre que concuerda con el patrón). En la expansión de nombres de ficheros, Bash utiliza patrones formados por los carácteres ?, * y [ de la siguiente manera:

?
Coincide con cualquier carácter (sólo uno)
*
Coincide con cualquier cadena, incluyendo a la cadena nula
[...]
Coincide con cualquiera de los carácteres enumerados entre los corchetes (por ejemplo [aeiou] coincide con cualquier vocal). Si se utiliza un guión se pueden incluir rangos (por ejemplo [a-zA-Z]). También es posible utilizar la sintáxis [:class:] para denotar una clase predefinida de carácter. Las clases predefinidas (POSIX 1003.2) son: alnum, alpha, ascii, blank, cntrl, digit, graph, lower, print, punct, space, upper, word, xdigit. Si el primer carácter entre corchetes es la admiración !, entonces entre los corchetes se enumeran los carácteres que no deben existir.

Ejemplos:

patrónCoincide con
Cualquier nombre
*rpmNombres que finalizan en 'rpm'
???datos*dt
Nombres que comienzan con tres carácteres cualesquiera, les sigue la cadena 'datos', luego pueden tener
cualquier cadena (o no) y han de acabar en 'dt'
[aeiou]???.jpgNombres que comienzan con una vocal en minúscula, seguidos de tres carácteres y la cadena '.jpg' 
Fichero[!a-z]Nombres que comienzan con 'Fichero' seguido de cualquier carácter que no sea una letra minúscula

Expansión de llaves

La expansión de llaves, Brace Expansion,  es similar a la expansión de nombres de ficheros. Nos sirve para teclear menos. Pero en este caso, los patrones generan cadenas que no necesariamente han de existir.

Ejemplos:

[vcarceler@localhost pruebas]$ echo Fichero-{1,2,3,4,5}
Fichero-1 Fichero-2 Fichero-3 Fichero-4 Fichero-5
[vcarceler@localhost pruebas]$ echo directorio/{gráficos,juegos}/bin/{*.exe,*.jpg}
directorio/gráficos/bin/*.exe directorio/gráficos/bin/*.jpg directorio/juegos/bin/*.exe directorio/juegos/bin/*.jpg

Ni las llaves ni las comas pueden ir entre comillas.

Alias

Es posible definir palabras que se substituirán por sentencias a nuestra conveniencia. Por ejemplo, para hacer que el shell interprete "ls -lia" cada vez que nosotros escribamos listado, podemos hacer:

alias listado="ls -lia"

Si se utiliza el comando interno alias sin argumentos se muestra la lista de alias definidos. Mediante el comando interno unalias se pueden borrar alias definidos. Un alias dura mientras está activa la sesión en la que se definió. Al cerrar el shell y abrir uno nuevo no estará definido. Si se quiere disponer de un determinado alias en cada sesión, será necesario definirlo en los ficheros de inicialización de Bash, típicamente ~/.bashrc.

Variables de entorno

Las variables de entorno son porciones de memoria que guardan datos. Estas variables pueden ser locales a la instáncia del shell  que se está utilizando o bien globales a todos sus subshells. En Bash las variables de entorno tienen un nombre alfanumérico. Para referirse al valor de la variable se utiliza el carácter dólar $ como prefijo del nombre.

Por ejemplo:

[vcarceler@localhost ~]$ echo $saludo                     -> Se accede a una variable no
-> inicializada.
[vcarceler@localhost ~]$ saludo="Hola, cómo va todo ?" -> Se asigna valor a 'saludo'
[vcarceler@localhost ~]$ echo $saludo -> Se comprueba el valor
Hola, cómo va todo ?
[vcarceler@localhost ~]$ export saludo="Hola ?" -> Se exporta la variable a este shell
[vcarceler@localhost ~]$ -> y a sus descendientes.

Como los alias, las variables de entorno sólo viven mientras vive el intérprete de comandos en el que fueron declaradas, al terminar la sesión pierden su valor.

Comandos internos relacionados:

export [variable]
Variables a exportar a los subshells. A no ser que se exporten de manera explícita cada shell tiene sus propias variables de entorno.
unset [variable] 
Libera la memoria de la variable indicada, de modo que esta deja de existir.
env
Muestra un listado de todas las variables de entorno definidas.

Algunas variables de entorno importantes son:

PATH
Guarda una lista de directorios (separados por el carácter ':') en los que se van a buscar los programas a ejecutar. El órden en la lista es importante. Si se llega al final de la lista sin encontrar el comando buscado por el usuario, entonces el shell presenta un mensaje de error al usuario.
HOME
Guarda la ruta del directorio de conexión para el usuario.
PWD
Guarda la ruta del directorio actual
DISPLAY
Guarda el identificador del escritorio en el que se intentarán abrir las ventanas de los programas que ejecutamos.
PROMPT_COMMAND
Si está definida, y su valor no es nulo, se supone que contiene una sentencia que el shell ejecuta automáticamente después de ejecutar lo que el usuario le manda.
PS1, PS2, PS3 y PS4
Definen el prompt del sistema. Puede consultar una lista de códigos de control para modificar el prompt.
RANDOM
Cada vez que se consulta su valor, se obtiene un número pseudoaleatorio entre 0 y 32767. Al fijar su valor se planta la semilla (seed) del generador de números pseudoaleatorios.
SECONDS
Al consultar su valor se obtiene el número de segundos desde que el shell se inició. 

Comillas

En Bash las comillas se utilizan para delimitar cadenas que tienen espacios (o tabuladores, o sáltos de línea). Si un fichero tiene un nombre con espacios, debe entrecomillarse.

Tipos de comillas:

Comilla símple (apóstrofe ')
La comilla símple delimita cadenas en las que no se realiza interpolación de variables. Es decir, no se substituyen por su contenido.
Comilla doble (")
Delimita cadenas en las que se realiza interpolación de variables. Se substituyen por su contenido.
Comilla invertida (acento abierto `)
Delimita cadenas que se suponen sentencias para el shell. El shell las ejecuta y retorna el resultado. Se realiza interpolación de variables.

Ejemplos:

[vcarceler@localhost ~]$ color_favorito=rojo
[vcarceler@localhost ~]$ echo 'Mi color favorito es $color_favorito'
Mi color favorito es $color_favorito
[vcarceler@localhost ~]$ echo "Mi color favorito es $color_favorito"
Mi color favorito es rojo
[vcarceler@localhost ~]$ listado=`ls -a .`
[vcarceler@localhost ~]$ directorio=/
[vcarceler@localhost ~]$ listado=`ls $directorio`
[vcarceler@localhost ~]$ echo $listado
bin/ boot/ dev/ etc/ home/ initrd/ lib/ lost+found/ mnt/ opt/ proc/ root/ sbin/ sys/ tmp/ usr/ var/
[vcarceler@localhost ~]$


Escapando carácteres

Ya se ha visto que algunos carácteres tienen un significado especial para el shell, por ejemplo los carácteres: ?, *, [, ], $, ', ", `, ... o el espacio ' ' que actúa como separador de palábras. Cuando se quiere desposeer a uno de estos carácteres de su significado especial, se utiliza la contrabarra \ (que evidentemente es un carácter especial) para escaparlos.

Ejemplos:

ComandoFunción
ls *
Listar todos los ficheros (se llamen como se llamen)
ls \*
Listar el fichero que tiene por nombre '*'
ls Hola que tal?
Listar los ficheros: Hola, que y todos aquellos que comienzan por
'tal' seguido de cualquier otro carácter
ls Hola\ que\ tal\? Listar el fichero que se llama textualmente 'Hola que tal?'

Redirecciónes de entrada/salida

En el shell existen tres flujos de entrada/salida:

La salida standard (stdout)
Es el lugar en el que se presenta la salida, a no ser que se indique lo contrario. La salida standard es la cónsola en la que se está trabajando.
La entrada standard (stdin)
Es el lugar del que se leen datos a no ser que se indique lo contrario. Por defecto, es el teclado.
La salida de error (stderr)
Es el lugar en el que se presentan los mensajes de error a no ser que se indique lo contrario. La salida de error por defecto apunta a la cónsola en la que se está trabajando.

Es posible redirigir cada una de ellas:

# Redirección de stdout hacia un fichero de nombre salida (redirección destructiva)
echo "hola" >salida
# Igual, pero sin destruir (añadiendo datos al fichero)
echo "adios" >>salida
# Redirección de la salida de error a un fichero (destructiva)
cat $fichero 2>salida_error
# Redirección de la salida de error no destructiva
cat $fichero 2>>salida_error
# Redirección de stdin hacia un fichero
less </etc/passwd


Tuberias (pipes)

Bash permite ejecutar varios programas, en procesos diferentes, a la vez. De tal manera que la salida y la entrada de estos procesos queda conectada formando una cadena.

Ejemplos:

cat /etc/passwd | less
cat /etc/passwd | grep /bin/bash | wc -l


Ficheros de control de Bash

Si existen los ficheros .bash_profile y .bashrc, que permiten configurar automáticamente el entorno de ejecución, se leen e interpretan cada vez que se invoca al intérprete de comandos Bash. En estos ficheros pondremos la definición de todos los alias y de todas las variables de entorno que esperemos encontrar en cada una de nuestras sesiones de trabajo.

También puede existir el fichero .bash_logout, que sería interpretado al cerrar la sesión de trabajo.

Estos ficheros pueden encontrarse en:

En /etc/profile
Con lo que resultan de aplicación para cualquier usuario
En el directorio personal ~ (/home/usuarioX)
Con lo que sólo se aplican a las sesiones del usuario

Cuando se invoca Bash, se tiene en cuenta si:

Se trata de un shell interactivo:
Un shell interactivo es un shell en el que el usuario escribe comandos y estos son ejecutados. Un shell no interactivo es aquel que se invoca para interpretar un shell script (trabajo por lotes).
Se trata de un login shell:
Un login shell es aquel que dá la bienvenida al sistema y se ejecuta automáticamente al identificarse con el nombre de usuario y la contraseña.

Casos: 

  1. Si se trata de un shell interactivo de tipo login (o se ha utilizado la opción --login) se leerá e interpretará (si existe y es legible) el contenido de /etc/profile. Después buscará (en el orden especificado) e interpretará el primer fichero que exista y sea legible de los siguientes: ~/.bash_profile, ~/.bash_login, ~/.profile
  2. Si se trata de un shell interactivo que no es de tipo login se buscará e interpretará (si existe y es legible) el fichero ~/.bashrc
  3. Si se trata de un shell no interactivo, Bash utiliza el contenido de la variable de entorno BASH_ENV para encontrar el fichero de inicialización. Es decir, la variable de entorno deberá contener la ruta absoluta del fichero a utilizar.

 


Para saber más: