El shell es el intérprete de comandos. En DOS normalmente el shell es el command.com, en UNIX existen muchos shell usados habitualmente.
Ofrece las mismas capacidades que csh, pero incluye funciones avanzadas, tanto para el usuario como para el programador.
En particular, podremos acceder a un historial de los comandos ejecutados, que se conserva incluso al pasar de una sesión a otra, utilizando los cursores.
Además, completa los nombres de comandos y archivos automáticamente, al presionar la tecla TAB.
Hay muchas otras versiones de shell además de éstas. Pero el estilo general de todos es muy similar. Lo que estudiemos en este curso será compatible con la mayoría de ellos.
Además de ejecutar los comandos que nosotros le indicamos, el shell interpreta ciertos caracteres especiales, a estos caracteres los llamamos metacaracteres.
Cuando nosotros utilizamos algún metacaracter, los comandos no lo reciben, sino que el shell lo reemplaza por lo que corresponda, y le pasa al comando ejecutado el resultado de ese reemplazo.
Eso es lo que entendemos por interpretar: reemplazar el caracter por otro caracter o por una cadena de caracteres, según corresponda.
*
, lo reemplaza por una lista de
los archivos que concuerdan con la expresión indicada.
Por ejemplo: echo *
nos mostrará todos los archivos del directorio. echo a*
nos mostrará
todos los archivos del directorio que comiencen con a. echo *o
nos
mostrará todos los archivos que terminen con o. echo /usr/local/*
nos mostrará todos los archivos que estén en ese directorio.
En el caso de que no hubiera ningún archivo que concuerde con la expresión, generalmente, nos mostrará la expresión que hayamos escrito.
?
el shell lo reemplaza por cualquier otro
caracter. Es decir que la expresión que escribamos se reemplazará por todos
los archivos que en esa posición tengan cualquier caracter, y en el resto de
la cadena tengan lo que hemos escrito.
Por ejemplo: echo ?ola
nos
podría mostrar archivos como hola
, sola
, Pola
.
echo a??a
, podría mostrar alla
, arca
, asia
.
Al igual que con el *
, si ningún archivo concuerda con el patrón,
generalmente, nos mostrá la misma expresión que hemos escrito.
Por ejemplo,
ls [af]*
nos mostrará todos los archivos que comienzan con a o con f.
Podemos además especificar un rango de caracteres, con un guión en el
medio. Por ejemplo, a-z
(letras minúsculas), 0-9
(números),
etc. y combinarlos con caracteres individuales siempre que no sea ambigua la
interpretación. (Considerar la concordancia con el caracter -).
Por ejemplo, podemos querer sólo los archivos que comienzan con números
seguidos de un -, en ese caso escribiríamos ls [0-9]-*}
o
ls [0-9][0-9]-*
, si comienzan con dos números seguidos de un -.
^
, estamos indicando que debe
concordar los caracteres que no se encuentran en el rango.
Por ejemplo, ls [^0-9]*
, nos listará todos los archivos que no
comiencen con un número.
Ejecutar un comando es tan sencillo como escribir el comando y apretar ENTER. Sin embargo, utilizando algunos de los metacaracteres de shell podemos combinar los comandos entre sí, y lograr resultados mucho más importantes.
;
es un separador de comandos, nos permite ejecutar un
comando a continuación de otro, equivalente a lo que sucedería si
ejecutáramos primero uno, y al terminar ejecutáramos el siguiente.
Es decir si escribimos ls; echo Hola
veremos la salida del echo
a continuación de la del ls
.
&
manda el comando a background, esto quiere decir, que nos
devuelve la línea de comandos inmediatamente despues de apretar Enter,
mientras el comando sigue ejecutándose en segundo plano.
La ejecución de tareas en segundo plano ya se ha estudiado anteriormente,
cuando se vieron los comandos relacionados con procesos. Este metacaracter
funciona de manera equivalente, y sus resultados pueden corroborarse
utilizando el comando jobs
.
Para ver un ejemplo, vamos a usar un nuevo comando, sleep
, (un comando
simple que espera una determinada cantidad de segundos). Por ejemplo
sleep 5
, espera 5 segundos antes de devolvernos la línea de comandos.
Ahora, utilizando &
: (sleep 20; echo Hola) &
. Al
escribirlo nos mostrará el PID del comando que estamos ejecutando, y nos
devolverá el shell; 20 segundos después, veremos aparecer "Hola" en
nuestra línea de comandos.
Antes de que termine de ejecutarse, podemos ejecutar jobs
y observar
que el proceso se está ejecutando, o bien ps
y observar que el
comando en ejecución es sleep.
Ejercicio:
hacer un comando equivalente que nos avise que ya pasaron los cinco
minutos necesarios para que hierva una pava de agua.
Además, el &
nos puede servir para separar comandos: cada vez que lo
utilizamos para separar comandos, mandará al comando que esté a su izquierda
a background.
Por ejemplo, echo '* ?* [A-Z-]*' nos mostrará * ?* [A-Z-]*.
Notar que si no cerramos las comillas y apretamos ENTER, el shell nos mostrará una línea en blanco esperando que sigamos ingresando nuestro comando, hasta que cerremos las comillas.
\
para escapar el
siguiente caracter. Escapar
significa que el shell no lo interpretará como un metacaracter.
Por ejemplo
echo \*
nos mostrará un *
.
#
es el señalador de comentario. Si el shell encuentra
un # al comienzo de una palabra, descartará todos los caracteres hasta en final de
línea. Por ejemplo, echo 3.1416 # Pi con un error de 0.0001
nos mostrará únicamente 3.1416
.
UNIX tiene un extenso manejo de entrada y salida, es una de las características principales que nos permite combinar pequeñas herramientas para lograr resultados más complejos.
La mayoría de los comandos UNIX que nosotros utilizamos tienen una entrada estándar, una salida estándar y una salida para errores estándar. Las denominamos stdin, stdout y stderr respectivamente.
La entrada estándard por omisión es el teclado, mientras que la salida estándard y la salida de errores son, por omisión, la pantalla.
Un comando genérico, lee datos de la entrada estándar, los procesa de alguna manera, y luego emite el resultado por la salida estándar. En el caso de que durante el proceso hubiera algún error, emitirá una aviso de ese error por la salida de errores.
El Shell se encarga de relacionar estos tres, lo cual no impide que un determinado programa maneje su entrada y su salida de una manera diferente.
>
nos permite direccionar la salida estándar de un
comando a un archivo. De manera que ps ax > procesos
guardará en el
archivo procesos
la salida del ps.
<
nos permite direccionar la entrada estándar de un
comando desde un archivo. Por ejemplo, el comando mail
nos sirve
para mandar mensajes a otros usuarios, si escribimos
mail user < archivo
mandará un mensaje con el contenido del archivo al usuario user.
>>
en lugar de un >
nos permite direccionar la salida
estándar a un archivo, sin sobreescribirlo, sino que le agrega los datos que
nosotros queramos al final. Si ahora hacemos ps ax >> procesos
tendremos el listado de procesos dos veces en un mismo archivo.
2>
nos permite redirigir la salida de errores a
un archivo. Por ejemplo, si ejecutamos ls archivo-feo 2> test
,
el error del ls
,
indicándonos que el archivo-feo no existe se almacenará en test.
|
. Por ejemplo, podemos
relacionar la salida de ls
con la entrada de wc
. Haciendo
ls | wc
, la salida de este comando será la cantidad de líneas,
palabras y caracteres que produjo ls
.
Este comando recibe el nombre de pipe, que en inglés significa cañería o tubería. Es decir que es un comando que entuba la salida de un comando con la entrada de otro.
Es interesante observar lo que sucede cuando hacemos:
ls > nuevo-archivo
,
esto es, el archivo nuevo aparece dentro del listado que hace ls. Esto se debe
a que el shell, al hacer la relación entre el archivo y el comando, crea el
archivo, y luego llama al ls
.
Además es necesario tener en cuenta que un comando no puede utilizar como
entrada y salida un mismo archivo. Por ejemplo, al ejecutar
cat archivo > archivo
,
el intérprete de comandos nos indicará que esto no es posible.
ls *
y echo *
.
ls /
y echo /
.
a.txt
, que contenga hola
, y
b.txt
,
que contenga chau
. Luego concatenarlos en un archivo ab.txt
.