Esta página puede ser redistribuida libremente bajo los términos de la licencia GPL. Vease ( GPL texto original ) o si lo prefiere (Traducción española no oficial de la GPL) Al margen de las obligaciones legales que se derivan del uso de esta licencia rogamos sea respetada la referencia a su lugar de publicación original www.ciberdroide.com. y a su autor original Antonio Castro Snurmacher (Madrid 01/01/2000).

Ausencia de Garantía

Esta ausencia de garantía se hace extensa a cualquier tipo de uso de este material y muy especialmente a las prácticas, ejercicios, y de ejemplos que encuentre en estas páginas. Deberá trabajar siempre salvo indicación contraria con un SO Linux y con un usario distinto de 'root' sin privilegios especiales. Como directorio de trabajo se procurará usar el directorio '/tmp' o algún otro que no contenga información valiosa. Tampoco se considera buena idea practicar en una máquina que contenga información valiosa.

Todo esto son recomendaciones de prudencia. En cualquier caso si algo sale mal toda la responsabilidad será únicamente suya. En ningún caso podrá reclamar a nadie por daños y perjuicios derivados del uso de este material. Para más información lea el contenido de la licencia GPL y abstengase de hacer prácticas si no está dispuesto a asumir toda la responsabilidad.

...
..

LA SHELL

sh(1) ksh(1) csh(1) bash(1)

Introducción a la shell de Unix
Existen varias shells para Unix, Korn-Shell (ksh), Bourne-Shell (sh), C-Shell (csh), y muchas más. Existen algunas para proósitos especiales. Por ejemplo la remote-Shell (rsh) se utiliza para ejecutar comandos en un ordenador remoto. La Secure Shell (Ssh) se utiliza para establecer una conexion segura con un ordenador remoto.

La más utilizada en Linux es la Bourne-Again SHell (bash).

Nosotros de momento vamos a tratar principalmente la Bourne Shell que es la más estándar.
La Korn-Shell y la bash son distintos superconjuntos distintos de la Bourne Shell y por ello todo lo que se diga para ambas. En Linux se suele usar la Bourne-Again SHell (bash), como sustituta de la Bourne-Shell (sh). Puntualmente también explicaremos alguna peculiaridad de la bash.

Para saber que shell está usando usted haga lo siguiente:

$ ps | grep $$

Si aparece -bash o -sh puede continuar sin problemas ya que está usando una shell adecuada para practicar lo que viene a continuación. En caso contrario tecle el comando 'sh' o el comando 'bash' antes de continuar. Vuelva a realizar la comprobación anterior y verá que ahora esta usando otra shell.

En cualquier caso cualquier Shell es un programa normal y corriente, pero incorpora muchos de los conceptos más prácticos de Unix. No tiene nada de particular que algunos sistemas incorporen algunas Shells distintas.

Una Shell no es solo un interprete de comandos. Una Shell es sobre todo un interprete de un potente lenguaje.

Estructura de la linea de orden
Sabe usted lo que es un introductor ? (en ingles lo llaman prompt). Pues es aquello que el interprete de comandos muestra para indicar que está esperando a que se introduzca una orden. En Unix el introductor de la orden de comandos no es siempre el mismo. Por defecto suele venir configurado distintos introductores para distintos interpretes de comandos y también se usa un introductor distinto para el usuario root. Sin embargo el introductor puede ser variado ya que es almacenado en una variable del sistema. En realidad la shell utiliza dos introductores distintos. Para ver cuales está utilizando ahora teclee lo siguiente:

$ echo "Introductor 1=$PS1"
$ echo "Introductor 2=$PS2"

Cuando aparece el primer introductor del sistema $PS1 indica que la shell está esperando la introducción de una orden. Las ordenes se terminan mediante . Si después de pulsar la shell no considera que el comando este completo quedará esperando más entrada mostrando el segundo introductor $PS2.

Si alguna vez no es capaz de terminar la introducción de un comando pruebe a abortar usando <Ctrl-C>

Una orden constará de un número variable de elementos separados por blancos, o por <tab>.

En una orden se pueden distinguir comandos, opciones, argumentos, meta-caracteres, comentarios, comandos internos...etc. Los blancos se usan para separar las opciones y argumentos presentes en una linea de ordenes y usados de esta forma (como separadores) una secuencia de blancos tiene el mismo efecto que uno solo. (Ojo en Unix las mayúsculas y minúsculas son significativas.)

