LINUX - El intérprete de órdenes II Objetivos: Se pretende introducir al alumno en el uso del intérprete de órdenes bash de LINUX. Ejecución de órdenes en el intérprete de órdenes Orden simple Como ya vimos en el guión de la primera práctica, una orden simple es una secuencia de palabras separadas por espacios y redirecciones y que terminan con un operador de control (normalmente un retorno de carro). La primera palabra especifica la orden a ser ejecutada y las palabras restantes se pasan como argumentos a la orden. 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 $? (SUGERENCIA: probarlo después de cada ejemplo). Tuberías 1.- Utilizar la orden find para encontrar en el directorio /etc (y sólo en este directorio) aquellos archivos que empiezan por la letra s. Comprobar el estado de salida de la orden. Comprobar el estado de salida de la orden si se permitiera recorrer todo el árbol de directorios a partir del directorio /etc. Explicar porque se obtiene dicho resultado. 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. 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. Orden: grep [opciones] patrón [archivo] Esta orden muestra la línea de los archivos que concuerdan con un patrón. Ejecución: ls l /usr/bin grep mail La primera orden da una lista detallada del contenido del directorio /usr/bin. La segunda orden grep coge esa lista como entrada estándar y saca por pantalla sólo aquellas líneas que contengan la palabra mail. 2.- La orden ps permite ver aquellos procesos que tenemos en marcha en el momento de ejecutar la orden. La opción A de esta orden además permite ver todos los procesos que está ejecutando la máquina sin distinción de propietarios. Listar de todos esos procesos que se obtienen sólo aquellos que sean bash. 1
Orden: cut [opciones] [archivo] Esta orden muestra por pantalla parte de cada línea de los archivos. Ejecución: cat /etc/passwd cut c1-10 Se le pasa a la orden cut el contenido del archivo /etc/passwd y ésta solo muestra por pantalla los 10 primeros caracteres de cada línea. 3.- Con la ayuda de la orden date mostrar únicamente la hora en la que estamos, eliminando la información adicional proporcionada como el día de la semana, día del mes, etc Orden: sort [opciones] [archivo] Esta orden se utiliza para ordenar alfabéticamente las líneas de los archivos de texto presentando el resultado por la salida estándar. Utilizado en una tubería ordena el resultado de la orden anterior. Ejecución: find /usr/bin -name "a*" -size +10k -size -50k sort Con la primera orden realizamos la búsqueda de aquellos archivos que están en el directorio /usr/bin y sus subdirectorios que empiecen por a y midan entre 10 kbytes y 50 kbytes. El resultado de la búsqueda es lo que se pasará como entrada estándar a la siguiente orden sort. En pantalla aparecerá la lista resultado de la búsqueda ordenada alfabéticamente. 4.- Ordenar los archivos del directorio de trabajo ordenándolos por tamaño. Orden: uniq [opciones] [entrada[salida]] Esta orden descarta aquellas líneas idénticas sucesivas (quedándose una sólo) que le lleguen por la entrada, escribiendo el resultado por la salida. Ejecución: cat ejemplo-uniq sort uniq La orden cat entrega a la orden sort como entrada estándar el contenido del archivo ejemplo-uniq. La orden sort ordena las líneas del archivo para que finalmente la orden uniq elimine aquellas líneas que están repetidas dejando una copia de cada una de ellas. Para este ejemplo es necesario crear un archivo con el nombre ejemplo-uniq (lo podéis hacer con la orden cat). Su contenido debe ser el siguiente: frase que empieza por la letra f otra frase que empieza por o expresión que empieza por la letra e frase que empieza por la letra f frase que empieza por la letra f otra frase que empieza por o expresión que empieza por e Las 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). 2
De los operadores de la lista, && y tienen igual precedencia, seguidos de ; y &, que también tienen igual precedencia entre ellos. Las órdenes separadas por ; se ejecutan secuencialmente. El resultado es el mismo que escribir una orden detrás de otra acabándolas con un retorno de carro. La diferencia está en que de esta forma se nos permite escribir todas las órdenes que queremos ejecutar en una sola línea. El intérprete de órdenes espera a que cada orden acabe antes de pasar a la siguiente. Ejecución: date ; sleep 5 ; date En este ejemplo, primero se ejecuta la orden date que nos muestra por la salida estándar la fecha y la hora actuales. Una vez finalizada la ejecución de esta orden, se ejecuta la siguiente sleep 5. Esta orden hace una pausa de 5 segundos. Una vez finalizada esta pausa, pasamos finalmente a la última orden que muestra de nuevo la fecha y la hora actuales donde podemos apreciar que han pasado esos 5 segundos. Las órdenes que terminan con el operador de control & son ejecutadas en segundo plano. En estos casos el intérprete de órdenes no espera a que la orden se acabe. Ejecución: echo primero ; date ; (sleep 5 ; echo segundo ; date)& echo tercero ; date Como en el ejemplo anterior aquellas órdenes que están separadas por ; se ejecutarán una detrás de otra tras la finalización de la anterior. Sin embargo en este ejemplo tenemos un conjunto de órdenes que se van a ejecutar en segundo plano: aquellas que están encerradas entre paréntesis y que finalizan con un & (el hecho de que estas órdenes estén entre paréntesis implica que un subintérprete de órdenes llamado por el intérprete al que le hemos dado la lista de órdenes se encargará de ejecutarlas). Esto hace que en la salida estándar aparezca la cadena primero seguido del resultado de la primera orden date; a continuación se pondrá en segundo plano la orden sleep (indicado por un mensaje del estilo [1] num) que provoca que el subintérprete haga una pausa de 5 segundos antes de seguir con las órdenes que siguen entre paréntesis. Debido a que esto se hace en segundo plano, el intérprete de órdenes seguirá con la ejecución de las órdenes pasando a ejecutar la orden echo tercero seguido de la tercera orden date (aquí se puede apreciar que la primera y la tercera orden date dan la misma información ya que el tiempo que transcurre entre ellas es muy pequeño). Si no hacemos nada, al cabo de los 5 segundos que dura la pausa, veremos entonces que aparece la cadena segundo seguido del resultado de la segunda orden date. Si se pulsa la tecla de nueva línea, veremos que aparece el siguiente mensaje: [1] + done ( sleep 5 ; echo segundo ; date ) indicando que ha finalizado la ejecución de las órdenes en segundo plano. Los operadores de control && y denotan listas AND y OR respectivamente. Una lista AND tiene la forma: orden1 && orden2 En este caso, orden2 se ejecuta si y sólo si orden1 devuelve un estado de salida 0 (es decir, si tiene éxito). Ejecución: cd prueba && tar -cf /home/alumnos/paquete.tar * Si el directorio prueba existe y se produce el cambio de directorio entonces se empaquetará en un archivo tar el contenido del directorio. NOTA: Aseguraros primero que hay archivos en el directorio que se puedan empaquetar sino aparecerá un aviso de error. Una lista OR tiene la forma: orden1 orden2 3
En este caso, orden2 se ejecuta si y sólo si orden1 devuelve un estado de salida distinto de 0 (es decir, si no tiene éxito). Ejecución: cd temp echo No existe el directorio Si el directorio temp no existe, la orden cd no se podrá ejecutar. En ese caso aparecerá el mensaje No existe el directorio. 5.- Escribir una lista de órdenes que intente entrar en el subdirectorio temp, y en el caso de no existir, crearlo. Uso de 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. Ejecución: CAD= Hola mundo ; echo $CAD Lo primero que se hace en este ejemplo es crear la variable CAD asignándole la cadena Hola mundo. Después, la orden echo muestra la cadena por la salida estándar. Hay que tener en cuenta dos aspectos muy importantes: - 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 Como se puede observar para poder asignar la cadena a la variable CAD hemos tenido que utilizar las dobles comillas. Si no lo hubiésemos hecho (escribiendo CAD=Hola mundo) habríamos obtenido un error ya que el intérprete de órdenes sólo habría asignado Hola a CAD y habría intentado ejecutar la palabra mundo como una orden. Existen varios tipos de entrecomillados: - Las dobles comillas se utilizan normalmente para las cadenas pero hay que destacar que el carácter 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. Ejecución: 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. Ejecución: 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 \. Ejecución: 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. Ejecución: var=`date` ; echo Hoy es $var 4
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 intérprete de órdenes. Orden: env [opciones][variable=valor][orden] Esta orden se utiliza para ejecutar otras órdenes en un entorno determinado por las variables definidas. Ejecución: env Sin argumentos, esta orden muestra todas las variables de entorno definidas por el sistema operativo. Además de las variables de entorno, como ya hemos visto anteriormente, podemos crear en cualquier momento variables que solo tendrán validez para el intérprete de órdenes para el que han sido definidas. En este caso, si se quieren ver los valores asignados a esas variables, además de las variables de entorno, hay que usar la orden set. Éstas son algunas de las variables que más frecuentemente se usan (podéis ver lo que valen ejecutando la orden echo $variable): - HOME: camino a nuestro directorio personal - USER: nombre de usuario asignado - SHELL: camino al intérprete de órdenes que se ejecuta por defecto - HOSTNAME: nombre asignado al computador - PATH: caminos en los que el intérprete busca las órdenes a ejecutar cuando no especificamos donde están Orden: wc [opciones] [archivo] Esta orden hace recuentos de caracteres, palabras y líneas contenidos en los archivos. Ejecución: cat ejemplo-uniq wc -l La orden cat entrega a la orden wc como entrada estándar el contenido del archivo ejemplo-uniq y la orden wc saca por pantalla cuantas líneas tiene el archivo. 6.- Utilizando la variable HOME, contar cuantos archivos tenemos en nuestro directorio personal. Estructuras de control de flujo Estructura condicional if-then-else Sintaxis: if lista ; then lista ; [ elif lista; then lista; ] [ else lista; ] fi La lista if se ejecuta. Si su estado de salida es cero, se ejecuta la lista then. De otro modo, se ejecuta por turno cada lista elif (ejecutando su correspondiente lista then en el caso de obtener un resultado de salida igual a cero). Si su estado de salida es distinto de cero se ejecuta la lista else. El estado de salida es el de la última orden ejecutada o cero si ninguna condición ha sido verdadera. Ejecución: if who grep s alumnos ; then echo alumnos está ; else echo alumnos no está ; fi En este ejemplo buscamos si el usuario alumnos está conectado o no. 5
Evaluación de expresiones condicionales En el caso de las estructuras de control de flujo puede ser de gran utilidad una orden que pruebe condiciones y devuelva cero, en el caso de que las condiciones se den (verdad), y un valor distinto de cero en caso contrario (falso). Esta orden se puede escribir de dos formas equivalentes: test <expresión de condición> [ expresión de condición ] - Evaluación de archivos Existen muchas opciones de evaluación de archivos que podéis encontrar en la página man de la orden test, de las cuales destacaremos las siguientes: -a archivo: Devuelve verdad si archivo existe. -d archivo: Devuelve verdad si archivo existe y es un directorio. arch1 -nt arch2: Devuelve verdad si arch1 es más reciente que arch2. arch1 -ot arch2: Devuelve verdad si arch1 es más antiguo que arch2. Ejecución: if [ -a.bashrc ]; then echo Está ; else echo No está ; fi - Evaluación de cadenas -z cadena: Devuelve verdad si la longitud de cadena es cero. -n cadena: Devuelve verdad si la longitud de cadena no es cero. cadena: Devuelve verdad si la longitud de cadena no es cero. cd1 == cd2: Devuelve verdad si las cadenas son iguales. Se puede emplear =. cd1!= cd2: Devuelve verdad si las cadenas no son iguales. cd1 \< cd2: Devuelve verdad si cd1 se ordena alfabéticamente antes que cd2. cd1 \> cd2: Devuelve verdad si cd2 se ordena alfabéticamente antes que cd1. Ejecución: if [ `uname` = Linux ]; then echo Esto es Linux ; else echo Esto no es Linux ; fi - Evaluación numérica Sintaxis: numero1 op número2 Los valores numéricos deben ser enteros (positivos o negativos). Los operadores numéricos que se pueden utilizar son: -lt Menor que (del inglés less than) -le Menor o igual que (less than or equal) -gt Mayor que (greater than) -ge mayor o igual que (greater than or equal) -eq igual a (equal) -ne no igual a (not equal) Ejecución: a=1 ; if [ $a lt 10 ]; then echo menor que 10 ; else echo mayor que 10 ; fi - Evaluaciones lógicas -o OR lógica -a AND lógica! NOT lógica 6
Ejecución: if [ -r.bashrc a w.bashrc ]; then echo.bashrc se puede leer y modificar ; else echo Falta algún permiso ; fi 7.- Utilizando la orden ps hacer una lista de órdenes que indique si existe o no algún proceso llamado bash (utilizar para eso una estructura de condición de tipo if-thenelse) Cálculos aritméticos Para el cálculo de una expresión aritmética se puede utilizar la siguiente: Orden: expr expresión_artimética Esta orden muestra por la salida estándar el resultado de la expresión aritmética. Ejecución: var=1 ; expr $var + 1 Muestra por pantalla el resultado de sumarle 1 a la variable var. Existe otro método que nos permite hacer esos cálculos que se denomina expansión aritmética a través del formato $(( expresión_aritmética )). Con la expansión aritmética, a diferencia de la orden expr, en vez de obtener un resultado por la salida estándar, el intérprete de órdenes sustituye la expansión aritmética por el propio resultado de la expresión aritmética. Ejecución: var=1 ; echo $(( $var + 1 )) Como en el ejemplo anterior, muestra por pantalla el resultado de sumarle 1 a la variable var. NOTA: Sin la orden echo, el intérprete de órdenes interpretaría el resultado de la expansión aritmética (es decir, 2) como una orden y daría un error al no encontrar ninguna orden que se llamara 2. En cualquiera de los dos casos anteriores, si expresión_aritmética no es válida, el intérprete de órdenes imprime un mensaje de error. Las reglas de evaluación aritmética son las siguientes: ++, -- incremento y decremento (pueden ser post o pre)!, ~ negación lógica binaria ** potencia *, /, % multiplicación, división, resto +, - suma y resta <<, >> desplazamientos de bits a izquierda y derecha <=, >=, <, > comparaciones ==,!= comparaciones de igualdad y desigualdad &, ^, AND, XOR y OR binarios &&, AND y OR lógicas expr?expr:expr evaluación condicional =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, = asignaciones Ejecución: a=5; b=3; res=$((a * b)); echo resultado = $res 7
El mismo ejemplo utilizando la orden expr plantea un pequeño problema. Nos vemos obligados a utilizar el carácter de escape \ antes del símbolo * ya que para el intérprete de órdenes éste es un símbolo especial: Ejecución: a=5; b=3; res=`expr $a \* $b`; echo resultado = $res Estructura repetitiva while Sintaxis: while lista ; do lista ; done La orden while ejecuta continuamente la lista do siempre que la última orden de la lista while devuelva un estado de salida cero. El estado de salida de la orden while es el de la última orden de la lista do ejecutada, o cero si no se ha ejecutado ninguna orden. Ejecución: a=10 ; while [ $a gt 0 ] ; do echo $a ; a=`expr $a 1` ; done Una orden que es muy útil junto con while es read. Esta orden espera que se le dé por la entrada estándar una línea de texto. En el siguiente ejemplo utilizamos conjuntamente while y read para filtrar de la información que nos llega de la orden ls l aquellas partes que no nos interesa que aparezcan por la salida estándar. Ejecución: ls -l while read perm links usr grp tam fecha hora nombre ; do echo $nombre : $tam ; done La orden ls con la opción l entrega información sobre los archivos actuales repartida por campos. Así pues para cada archivo tendremos los siguientes campos: permisos, enlaces, usuario propietario, grupo, tamaño, mes día y hora de la última modificación y nombre del archivo. Para cada línea que nos da la orden ls l asignamos cada uno de esos campos a las variables que siguen la orden read y sacamos por la salida estándar con la orden echo sólo aquellos datos que nos interesan, en este caso, el nombre y su tamaño. Con los ejemplos que vienen a continuación veremos qué ocurre en los casos en que haya más (o menos) variables que campos por asignar. Ejecución: ls -l while read perm links usr grp resto ; do echo $resto ; done En este caso, podemos ver que el primer campo se asigna a la variable perm, el segundo a la variable links, el tercero a usr, el cuarto a grp y finalmente el resto de campos (lo que en el ejemplo anterior se asignaba a las variables tam, mes, día, hora y nombre) se asigna a la variable resto. Ejecución: ls -l while read perm links usr grp tam fecha hora nombre otro ; do echo $nombre: $otro ; done Con este ejemplo podemos ver que si existen menos campos que variables, éstas se quedan sin asignación. Ejercicios: 8.- Explicar porque en el ejemplo anterior aparece en la primera línea sólo :. 8
9.- Escribir una lista de órdenes utilizando la estructura de repetición de tipo while que pida un número n y presente por la salida estándar el doble de cada número de 1 a n. 10.- Escribir una lista de órdenes que muestre por la salida estándar parte de la información entregada por la orden ls con la opción l sobre el directorio /usr/bin. La información constará del nombre del archivo sólo en el caso de que sea un enlace a otro archivo (Sugerencia: utilizar la opción de evaluación de archivos L) 11.- Hacer una lista de órdenes en la que se pide el nombre de un directorio para a continuación comprobar que existe y que efectivamente es un directorio. En caso afirmativo pasarse al directorio y en caso contrario mostrar un mensaje de error (utilizar la orden read y la construcción condicional if-then-else) Estructura repetitiva for Sintaxis: for nombre [ in palabras; ] do lista; done La lista de palabras que va detrás de in se expande, generando una lista de elementos. La variable nombre se define como cada uno de los elementos en cada iteración, y lista se ejecuta cada vez para cada uno de los elementos. El estado de retorno es el de salida de la última orden que se ha ejecutado. Si la expansión de elementos después de in resulta vacía, no se ejecuta ninguna orden y el estado de salida es cero. Ejecución: for x in *; do (cd $x && mkdir temp) ; done Este ejemplo sirve para crear en cada uno de los subdirectorios del actual directorio de trabajo una carpeta con el nombre temp. Ejercicios: 12.- Con una estructura repetitiva de tipo for escribir una lista de órdenes que dé el recuento de archivos y directorios que hay en el directorio actual. 13.- Repetir el ejercicio 9 con una estructura repetitiva de tipo for. Para ello utilizar la orden seq. (man seq) Existe otra forma de utilizar la estructura repetitiva for que es exclusiva del intérprete de órdenes bash. Sintaxis: for (( expr1 ; expr2 ; expr3 )) do lista; done En este caso, en primer lugar se evalúa expr1 siguiendo las reglas de evaluación aritméticas. Si el estado de salida de expr1 no es nulo, no se ejecuta la estructura repetitiva. Si expr1 es válida, se evalúa expr2. Cada vez que el estado de salida de expr2 da un resultado diferente de cero, se ejecuta la lista y se evalúa expr3, finalizando la ejecución de la estructura repetitiva en cuanto el estado de salida de expr2 sea cero. Ejecución: for (( x=0 ; x<=10 ; x++ )) do echo $x ; done Aparecen en pantalla los números del 0 al 10. Ejercicios: 14.- Repetir el ejercicio 9 con una estructura repetitiva de tipo for con la última forma vista. Existen otras estructuras de control de flujo de las que se puede obtener información a través de la página man del intérprete de órdenes (man bash). Estas son las estructuras until, case y select. También se puede encontrar información sobre las órdenes break, continue y exit, que permiten parar la ejecución de las estructuras repetitivas, de forma similar a C/C++. 9