Tabla de Contenidos
Terminal o Intérprete de Órdenes
Como todos los sistemas operativos derivados de Unix, GNU/Linux dispone de un 'intérprete de órdenes
' o 'terminal
' (en inglés se utiliza la palabra shell
), que hace de interfaz entre el usuario y el propio sistema operativo.
Existen varios intérpretes de comandos del shell, sh (Bourne Shell, predecesor del BASH), el csh o tcsh (C Shell), ksh (Korn Shell), y mencionaré por último al 'bash
' (acrónimo de 'B
'ourne
'A'gain
'SH'ell
), utilizado por la distribución UBUNTU que yo utilizo.
Introducción
Un terminal es una forma de acceder al sistema sin utilizar la interfaz gráfica, es decir, realizar todo tipo de tareas en formato texto. La forma de utilizar el sistema de este modo es mediante órdenes.
El terminal muestra en pantalla un indicador de línea de órdenes (en inglés se utiliza la palabra prompt
que literalmente traduciría “pronto o listo” pero se puede definir como ayuda visual
o palabra que ayuda
) esperando que el usuario introduzca una orden.
Este indicador finaliza generalmente por un caracter '$
', cuando eres un usuario normal, o '#
' cuando eres un súper usuario (administrador).
El prompt
se puede configurar al gusto (ver este artículo) y que en UBUNTU muestra el siguiente aspecto:
usuario
@equipo
:directorio actual
$
El carácter '~' que usualmente verá al iniciar un terminal indica que está en el directorio /home/usuario. Es una especie de abreviatura.
Lo típico, cuando corren los comandos UNIX en general, es que no devuelvan nada en pantalla. Por lo tanto, la única manera de saber que un programa o comando terminó de ejecutar, es cuando vuelve a aparecer en pantalla el prompt 1)
Para acceder a una terminal se puede hacer de dos formas, una es con una aplicación como el terminal de GNOME, Xterm o Konsole de KDE, que son emuladores de la terminal dentro de una interfaz visual.
Mostramos a continuación una imágen de la GUI o emulador de terminal en interfaz gráfica, en este caso, el escritorio de GNOME.
Otra forma es salirse del entorno gráfico y acceder a un entorno completamente en modo texto, algo así como entrar en sólo símbolo de sistema en Windows 98.
Para esto último debemos teclear 'Control+Alt+F1
'.
Linux proporciona por defecto seis terminales de este tipo, de 'Control+Alt+F1
' a 'Control+Alt+F6
'.
Si queremos volver al modo gráfico lo hacemos con 'Alt+F7
'.
La interfase de línea de comandos o CLI, accedemos a ella a través de la Barra de Herramientas→Aplicaciones→Accesorios→Terminal.
Linux tiene al menos 2 interfases para comunicarse con el usuario la CLI (Command line interfase - interfase de línea de comandos) y la GUI (interfase de modo “grafico” - aunque la anterior “también” es gráfica).
La diferencia entre ambas, es que con la primera (CLI), se pueden hacer muchas cosas preestablecidas, pero aún más cosas que no están preestablecidas (veremos más adelante); sin embargo las GUI solo permiten realizar las tareas para las cuales están programadas, quitándole al sistema flexibilidad, sofisticación y eficiencia.
Entrada, salida y error estándar
Como bien se explica en el artículo sistema de ficheros, en Linux todo es un archivo.
Cada vez que ejecutamos una orden, el sistema operativo le abre automáticamente tres interfaces (en los sistemas operativos tipo UNIX se utiliza el término archivo):
- la entrada estándar,
- la salida estándar
- y el error estándar.
* La entrada estándar (stdin
) se refiere al archivo por el que una orden recibe su entrada (por defecto, es 'el teclado
').
* La salida estándar (stdout
) se refiere al archivo por el que una orden presenta sus resultados (por defecto, es 'la pantalla
' o más concretamente la ventana en la que se está ejecutando el intérprete de órdenes).
* El error estándar (stderr
) se refiere al archivo por el que una orden presenta los mensajes que va generando cuando ocurre un error (por defecto, también es 'la pantalla
').
Estos archivos se crean en el directorio /dev
.
Redirección
Antes de que se ejecute una orden, es posible redirigir cualquiera de sus archivos de salida, es la 'redirección
'.
Para llevarla a cabo es necesario utilizar los operadores de redirección que se procesan en el orden en el que aparecen:
Redirección de la entrada estándar - STDIN
Cuando se quiere redirigir la entrada estándar de una orden a un archivo, es necesario utilizar el operador de redirección ''<'
' seguido del nombre del archivo.
En este caso, una orden lee los datos de entrada que necesita desde el archivo señalado, en vez de desde el teclado.
Redirección de la salida estándar - STDOUT
La salida por defecto de cualquier orden dada en el bash es el monitor. Por ejemplo; si utilizas la orden cal, te mostrará un calendario en la pantalla. Sin embargo puedes hacer que te envíe esos datos a un documento de texto por ejemplo escribiendo lo siguiente:
$ cal 2006 > mitexto.txt
vemos como se utiliza el operador ' '>'
' para establecer que la dirección de la salida será un archivo de texto llamado mitexto.txt, si el archivo no existe se crea, y si existe se vacía antes de hacer la redirección.
Para que al redireccionar no borre el contenido de un archivo que ya existe, es decir, que se agregue el calendario al final del archivo, se debe utilizar el operador ' '»'
'.
Redirección del error estándar - STDERR
Cuando se quiere redirigir el error estándar de una orden a un archivo, es necesario utilizar el operador de redirección ' '2>'
' seguido del nombre del archivo.
Como en la salida estándar, en el caso de no existir el archivo se crea, y en el caso de existir, el archivo se vacía antes de hacer la redirección.
En el caso de que se quiera añadir el error estándar de una orden sin borrar el contenido de un archivo que ya existe, el operador de redirección a utilizar debe ser ' '2»'
'.
Por ejemplo, si introduces en el terminal:
$ sadfasgdgfadfgsd
Te mostrará un error por pantalla ya que la orden no existe.
Si redireccionamos el error estándar a un archivo:
$ sadfasgdgfadfgsd 2> error.txt
El error ahora no se muestra por pantalla, como si nada pasara; se guarda en el archivo error.txt
.
Uniendo las salidas
Utilizando Bash 2)se puede usar >out.txt, para redireccionar stdout al archivo out.txt, mientras que stderr sigue saliendo en la consola.
Pero si hacemos:
comando>out.txt 2>err.txt
las salidas de error van a parar al archivo err.txt.
Ahora bien, si hacemos:
comando>out.txt 2>&1
entonces todos los mensajes que salen por stdout como por stderr, van a parar a out.txt
Con 2>&1 le estamos diciendo al sistema operativo que use la misma unidad de salida para stderr como para stdout.
Las salidas también se pueden bifurcar (comando tee), o se pueden encadenar utilizando tuberías o pipes, como se explica en el apartado Archivos de órdenes o ''scripts".
Los comodines
Los comodines son unos caracteres especiales que pueden sustituir a nombres y a partes de nombres de los 'archivos y/o directorios
'.,
Esto facilita especificar múltiples nombres como argumentos de una orden.
'*': sustituye cualquier secuencia de caracteres.
'?': sustituye un único caracter.
Variables
Como en cualquier lenguaje de programación, se pueden utilizar variables en las órdenes. No hay ninguna necesidad de declarar la variable. Ésta se crea automáticamente al asignarle cualquier valor.
Por ejemplo: $ Ejemplo="Esto es un ejemplo" ; echo $Ejemplo
El caracter ' ';'
' se explica en Estructuras de control de flujo.
El comando 'echo
', muestra por la salida estándar, cadenas de texto separándolas con un espacio.
Hay que tener en cuenta que:
* No debe aparecer ningún espacio entre el nombre de la variable, la asignación ('=
') y la cadena.
* La referencia a cualquier variable se hace poniendo el símbolo '$' delante de su nombre.
NOTA : Como se puede observar para poder asignar la cadena a la variable ''Ejemplo'', hemos tenido que utilizar las dobles comillas. Si no lo hubiésemos hecho (escribiendo ''$ Ejemplo=Esto es un ejemplo'') habríamos obtenido un error ya que el intérprete de órdenes sólo habría asignado ''Esto'' a ''Ejemplo'' y habría intentado ejecutar la palabra ''es'' como una orden.
Existen varios tipos de entrecomillados:
* Las dobles comillas se utilizan normalmente para las cadenas pero hay que destacar que el caracter especial ‘$’ es interpretado entre las dobles comillas cuando viene seguido de un nombre de variable, dando lugar a la sustitución de la variable por su valor:
* $ var=5 ; echo “Aparece el valor $var”
* Entre las comillas simples, no es interpretado ningún carácter especial, y se escribe tal cual lo que hay entre ellas:
* $ var=5 ; echo 'No aparece el valor $var'
* Podríamos obtener el mismo resultado utilizando doble comillas, si ponemos delante del carácter especial ‘$’ el carácter de escape ‘\’.
* $ var=5 ; echo “No aparece el valor \$var”
* Las comillas simples inversas hacen que se ejecute la orden que contienen, asignando el resultado de la ejecución a la variable:
* $ var=`date` ; echo “Hoy es $var”
Variables de entorno
Existen variables asignadas por el sistema operativo, desde que se inicia una sesión hasta que la cerramos.
Estas variables pueden ser utilizadas desde cualquier terminal.
Para ver las todas las variables de entorno definidas, se utiliza la orden 'env
' sin argumentos:</
$ env
Éstas son algunas de las variables que más se usan:
* 'HOME
': ruta de nuestro directorio personal.
* 'USER
': nombre de usuario asignado.
* 'SHELL
': ruta al intérprete de órdenes que se ejecuta por defecto.
* 'HOSTNAME
': nombre asignado al equipo.
* 'PATH
': rutas en las que el intérprete busca las órdenes a ejecutar cuando no especificamos donde están.
Debemos3) dejar claro que las variables de entorno son propias del entorno en el que estamos, por lo tanto quedan, son propias del entorno.
Ahora bien, hay un tema muy importante y sutil, que es el alcance de las variables de entorno.
Hay dos tipos de variables de entorno :
- las que podríamos definir como locales
- y las que podríamos definir como globales
Las locales solo son accesibles a los comandos que se ejecutan en el shell local, o el shell que nos está atendiendo.
Las globales se trasmiten de padres a hijos… ok, se complicó no ??, procesos padres… hijos…
En realidad cuando nos referimos a procesos hijos en LINUX, nos referimos a los procesos que son resultado de otro proceso del cual se genera.
Por ejemplo, si corro un comando ls, el shell es el proceso padre y el ls es el proceso hijo, de hecho, lo que en realidad ocurrió internamente es un fork(bifurcación). Lo que hace LINUX cuando larga a correr un programa, es lo siguiente :
Primero duplica el proceso que está ejecutando, por ejemplo, si es un shell, lo duplica ejecutando la función fork() del sistema, y luego el proceso hijo realiza un exec() con lo cual se transforma en el comando ls que es lo que queríamos ejecutar.
Al ejecutar un fork(), el proceso original se duplica, pero cada uno sabe si es padre o es hijo; y es el hijo el que finalmente se transforma en el nuevo proceso que se quería ejecutar.
En el proceso de ejecución del fork(), se trasmiten sólo las variables globales que son las variables que propiamente uno dice que son las variables de entorno.
Para que una variable sea de entorno, uno debe exportarla. Uno puede primero crear la variable
pepe=123
y luego exportarla,
export pepe
o se pueden hacer las dos cosas juntas,
export pepe=123
De esa manera, los procesos hijos del proceso donde se creó la variable pepe, también podrán disponer de esa variable. Y ese, es el mecanismo más sencillo de comunicación entre procesos.
Uno puede invocar un nuevo programa, pasándole argumentos, o simplemente creamos un conjunto de variables de entorno con los datos que el nuevo proceso requiere y el nuevo proceso los toma allí.
Órdenes
En una orden simple, la primera palabra especifica la orden a ser ejecutada y las palabras restantes se pasan como opciones y argumentos de la orden:
$ '''orden [''' opciones '''] [''' argumentos ''']'''
ADVERTENCIA : Tanto para archivos, ficheros, órdenes y opciones de órdenes, las '''mayúsculas son determinantes'''.
Las órdenes simples devuelven un valor que representa su estado de salida. Si no ha habido ningún error, el valor devuelto es un 0, en caso contrario, devolverá un valor distinto de 0.
Podemos ver en todo momento cual es el valor devuelto poniendo la orden:
$ echo $?
Es decir, se almacena en la variable '?
'.
La mayoría de las órdenes disponen de una ayuda en forma de manual.
Para acceder a este tipo de ayuda basta con introducir en el terminal:
$ man '''orden'''
Si bien todas las órdenes y programas disponen de una breve ayuda y/o descripción cuando se le pasa como opción '-h
' o '–help
'.
CONSEJO: En general; ''si no se ha trabajado mucho con una orden, es muy conveniente leer la página del manual antes de ejecutarla''.
Navegación e interacción con ficheros
* Cambiar el directorio de trabajo ('c
'hange 'd
'irectory):
$ cd <opciones> <ruta>
* Listar el contenido de directorios ('l
'i's
't):
$ ls <opciones> <ficheros>
* Crear un directorio ('m
'a'k
'e 'dir
'ectory):
$ mkdir <opciones> <nombre del directorio>
* Borrar un directorio ('r
'e'm
'ove 'dir
'ectory):
$ rmdir <opciones> <nombre del directorio>
* Copiar un archivo o directorio en el directorio especificado ('c
'o'p
'y):
$ cp <opciones> <archivo> <ruta>
* 'M
'o'v
'er un archivo o directorio a un archivo o directorio.
Se utiliza también para renombrar; el origen y destino es un archivo ('m
'o'v
'e):
$ mv <opciones> <archivo> <ruta>
* Borrar archivos o directorios ('r
'e'm
'ove):
$ rm <opciones> <archivo>
:Si un directorio no está vacío no es posible borrarlo con la orden 'rmdir
'.
La orden 'rm
' nos lo permite con la opción '–R
' borrando el contenido del directorio especificado y todos sus subdirectorios de forma recursiva (ver su página del 'man
'ual).
ADVERTENCIA : Hay que tener mucho cuidado con la orden: $ rm -R <directorio> Ya que borra todos los archivos y directorios dentro del directorio a borrar.
* Mostrar por pantalla el contenido de los archivos por la salida estándar.
Si no se especifica ningún archivo, la orden repite todo lo que se ha escrito por la entrada estándar.
Para indicar que se ha acabado de escribir hay que pulsar 'CTRL+D
'.
$ cat <opciones> <archivos>
* Buscar archivos recorriendo árboles de directorios especificados por las rutas, evaluando de izquierda a derecha las condiciones de búsqueda.
Además se pueden especificar acciones sobre los resultados obtenidos:
$ find <ruta(s)> <condición(es) de búsqueda> <acciones>
* Mostrar el nombre del directorio en el que uno se encuentra situado ('p
'rint 'w
'orking 'd
'irectory).
Se dice que ese directorio es el directorio de trabajo
:
$ pwd <opciones>
* Cambiar los permisos de un archivo.
Estructuras de control de flujo
Listas de órdenes
Una lista de órdenes es una secuencia de una o más tuberías separadas por uno de los operadores ‘;’, ‘&’, ‘&&’ o ‘||’ y terminada opcionalmente por ‘;’, ‘&’ o (nueva línea).
Archivos de órdenes o ''scripts''
== Tuberías ==
Una tubería es una secuencia de una o más órdenes separadas por el carácter '|'. En este caso la salida estándar de una orden se conecta con la entrada estándar de la siguiente orden, por eso el nombre de tubería.
El intérprete de órdenes espera a que todas las órdenes de la tubería terminen antes de presentar un resultado por la salida estándar. El estado de salida de una tubería corresponde al estado de salida de la última orden.
Asociado a las tuberías se suelen utilizar una serie de órdenes que se utilizan como filtros (por ejemplo la orden grep
). Ej: mostrará sólo los ficheros que no sean directorios:
$ ls -l | grep "^-"
== Caso de estudio ==
Un ejemplo práctico del uso de las tuberías o pipes es el siguiente 4):
Se puede encadenar la salida de un comando a la entrada de otro (tal como lo vimos anteriormente), y esto se realiza mediante las pipes o tuberías representadas por el carácter |.
comando1|comando2|comando3
De esta manera, la salida de comando 1, ingresa por el stdin de comando 2 y la salida de comando 2 ingresa por el stdin de comando 3.
Esto permite, encadenar comandos que realizan diferentes tareas, para, de esa manera, ampliar la capacidad de procesamiento de la computadora.
En definitiva, estamos armando pequeños programas, que hacen cosas que no estaban previstas anteriormente. De ahi, surge la filosofía de UNIX de disponer de comandos sencillos, que hagan bien una cosa.
Al encadenar comandos con pipes, extendemos las posibilidades haciendo cosas que los comandos por separados no pueden hacer.
Vayamos pues al ejemplo:
Supongamos que tenemos un archivo de texto palabras.txt que contiene las siguientes palabras :
texto, anexo, pretexto, contexto, sujeto, predicado, texto, sujeto, sujeto, anexo, panfleto, anexo.
El comando SORT nos permite ordenarlo alfabéticamente :
SORT palabras.txt
del cual se obtiene el siguiente resultado :
anexo
anexo
anexo
contexto
panfleto
predicado
pretexto
sujeto
sujeto
sujeto
texto
texto
para tener las cosas más claras podemos utilizar el comando UNIQ, que saca cada palabra una sola vez, porque en definitiva lo que hace UNIQ, es eliminar las líneas repetidas.
Haciendo una tubería obtenemos :
SORT palabras.txt |UNIQ
anexo
contexto
panfleto
predicado
sujeto
texto
pero, si usamos UNIQ con la opción -c, coloca en la salida y junto a cada palabra, la cantidad de veces que esa palabra aparece en el texto :
SORT palabras.txt |UNIQ -c
3 anexo
1 contexto
1 panfleto
1 predicado
1 pretexto
3 sujeto
2 texto
Pero ahora, podemos volver a ordenarlo, pero numéricamente (para eso usamos la opción -n del SORT), para poner al principio las palabras menos frecuentes y al final las más frecuentes.
SORT palabas.txt | UNIQ -c | SORT-n
1 contexto
1 panfleto
1 predicado
1 pretexto
2 texto
3 anexo
3 sujeto
Esto, a su vez lo podemos unir con el comando GREP. que permite buscar expresiones regulares dentro de archivos de texto o desde stdin.
Lo podemos unir a la tubería que estuvimos viendo para extraer y numerar palabras que contengan un determinado patrón, por ejemplo, las palabras que contengan “texto”.
SORT palabras.txt | UNIQ -c | GREP texto | SORT -n
1 contexto
1 pretexto
2 texto
Podemos unir, todo esto, al comando SED (Stream Editor) que permite cambiar o modificar el contenido del texto de un archivo. Por ejemplo, podemos cambiar “texto” por “verso”.
SORT palabras.txt | UNIQ -C | GREP texto | SED 's/texto/verso/pg' | SORT -n
1 converso
1 preverso
2 verso
Apasionante no, pues bien, vayamos a otro ejemplo :
El comando DU nos informa de la ocupación de un directorio determinado (disk usage). Si lo unimos con el comando SORT, podemos listar los directorios en orden ascendente de ocupación :
du -sk* | sort -n
Estos, son apenas unos ejemplos mínimos que apenas arañan las posibilidades reales. Cada uno de estos comandos posee varias opciones, con lo que las posibilidades se multiplican, y en especial, comandos como SED, GREP, o AWK, los cuales poseen opciones o capacidades de programación con ellos que hacen crecer exponencialmente las posibilidades de procesamiento de datos de desde la consola LINUX, empleando simples comandos y con mínima programación.
Por esta razón es que los “linuxeros” son tan afectos a guardar logs y datos en simples formatos de texto ASCII, pues las herramientas con que cuentan son tan sofisticadas, que pueden literalmente hacerlo todo desde la línea de comando.
Si pensamos que existen miles de estos comandos en línea, uno apenas se da cuenta del verdadero potencial que encierra LINUX.
Named Pipes - otro caso de Tuberías
Las tuberías o pipes 5)de las que hablamos, que permiten conectar stdout de un proceso, con el stdin de otro, se puede decir que son pipes anónimos, pues existen entre estos dos procesos y mientras estos procesos se están ejecutando.
Cuando termina la ejecución desaparecen, pero como se mencionó, todo en LINUX son archivos (o casi todo), se pueden crear pipes con nombre en el filesystem de LINUX y esos pipes, pueden ser usados en procesos para comunicarse entre si.
Un nuevo ejemplo muy sencillo :
Primero abrimos 2 consolas (terminales), en una de ellas creamos un named pipe con el comando,
mkfifo pepe
el comando se llama mkfifo, pues los pipes son todos FIFO (first in, first out), o sea, que lo primero que entra a un pipe, es lo primero que sale por la otra punta.
Si hacemos un ls -l veremos que apareció un nuevo archivo pepe, y que tiene como tipo de archivo una p.
prw-r--r-- 1 ken ken 0 2012-01-08 21:43 pepe
Vamos a poner a uno de los shells, a leer datos de ese archivo (pepe), para la cual escribimos en la otra consola :
while read linea do echo $linea done <pepe
Como veremos, el prompt no reaparece, pues estamos en un loop infinito, donde el shell simplemente está esperando que aparezcan líneas de texto en el archivo pepe y cada vez que aparece una línea de texto la lee y la imprime por pantalla.
Ahora cambiemos a la otra consola (o shell) y ejecutemos el comando siguiente :
cat palabras.txt>pepe
O sea, volcamos el contenido del archivo palabras.txt al archivo pepe.
El prompt reaparece y no pasa nada… en esa consola, pero vayamos a la otra; veremos que apareció en pantalla el contenido de texto del archivo.
Esta es una manera muy eficaz de comunicar datos entre procesos que no estén vinculados a través del mismo shell. Se puede tener un proceso que simplemente espera leer datos de un named pipe, y otro procesos que simplemente escriben datos allí y el proceso que lee el pipe procesa esos datos.
¡Simple y brillante! ¿No?
Ver también
Eso corresponde a una ejecución en “FOREGROUND” (digamos una ejecución interactiva en la cual el shell no puede hacer otra cosa que esperar a que termine el comando).
Pero, si al invocar el comando se agrega un & al final antes del ENTER, el comando se ejecuta de “BACKGROUND” (de fondo), y reaparece inmediatamente el prompt, por lo que se puede seguir usando el shell mientras el comando que se largó en background sigue procesando cosas.