A continuación se menciona de forma no exaustiva los elementos del lenguaje shell-script. No intentamos que se aprenda todos estos elementos ni vamos a comentar ahora todos ellos. Bastará que se haga una idea de que tipos de elementos pueden intervenir.

Para una referencia exacta y completa debería acudir a la página man bash(1) aunque eso resultaría excesivo en este momento.

Vamos a dar una serie de ejemplos para que los practique en su ordenador. No es necesario que introduzca los comentarios.

# Orden con un único comando sin opciones ni argumentos
$ ls

# Orden sin ociones pero con tres argumentos
$ ls . / ..

# Orden con un comando y una opción
$ ls -l

# Orden con un comando y tres opciones indicada de varias formas
# distintas pero equivalentes entre si.
$ ls -trl
$ ls -rtl
$ ls -ltr
$ ls -l -t -r
$ ls     -l    -t     -r
$ ls -lt -r
$ ls -l -tr

# Opciones que empiezan con '--'
$ ls --help
$ ls --version
$ ls --color=auto

# Ejemplo de opciones y argumentos sensibles al orden.
$ date -d now -R
$ date -d -R now

# ejemplo de opcion que empieza por '+'
$ date +'%a %b %e %H:%M:%S %Z %Y'

Expansión de la linea de orden:
Existe un detalle muy importante en el funcionamiento de la shell. Una cosa es lo que nosotros escribimos y otra lo que la shell ordena que se ejecute. Antes de que la shell ejecute comando alguno expande la linea de ordenes. Es decir esa linea se transforma en otra linea más larga. La orden resultante puede ser muy larga y tener muchos argumentos. Por ejemplo un '*' será sustituido por la lista de ficheros que se encuentren en el directorio actual. Quizas alguien encuentre que existe un parecido con el uso de '*.*' en Msdos por ejemplo pero el parecido es muy superficial y engañoso. Cuando en Unix hacemos 'ls *' el intreprete de comandos expande el asterisco y el comando ls recibe una lista de ficheros que tiene que listar. En Msdos cuando hacemos 'dir *.*' el interprete de comandos no expande nada. El comando dir recibe como argumento no la lista de ficheros que tiene que listar sino un '*.*' y será el propio comando dir quien tenga que expandir el argumento para obtener la lista de ficheros que tiene que listar. Por lo tanto la expansión en Unix está centralizada en el interprete de comandos el cual permite hacer expansiones mucho más potentes que en Msdos. Un comando no es más que un ejecutable y los ejecutables generalmente admiten argumentos. En Unix los comandos toman los argumentos que les pasa la shell después de la expansión.

El '*' se expande en base a los nombres de ficheros presentes en nuestro directorio actual, sustituyéndose por una cadena de caracteres cualquiera que no empieze por un punto.

Vamos a realizar una práctica completa. Algunos de los comandos que vamos a usar como 'cd', 'mkdir', 'touch' y otros son comandos que no explicamos en este momento pero que servirán para situarnos en un directorio de trabajo y crear algunos ficheros para la práctica.

Las lineas que empiecen con $ comando ... son comandos que debe introducir.
Las lineas en amarillo salida del comando ... son la salida obtenida.
Esto no quiere decir que siempre que pongamos un comando vayamos a poner a continuación
su salida en amarillo pero si la ponemos es para que compruebe lo que debe obtener.

Ahora introduzca los comandos que indicamos a continuación y compruebe la salida obtenida.

$ cd /tmp
$ mkdir pruebas
$ cd pruebas
# Ya hemos creado un directorio de pruebas y ya estamos dentro de él.
# Para comprobarlo hacemos
$ pwd
/tmp/pruebas
# Ahora creamos unos ficheros para practicar
$ touch kk1 kk2 kkkk kk.txt kk.doc j2.txt .kk
$ echo *
kk1 kk2 kkkk kk.txt kk.doc j2.txt
$ echo k*
kk1 kk2 kkkk kk.txt kk.dox
$ echo *xt
kk.txt j2.txt
$ echo *.

$ echo .*
.kk
$ echo *.*
kk.txt kk.doc j2.txt

