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). |
![]() |
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.
|
Introducción a la shell de Unix
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.
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:
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
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 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.
Expansión de la linea de orden:
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.
Ahora introduzca los comandos que indicamos a continuación y
compruebe la salida obtenida.
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.
A continuación no teclee nada ya que el resultado es solo una hipotesis.
Supongamos que obtenemos un error de este tipo.
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
o también.
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.
Peculiaridades de expansión en la bash de Linux
Redirección de entrada salida:
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.
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.
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.
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.
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:
El mismo efecto podríamos conseguir usando.
Esto se puede utilizar para vaciar ficheros respetando los
permisos originales.
Vamos mirar el contenido del directorio raiz
Ahora queremos repetir el comando pero guardando el resultado en
el fichero kk
No vemos nada porque toda la salida va a parar al fichero. Para visualizar
el contenido de un fichero usaremos el comando 'cat'.
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.
Recuerde que lo que sigue a un '|' ha de ser siempre un ejecutable.
Esto habrá dado un error. Probemos ahora a redirigir la salida estandar
de errores a un fichero y la salida estandar a otro.
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.
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.
Operador grave:
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.
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:
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:
Ejercicio:
Resumen
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 '+'.
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
Existen varias shells para Unix, Korn-Shell (ksh), Bourne-Shell (sh),
C-Shell (csh), y muchas más. Existen algunas para propó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 Korn-Shell y la Bash son distintos superconjuntos distintos
de la Bourne-Shell y por ello todo lo que se diga para la Burne-Shell
será válido también para la Korn-Shell y para la Bash.
$ ps | grep $$
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"
Son ficheros ejecutables. Para que la shell localice el
comando deberá estar en un subdirectorio que forme parte de la
variable PATH o de lo contrario debe especificarse el camino
completo.
Generalmente las opciones de un comando son letras precedidas de un
signo '-'. En algunos comandos se pueden poner varias opciones como
varias letras seguidas precedidas del signo '-'.
Algunas opciones pueden venir como '--<opcion>' y concretamente
en Linux es muy frecuente poder usar las opciones --help y --version
en casi todos los comandos.
Precisamente con la opción --help obtendremos generalmente la lista
de opciones disponibles para un comando.
También es frecuente el uso de opciones precedidas por '+'.
En algunos comandos el orden de las opciones es muy significativo.
No merece la pena hablar más de esto ahora porque vamos a usar un
montón de comandos con un montón de opciones en este curso y tendremos
ocasión de practicar mucho con ellas.
Tienen un significado especial para la shell y son uno de los
siguientes caracteres:
; & ( ) | > > <espacio> <tab>
|| & && ; ;; ( ) | <nueva-linea>
Son literales tomados como parámetros de entrada para
algún comando.
Todo lo que sigue al carácter '#' hasta <nueva-linea> será un
comentario.
Son palabras reservadas para implementar el lenguaje shell-script.
Son palabras reservadas:
case, do, done, elif, else, esac, fi, for, function, if, in,
select, then, until, while, time.
Comandos que están implementados dentro de la propia shell.
No necesitan PATH. Ejemplos de comandos internos son:
cd, exec, arg, eval, exit,...
$ # Orden con un único comando sin opciones ni argumentos
$ ls
$ ls . / ..
$ ls -l
$ # 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
$ ls --help
$ ls --version
$ ls --color=auto
$ date -d now -R
$ date -d -R now
$ date +'%a %b %e %H:%M:%S %Z %Y'
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.
Las lineas que empiecen con
$ comando ...
son comandos que debe introducir salvo que se trate de un comentario.
Las lineas en amarillo
salida del comando ...
son la salida obtenida.
su salida en amarillo pero si la ponemos es para que compruebe lo que debe obtener.
$ 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 *.
.kk
$ echo *.*
kk.txt kk.doc j2.txt
$ echo ???
kk1 kk2
$ echo kk?
kk1 kk2
$ ls *
ksh: /bin/ls: arg list too long
$ ls
$ ls .
$ ls | wc -l
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.
Este es un tipo de expansión que no tiene para nada en cuenta los ficheros
existentes en el directorio actual.
$ echo a{d,c,b}e
ade ace abe
Esta es una característica especial de la shell de Linux que resulta
realmente util. Expande la tilde como directorio home.
$ echo ~
$ echo ~root
$ echo ~root/*
Esto permite expandir expresiones $((expression))
$ echo $(((4+11)/3))
5
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.
Redirige la salida estándar a un fichero o dispositivo.
Redirige la entrada estándar tomándola desde un fichero.
Comunica dos procesos por medio de entrada salida.
Ojo no confundir con Msdos. En Unix los procesos comunican
directamente pasándose los datos directamente sin crear
ningún fichero temporal. El proceso que lee quedara en espera
mientras el el proceso que escribe mantenga abierto el
dispositivo de salida estándar incluso si momentáneamente no se
produce salida. Cuando el proceso escritor termina cierra todos
sus ficheros y el proceso lector acusará la condición como un
fin de datos de entrada.
Redirige la salida estándar a un fichero sin sobreescribirlo.
En lugar de ello añade al final del mismo.
Redirige entrada estándar desde la propia linea de ordenes.
(En lugar de FIN se puede usar cualquier literal).
Redirige la salida estándar de errores a un fichero o dispositivo.
Redirige la salida estandard de errores donde esta redirigido la
salida estandard. (0=entrada estandar, 1=salida estándar, 2=salida
de errores estándar)
$ date > /dev/null
$ time whoami > /dev/null
$ time whoami 2> /dev/null
$ cd /tmp
$ mkdir pruebas > /dev/null
$ cd pruebas
$ # Para comprobar que estamos en '/tmp/pruebas' hacemos
$ pwd
/tmp/pruebas
$ cat < /dev/null > kk
$ ls -l kk
$ > kk
$ ls -l kk
$ ls -l
$ ls -l / > kk
$ # Mostrar el contenido del fichero kk.
$ cat kk
$ cat < kk
$ # se identifica como nuestro terminal.
$ cat < kk > /dev/tty
$ ls -l / | tee kk1
$ cat kk1
$ touch kk
$ date | kk
S touch kk
$ date | kk 2> errores > salida
$ cat errores
$ cat salida
$ yes | head --bytes=1000 | wc
500 500 1000
Tubería rota
$ # 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
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 ---
$ # 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-*
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 orden siguiente muestra el mismo literal que el entre comillas.
$ echo "* * ? *"
$ # '?' no se interpretan.
$ echo "* ? `pwd` $PATH"
$ echo '* ? `pwd` $PATH'
$ echo \"\"
$ # 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
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
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.
$ set -x
+ set -x
$ echo -e \\\\\\\\n
+ echo -e '\\\\n'
\\n
$ set +x
+ set +x
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.
Si quiere hacernos llegar alguna duda, aclaración,
crítica, o contribución personal, utilice nuestro
formulario de contacto y nosotros le contestaremos