Fijese que los resultados dependen de los ficheros existentes en el directorio actual. Los mismos comandos realizados desde otro directorio distinto darían otro resultado.

Mantenga la sesión de la práctica en este punto porque continuaremos haciendo algunas prácticas más desde este mismo punto.

El '?' se expande como un único carácter y tampoco expande un punto en el comienzo del nombre del fichero. Introduzca ahora los siguientes comandos y compruebe la salida obtenida.

$ echo ???
kk1 kk2
$ echo kk?
kk1 kk2

A continuación no teclee nada ya que el resultado es solo una hipotesis. Supongamos que obtenemos un error de este tipo.

$ ls *
ksh: /bin/ls: arg list too long

Esto significa que el '*' se expande en un número demasiado grande de ficheros y eso resulta un problema para el interprete de comandos. La forma de obtener la lista de ficheros sería haciendo

$ ls

o también.

$ ls .

Si quisieramos averiguar el número de ficheros podríamos contarlos con 'wc'. Aqui estamos usando un '|' que aun no hemos explicado pero que lo explicaremos en el próximo capítulo de redirección de entrada salida.

$ ls | wc -l

Peculiaridades de expansión en la bash de Linux
Solo vamos a mencionar unas cuantas y remitimos al lector a la página del manual de bash para más informacion.

En bash(1) en el apartado de EXPANSION se mencionan más cosas.

Redirección de entrada salida:
Normalmente los proceso utilizan para entrada y salida de datos unos dispositivos estandar. Son entrada estandar, salida estandar y salida estandar de errores. Generalmente se utiliza como entrada estandar la entrada de teclado y como salida estandar y salida estandar de errores la pantalla. La salida estandar se usa como salida normal de datos y la salida estandar de errores se usa para sacar mensajes que indican algún error, aunque también se usa simplemente como flujo alternativo en caso de que no resulte deseable su mezcla con otros datos que deben salir por salida estandar.

Se puede alterar flujo de datos que va desde un dispositovo estandar a un programa o viceversa puede ser redirigido a otro dispositivo, o a otro programa, o fichero, etc..

Desde la shell se puede hacer esto de varias formas.

Ejemplos

La orden siguiente no produce ningún resultado visible porque la salida estándar se redirige al dispositivo /dev/null. Este dispositivo es como un pozo sin fondo. A diferencia de una papelera de Windows no se puede recuperar luego nada.

$ date > /dev/null

Ahora un ejemplo curioso. El comando 'time' sirve para medir consumos de tiempos de otros comandos y para evitar mezclas de datos en salida estandar se decidió que la salida normal de time fuera la salida estandar de errores.

$ time whoami > /dev/null

Podemos ver el consumo de tiempo del comando 'whoami' porque este sale por la salida estandar de errores. Es un ejemplo de utilización de la salida estandar de errores para sacar información que no tiene nada que ver con ningún error. Sin embargo time es un comando interno y por ello lo siguiente no funcionaría como usted piensa.

$ time whoami 2> /dev/null

En este caso la redirección afecta solo al comando whoami.

Los comandos internos son parte de la shell y para redirigir su salida habría que redirigir la salida completa de la shell. Dejaremos esto para un capítulo posterior.

Antes de continuar vamos a asegurarnos que estamos en un sitio seguro para trabajar.

$ cd /tmp
$ mkdir pruebas > /dev/null
$ cd pruebas
# Para comprobar que estamos en '/tmp/pruebas' hacemos
$ pwd
/tmp/pruebas

El contenido de '/tmp' suele ser vaciado cuando rearranca la máquina o quizas en algún otro momento. Contiene información temporal. Hemos usado /dev/null para ignorar la salida de errores del comando mkdir. Si ya existía '/tmp/pruebas' mkdir habría dado un error pero nosotros lo ignoramos porque solo nos intersa que lo cree en caso de que no exista y en caso contrario da igual. El dispositivo '/dev/null' también resulta útil para simular una entrada nula de datos. Por ejemplo para crear un fichero vació. Si ya estamos situados en '/tmp/pruebas' pruebe lo siguiente:

$ cat < /dev/null > kk
$ ls -l kk

El mismo efecto podríamos conseguir usando.

$ > kk
$ ls -l kk

Esto se puede utilizar para vaciar ficheros respetando los permisos originales.

Vamos mirar el contenido del directorio raiz

$ ls -l

Ahora queremos repetir el comando pero guardando el resultado en el fichero kk

$ ls -l / > kk

No vemos nada porque toda la salida va a parar al fichero. Para visualizar el contenido de un fichero usaremos el comando 'cat'.

# Mostrar el contenido del fichero kk.
$ cat kk

# Equivale al anterior.
$ cat < kk

# Parecido a los dos anteriores. /dev/tty es un dispositivo que
# se identifica como nuestro terminal.
$ cat < kk > /dev/tty

También podemos visualizar la salida de un comando a la vez que guardamos la salida en un fichero. El comando tee actua como un bifurcación.

$ ls -l / | tee kk1
$ cat kk1

Recuerde que lo que sigue a un '|' ha de ser siempre un ejecutable.

S touch kk
$ date | kk

Esto habrá dado un error. Probemos ahora a redirigir la salida estandar de errores a un fichero y la salida estandar a otro.

S touch kk
$ date | kk 2> errores > salida
$ cat errores
$ cat salida

Existe un comando 'yes' que sirve para generar continuamente respuestas afirmativas en forma de caracteres 'y' y finales de linea. Este comando está pensado para ser usado con '|' y proporcionar entradas afirmativas a un comando. Usaremos el comando 'head' que sirve para sacar por salida estandar solo una primera parte de los datos de la entrada. La cantidad datos puede especificarse en número de lineas en bytes, etc.. Nosotros vamos a utilizar bytes. La salida de 'yes' la entregaremos a head para que tome los 1000 primeros bytes que pasamos al programas 'wc' para que cuente lineas palabras y caracteres.

$ yes | head --bytes=1000 | wc
500 500 1000
Tubería rota

El comando 'yes' podría funcionar eternamente. Su salida redirigida por las buenas a un fichero llenaría el disco (cosa nada desable por cierto) y acabaría dando un error. En lugar de esto lo hemos redirigido a un programa que aceptará solo los 1000 primeros caracteres y luego cerrará su entrada provocando un error de Tubería rota.

La shell también permite introducir datos en una forma especial que se llama documento-aqui. Para variar un poco usaremos ahora el comando 'sort' que sirve para ordenar. Observe que en este ejemplo un fin de linea no termina el comando. Por ello aparecerá el introductor secundario $PS2 que nosotros indicamos con un '>' en amarillo.

# Para ordenar unas lineas que introducimos en la propia linea de
# ordenes usamos el operador '<<' seguido de una clave de 
# finalización, de la entrada.
$ sort <<FIN
> aaaaa
> cccccc
> zzzzz
> bbbb
> yyyy
> FIN

aaaaa
bbbb
cccccc
yyyy
zzzzz

Operador grave:
Cuando colocamos algo dentro de las comillas graves '`' la shell lo interpretara como una cadena cuyo valor es sustituido por el resultado de ese comando que produce ese comando en salida estándar.

$ echo date produce --- `date` ---

date produce --- lun ene 10 20:59:12 CET 2000 ---

El resultado es solo un ejemplo y no puede coincidir exactamente con el resultado obtenido por usted ya que debería poner la fecha correcta en su sistema.

Quizas el ejemplo que acabamos de usar no le de una idea exacta de la potencia del operador grave. Recuerde que esto ocurre durante la expansión de la linea de ordenes antes de ejecutarse el comando echo. Por ello podemos hacer cosas como las siguientes.

# Ejecutamos date y guardamos el resultado en un fichero cuyo
# nombre construimos en base a nuestro nombre de usuario y al
# año de la fecha actual.
$ date > fichero-`whoami`-`date +%Y`
$ ls -l fichero-*
$ cat fichero-*

Hemos usado un comando para construir el nombre del fichero de salida. No mostramos los resultados pero confiamos en que ya los ha comprobado.

Caracteres de escape:
Dado que la Shell interpreta catarteres. Blancos como separadores. Asteriscos e interrogaciones como comodines. Operador grave. Comillas dobles, Comillas normales, $, etc... Se plantea el problema de que hacer cuando queremos usar estos caracteres como tales sin que sean interpretados por la shell. Existen varias formas de escapar caracteres para que la shell no los expanda, o interprete.

La shell no el el único programa con capacidad de expandir e interpretar caracteres especiales. Por ejemplo find, egrep, sed, y otros también interpretan ciertos caracteres que además pueden coincidir con algunos de los que interpreta la shell. Para usar caracteres especiales que pueden interpretarse por la shell habrá que escaparlos siempre que deseemos que lleguen al comando.

Vamos a ver tres formas distintas de escapar caracteres:

Vamos a ver todo esto con unos ejemplos:

# La orden siguiente muestra el mismo literal que el entre comillas.
$ echo "* * ? *"

# En este caso la shell interpreta `pwd` y $PATH, en cambio '*' y
# '?' no se interpretan.
$ echo "* ? `pwd` $PATH"

# En este caso se conserva todo el literal sin interpretar.
$ echo '* ? `pwd` $PATH'

# A continuación la orden mostrará dos comillas dobles.
$ echo \"\"

# El caracter <nueva-linea> también puede ser escapado y en este
# caso puede servir para introducir comandos muy largos de forma
# más legible. Para hacer esta prueba deberá pulsar la tecla de
# <nueva-linea> justo a continuación del caratcter '\'
$ ls -l / | \
head -n 10 | \
tee /tmp/resultado | \
wc

Ejercicio:
Las siguientes ordenes producen resultados cuya explicación no resulta trivial. Esto es debido a que permitmos con la opción -e que el comando 'echo' interprete caracteres. Por lo tanto lo que ocurre es que primero el interprete de comandos interpreta los caracteres de la linea de órdenes y luego el comando echo interpreta los caracteres recibidos. Consulte la página man de echo(1) y busque la opción -e. Ejecute los comandos que le proponemos a continuación e intente explicar los resultados. Puede que necesite papel y lapiz.

$ echo -e \n
n
$ echo -e \\n


$ echo -e \\\n


$ echo -e \\\\n
\n $ echo -e \\\\\n
\n $ echo -e \\\\\\n
\
$ echo -e \\\\\\\n
\
$ echo -e \\\\\\\\n
\\n

Resumen
Hemos visto muchas cosas que nos permiten hacernos idea de la potencia de la Bourne-Shell de Unix. La bash de Linux es un superconjunto. Es decir es compatible pero más potente.

Queremos resaltar que para las personas que han conseguido terminar este capítulo ya se ha conseguido algo importante. Personas acostumbradas al usos de Msdos cometen barbaridades que luego ni siquiera son capaces de explicar. La clave muchas veces está en la expansión de la linea de ordenes.

En realidad este tema no está agotado ni mucho menos y lo completaremos en capítulos posteriores aunque intercalaremos material más ligero porque no deseamos hacerle sufrir demasiado. Solo lo justo y por ello creo que es el momento de confesar que existía un truco para realizar más facilmente el ejercicio anterior aunque no lo mencionamos antes porque habría resultado demasiado fácil y cómodo. Se trata de activar la traza de la shell con 'set -x'. De esta forma podemos ver el resultado de la expansión de linea de órdenes precedida por el signo '+'.

$ set -x
+ set -x
$ echo -e \\\\\\\\n
+ echo -e '\\\\n'
\\n
$ set +x
+ set +x

Puede utilizar este truco cuando no sepa que está ocurriendo con la expansión de una linea de ordenes que no funciona como usted pensaba.

Test
Puede comprobar sus conocimientos respondiendo el siguiente test.
Para ello seleccione las opciones que se ajusten a la verdad y luego pulse el boton para ver el resultado de su test.

1 Un comentario consiste en un # al principio de la linea
2 man --help obtiene la lista de opciones que se pueden usar con el comando help
3 El orden de las opciones de un comando es indiferente
4 echo a*b lista los ficheros del sistema que empiezan por a y terminan por b
5 El operador | conecta la entrada y salida de dos procesos a través de un fichero temporal
6 echo "$PATH `date`" y echo '$PATH `date`' producirán idéntico resultado
7 echo "*" y echo '*' producirán idéntico resultado

Resultado del test

Si quiere hacernos llegar alguna duda, aclaración,
crítica, o contribución personal, utilice nuestro
formulario de contacto y nosotros le contestaremos
contacto

.
....
...

Volver a la página anterior
Volver a la página principal de la tienda