ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC Práctica 1: Uso del Shell 1. Introducción Entrada en el sistema o login Unix es un sistema multiusuario, es decir, permite que varios usuarios utilicen la misma máquina de forma segura. Para ello es necesario que cada usuario tenga una identificación que permita al sistema saber quién está utilizando la máquina en cada momento, y qué cosas tiene permiso para hacer (qué operaciones y sobre qué recursos). Esta identificación es lo que se conoce como nombre de usuario o login name, y es lo primero que el sistema nos pide que introduzcamos cuando queremos abrir una sesión. Para evitar que ningún usuario pueda hacerse pasar por otro, cada usuario tiene asociada una clave de acceso o password, que el sistema nos pide que introduzcamos a continuación y que le permitirá verificar que realmente el usuario es quien dice ser. Si la clave de acceso es la correcta, el usuario puede empezar a trabajar en el sistema. angren login: e3432065 <cr> password: ???????? <cr> Figura 1: Entrada en el sistema Cuando introducimos el password éste no aparece por la pantalla, para evitar que nadie descubra nuestra contraseña por accidente. En Unix existe un usuario especial, root (superusuario), que es el único con privilegios para acceder a todos los datos del sistema y realizar las tareas de administración. Password El password es una secuencia de entre 6 y 8 caracteres alfanuméricos (si se introducen más caracteres se ignoran los finales). Es recomendable que sea una combinación de letras mayúsculas, minúsculas, caracteres especiales y números, y que no sea fácilmente deducible, para dificultar que otros usuarios la descubran. Por este motivo, también es conveniente cambiarla periódicamente. Para modificar la contraseña, disponemos del comando passwd. angren% passwd Changing password for e3432065 Old password: New password: Retype new password: Figura 2: Cambio de password para el usuario e3432065 Existe un fichero en el sistema que almacena los passwords de todos los usuarios (/etc/passwd). Este fichero puede ser leído por cualquier usuario, pero sólo puede ser modificado por el superusuario (y por el comando passwd). La clave de acceso se guarda encriptada, para evitar que se pueda descubrir al leer este fichero. Además de la contraseña, para cada usuario se guarda información adicional, como por ejemplo cuál es el primer programa que se debe ejecutar cuando ese usuario inicia una sesión o cuál es el directorio del sistema de ficheros en el que inicia la sesión (ver figura Práctica 1: Uso del Shell 1 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC 3). login_name:passwd_encriptado:uid:gid:comentarios:login_directory:/usr/bin/csh • Nombre de usuario o login name • Clave de acceso • Identificador de usuario (uid): internamente, el sistema operativo identifica cada usuario a través de este entero • Identificador de grupo del usuario (gid): en Unix es posible agrupar los usuarios en función de alguna característica común, y dar un identificador a cada grupo. Por ejemplo, todos los alumnos podrían formar un grupo y los profesores otro. • Comentario sobre el usuario: depende del administrador, por ejemplo puede ser el nombre y apellidos del usuario, teléfono de contacto, etc • Directorio base de los ficheros del usuario (directorio de login): normalmente cada usuario tiene asignada una zona de trabajo dentro del sistema de ficheros, y cuando el usuario entra en el sistema lo hace en el directorio base de esa zona • Primer programa de usuario que se tiene que ejecutar cuando éste inicia la sesión (normalmente es un intérprete de comandos) Figura 3: Línea del fichero de passwords: información de un usuario Intérprete de comandos Un intérprete de comandos (shell en UNIX) es un programa que facilita el uso interactivo del sistema operativo. El shell recibe las operaciones o comandos que el usuario quiere realizar y solicita al sistema operativo que las lleve a cabo. Existen varios intérpretes de comandos muy conocidos, y adenás cada usuario podría programar el suyo propio. Alguno de los intérpretes más difundidos con UNIX son el C-shell (/bin/csh), el Bourne shell (/bin/sh) y el Korn shell (/bin/ksh). El intérprete de comandos que utilizaremos en las prácticas de ISOP es el C-shell. Normalmente, los usuarios tienen un intérprete de comandos como programa de inicio de sesión, y cada usuario puede decidir cuál quiere utilizar mediante el comando chsh (figura 4) De esta angren% chsh -s /bin/csh e3432065 Figura 4: El shell inicial del usuario e3432065 pasa a ser el c-shell manera, la próxima vez que el usuario inicie una sesión lo hará ejecutando el nuevo shell. Además, un shell se puede lanzar en cualquier momento como cualquier otro programa de usuario, y finalizar su ejecución con los comandos exit o logout o pulsando las teclas ctrl-d. El comportamiento habitual de un intérprete de comandos es el siguiente. Cuando el shell está preparado para recibir un comando, se lo indica al usuario mostrando por pantalla el prompt. El prompt puede ser un único carácter ($, %, >, ...), o una cadena de carácteres (el login name del usuario, el nombre de la máquina, ...), y cada usuario puede decidir el prompt que quiere utilizar en sus sesiones. Una vez que el usuario introduzca un comando, el shell hará lo necesario para que se ejecute y esperará a que finalice la ejecución. Cuando esto ocurra, volverá a mostrar por pantalla el prompt para que el usuario sepa que el shell ya está preparado para recibir la siguiente petición. Comandos Los comandos son la forma que tiene un usuario (no programador) de solicitar servicios al sistema. Estos comandos pueden ser funciones que los intérpretes implementan (comandos internos), o pueden ser programas separados (comandos externos) que los intérpretes lanzan. Por lo tanto, los comandos internos disponibles dependerán del intérprete de comandos que se esté utilizando. Bajo el punto de vista del shell, cualquier programa que el usuario intente lanzar es un comando externo. Práctica 1: Uso del Shell 2 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC Salida del sistema o logout Para finalizar la sesión el usuario debe introducir los comandos logout o exit, o pulsar ctrl-d. Esto provocará que el intérprete de comandos finalice su ejecución, haciendo que vuelva a ejecutarse el proceso encargado de recibir peticiones de entrada (vuelve a salir la petición de login). 2. Primeros Comandos Examinar el entorno date cal [[mes] año] who finger ps muestra la fecha y hora actual muestra el calendario de la fecha especificada lista todos los usuarios que en ese momento tienen una sesión abierta en la máquina. lista de todos los usuarios con información general almacenada muestra los procesos que el usuario tiene en ejecución Gestionar el directorio ls cd nombre_directorio pwd mkdir nombre_directorio rmdir nombre_directorio cp fuente destino rm fichero mv fichero nuevo_nombre cat [fichero] more [fichero] wc [fichero] lista de los ficheros de un directorio hace que el directorio actual pase a ser nombre_directorio muestra cuál es el directorio actual crea un subdirectorio llamado nombre_directorio borra el subdirectorio nombre_directorio copia el fichero fuente sobre el fichero destino borra un fichero cambia el nombre de un fichero; si nuevo_nombre es un directorio, el fichero conserva el nombre pero se mueve al directorio especificado muestra el contenido de un fichero muestra el fichero pantalla a pantalla cuenta el número de líneas, palabras y caracteres del fichero 3. El Sistema de Ficheros 3.1. Nombres de ficheros Un nombre válido de fichero es cualquier concatenación de caracteres de longitud inferior a 256. Conviene que los nombres no contengan caracteres especiales del shell (? * ' “ / \) para no complicar el uso de esos nombres. Los nombres de fichero que empiezan por punto (.) sólo se visualizan con la opción -a del comando ls. 3.2. Organización del sistema de ficheros Los ficheros se pueden agrupar en directorios que a su vez pueden contener otros directorios. De esta manera, se obtiene un sistema de ficheros organizado de forma jerárquica. Normalmente un usuario tiene asignado un directorio de login, en el que está situado cuando entra en la sesión, y en el que puede dejar sus ficheros y crear sus propios directorios. Una vez dentro de la sesión el usuario puede moverse por el sistema de ficheros utilizando el comando cd, siendo el directorio actual o current working directory aquél en el que se encuentra en cada momento. El directorio raíz (root) es el origen de la jerarquía y su nombre es una barra (/). Tradicionalmente este directorio, contenía los siguientes directorios: Práctica 1: Uso del Shell 3 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC • el directorio /bin contiene los programas de utilidad más utilizados en el sistema. • en el directorio /etc se encuentran ficheros con datos que se usan en la gestión del sistema. Por ejemplo el fichero /etc/passwd. • el directorio /tmp sirve para almacenar ficheros temporales. Los comandos y utilidades del sistema pueden crear ficheros en este área que tras su ejecución pueden ser borrados. • en el directorio /lib se encuentran las librerías de programas que se pueden montar con programas creados por el usuario en C, FORTRAN o cualquier otro lenguaje de programación disponible. • el directorio /usr es un directorio de propósito general que algunas veces contiene las áreas de trabajo de los usuario. Contiene también otros directorios como el /usr/bin con programas de utilidad. • el directorio /dev contiene ficheros de tipo dispositivo: impresoras, terminales, cintas,... Aunque este tipo de organización no es obligatoria, todavía es la elegida por muchos administradores de sistemas. 3.3. Pathnames A la hora de identificar un fichero concreto dentro de la estructura de directorios se puede usar su pathname absoluto o pathnames relativos. El pathname absoluto de un fichero consiste en todos los directorios que se deben seguir desde la raíz hasta llegar a él, finalizando con el nombre del fichero. Los pathnames relativos consisten en los directorios que hay que recorrer desde el directorio actual hasta el fichero, finalizando también con el nombre del fichero. Por ejemplo, el fichero llamado fichero1 que aparece en el sistema de ficheros de la figura 5 tiene como pathname absoluto: /u2/est/e3432065/practica1/fichero1; suponiendo que el directorio actual sea /u2/est/e3432065, el pathname relativo del mismo fichero sería: practica1/fichero1; por último, si el directorio actual fuera /u2/est/e3432065/practica1, entonces el pathname relativo sería únicamente su nombre: fichero1. / pathname absoluto u2 est directorio de trabajo a pathname relativo a e3432065 practica1 fichero1 directorio de trabajo b pathname relativo b Figura 5: Pathname de un fichero 3.4. Permisos de acceso a los ficheros La opción -l del comando ls produce un listado de los ficheros del directorio con una serie de información adicional acerca de cada fichero. Entre esta información encontramos el tipo de fichero, el propietario del fichero y el grupo al que pertenece el fichero, la fecha y hora en las que fue modificado por última vez y los permisos de acceso que tiene asociados. El primer carácter de la información del fichero indica el tipo de fichero (por ejemplo, una d significa que se trata de un directorio, y un - significa que es un fichero ordinario). Los siguientes tres caracteres indican si el propietario del fichero puede leerlo, escribir en él o ejecutarlo, respecPráctica 1: Uso del Shell 4 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC tivamente. La representación que se utiliza es un guión (-) para indicar que no se puede realizar esa operación o las letras r (lectura), w (escritura) o x (ejecución), para indicar que si que es posible. A continuación vienen otros 6 caracteres que se interpretan de la misma manera, pero referidos a los usuarios del mismo grupo que el propietario y al resto de usuarios. En el ejemplo de la figura 6, el propietario de fichero1 (e3432065) lo puede leer y modificar, pero no lo puede ejecutar. Los usuarios del mismo grupo que el propietario (est) sólo lo pueden leer, y el resto de usuarios del sistema también pueden leerlo únicamente. angren% pwd /u2/est/e3432065/practica1 angren% ls -l -rw-r--r-- 1 e3432065 est 1024 Sep 15 10:00 fichero1 angren% Figura 6: Permisos de acceso de un fichero La codificación interna de estos permisos es mediante 3 dígitos en octal, tal y como se ve en la figura 7. rw-r--r-1 1 0/10 0/100 4 (resto de usuarios) 4 (usuarios del mismo grupo) 5 (propietario del fichero) Figura 7: Representación en octal de los permisos de acceso Tener permiso de ejecución sobre un directorio significa que se puede atravesar o hacer cualquier operación sobre los ficheros que contiene (siempre respetando los permisos de acceso al fichero en cuestión). El permiso de lectura sobre un directorio nos autoriza a listar su contenido, y el permiso de escritura añadir ficheros dentro de él. Para poder acceder de alguna manera a un fichero, es necesario que todos los directorios que hay en el camino hasta ese fichero nos permitan hacerlo. Cuando se trata de un fichero que contiene un script, para poder ejecutarlo no basta con tener permiso de ejecución, sino que también es necesario tener permiso de lectura. Si por el contrario es un fichero en formato ejecutable, con el permiso de ejecución es suficiente. Cuando se crea un fichero, se hace con unos permisos por defecto (determinados por el valor de una máscara de bits, que el usuario puede modificar). Si posteriormente se quieren modificar estos permisos se puede utilizar el comando chmod. Este comando se puede utilizar de varias maneras dos de las cuales aparecen en la figura 8, en la que se añade a fichero1 permiso de ejecución tanto para el propietario del fichero como para los miembros de su grupo. La primera manera que se angren% chmod ugo+x fichero1 angren% ls -l -rwxr-xr-x 1 e3432065 est 1024 Sep 15 10:00 fichero1 angren% chmod 755 fichero1 angren% ls -l -rwxr-xr-x 1 e3432065 est 1024 Sep 15 10:00 fichero1 Figura 8: Modificación de los permisos de acceso de un fichero muestra indica que a los permisos del propietario (u), de su grupo (g) y del resto de usuarios (o)hay que añadir (+) permiso de ejecución (x). Si en lugar de añadir se quisiera eliminar un permiso, se Práctica 1: Uso del Shell 5 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC utilizaría el símbolo -. La segunda manera consiste en indicar, codificado en octal, como queremos que queden los permisos asociados al fichero (755). Otros comandos para la gestión de ficheros También existen comandos que nos permiten comparar el contenido de dos ficheros (diff, cmp), averiguar el directorio en el que se encuentra un determinado fichero (find), o el pathname de un determinado comando (whereis o which), cambiar el propietario o el grupo de un fichero (chown, chgrp), etc. 4. El C-shell: uso interactivo El C-Shell es el intérprete de comandos que se usará en las prácticas de laboratorio. Muchas de las características descritas a continuación están también presentes en los otros shells que vienen con Unix, aunque a veces hay variaciones en la sintaxis o en los nombres de los comandos internos. El C-shell utiliza dos ficheros de control, en los que el usuario puede dejar comandos para que se configure el entorno de trabajo tal y como lo desea. Estos dos ficheros son los llamados .login y .cshrc, y deben estar en el directorio base de la zona de trabajo del usuario (podéis ver como ejemplo los que tenéis en vuestra cuenta de angren: more .login, more .cshrc). Cuando el C-shell inicia su ejecución interpreta el fichero .cshrc. Si además, el inicio de ejecución coincide con el inicio de la sesión del usuario, el C-shell interpreta a continuación el contenido del fichero .login. 4.1. El Entorno de trabajo 4.1.1. Variables El C-shell tiene variables que le permiten caracterizar el entorno de trabajo. El usuario puede consultar qué variables hay definidas y el valor que tienen asociado mediante el comando set (figura 9). angren% set argv autologout cwd history home noclobber path prompt savehist shell status system term user () 600 /u2/est/e3432065/practicas 20 /u2/est/e3432065 (/usr/bin .) angren.eupvg.upc.es !: 20 /usr/bin/csh 0 angren.eupvg.upc.es vt100 e3432065 Figura 9: Ejemplo de variables del shell definidas Para modificar el valor de una variable o para definirla se puede usar también el comando set, Práctica 1: Uso del Shell 6 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC pero añadiendo el nombre de la variable y el nuevo valor que se le quiere dar. angren% set history=10 angren% set argv () autologout 600 cwd /u2/est/e3432065/practicas history 10 . . . user e3432065 Figura 10: Modificación de una variable del shell No todas las variables tiene sentido que sean modificadas por el usuario. Por ejemplo, la variable cwd (current working directory) indica el directorio actual, y el shell se encarga de actualizarla cada vez que el usuario cambia de directorio. Las modificaciones de las variables sólo son válidas durante la ejecución del C-shell en el que se hacen, y se pierden una vez que el shell finaliza la ejecución. Cuando se lance un nuevo shell las variables vuelven a inicializarse con los valores por defecto. Si queremos que ciertas variables siempre tomen por defecto los valores que nos interesan podemos incluir en el fichero .cshrc los comandos que modifican los valores de esas variables. De esta manera cuando iniciemos una sesión, automáticamente se configurará nuestro entorno de trabajo tal y como lo queremos. El significado de alguna de estas variables es: • history: se utiliza para el mecanismo de historia, explicado más adelante. • home: habitualmente contiene el directorio base del usuario en el sistema de ficheros. Cuando introducimos el comando cd sin ningún argumento, hacemos que el directorio actual pase a ser el indicado por esta variable. • prompt: lo muestra el shell por pantalla para indicar al usuario que está preparado para recibir comandos. Puede asignársele el valor de otra variable, como la que contiene el username (user), o el directorio actual (cwd). • status: contiene el valor de retorno del último comando que se ha ejecutado. • path: contiene una lista de directorios, separados entre si por un espacio en blanco. Cuando el usuario introduce el nombre de un programa que quiere lanzar, el shell lo busca en los directorios que indica esta variable, de izquierda a derecha, y para de buscar cuando encuentra un fichero con el nombre que el usuario ha introducido (ver sección 4.1.7. Ejecución de comandos). El usuario puede añadir a esta variable todos los directorios que considere oportunos. • term: esta variable indica el tipo de terminal desde el que se está trabajando. • shell: contiene el login shell del usuario. Además de las variables que usa el shell, existen otro tipo de variables que describen el entorno de trabajo (variables de entorno) que el shell pasa como parámetros a los procesos que lanza. Un usuario puede definir sus propias variables de entorno, para describir características que el shell no contemple. El valor de estas variables se puede consultar con el comando printenv, y para asig- Práctica 1: Uso del Shell 7 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC narles valores se utiliza el comando setenv. angren% setenv MAIL /var/mail/e3432065 angren% Figura 11: Definición de una variable de entorno Para distinguir los dos tipos de variables, se suelen dar nombres con letras mayúsculas a las variables de entorno, mientras que las variables del shell suelen tener nombres con letras minúscula. 4.1.2. Patrones de nombres de archivo Los metacaracteres son caracteres especiales, que el shell interpreta y substituye por el significado que tienen asociado. Algunos de estos metacaracteres se utilizan para definir patrones de nombres. Por ejemplo, un asterisco (*) significa “cualquier grupo de caracteres”; un interrogante (?) significa “cualquier carácter” (uno sólo); y los corchetes ([]) pueden servir para especificar un rango de caracteres. angren% ls f1 f2 f3 fichero1 fichero2 fichero3 angren% ls *1 f1 fichero1 angren% ls ?1 f1 angren% ls f[1-3] f1 f2 f3 angren% Figura 12: Ejemplo de patrones de nombres Otro carácter con significado especial es la tilde (~), que seguida de un username se interpreta como el home directory del usuario con ese username. Si no se pone username se substituye por nuestro home directory (por ejemplo, el comando ls ~ listará el contenido de nuestro home directory). 4.1.3. Inhibición de los caracteres interpretados Si se desea que el shell no interprete algún carácter, éste se puede preceder de la barra invertida (\) o bien, si se trata de un grupo de caracteres, encerrarlos entre comillas dobles o simples (“) ('). En la figura 13 tenemos un ejemplo que muestra la necesidad de poder evitar la interpretación de estos caracteres. En el ejemplo se pretende borrar un fichero al que hemos llamado temp?, en el caso a, el shell interpreta que queremos borrar cualquier fichero cuyo nombre coincida con el patrón (por ejemplo, intentaría borrar temp1, temp2, temp?, etc...). Si embargo, en el caso b, con el uso de la \ le indicamos al shell que en esta situación el carácter ? debe ser tratado como cualquier otro carácter. angren% rm -i temp? temp1: ? (y/n) n temp2: ? (y/n) n temp?: ? (y/n) y angren% caso a angren% rm -i temp\? temp?: ? (y/n) y angren% caso b Figura 13: Inhibición de caracteres interpretados Práctica 1: Uso del Shell 8 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC 4.1.4. History El C-shell ofrece un mecanismo de historia de los últimos comandos que el usuario ha ejecutado. Mediante este mecanismo el usuario puede recuperar estos comandos sin necesidad de volver a teclearlos. La variable history del C-shell contiene la cantidad de comandos que debe recordar (si no está definida, entonces el Cangrenangrenangren-shell no recuerda ningún comando). Utilizando el comando history se obtiene la lista de comandos guardados en la historia. Cada comando aparece precedido por un número que puede usarse para referirse a él. Los siguientes comandos permiten reejecutar alguno de los comandos guardados en la lista de history: !! !cadena !# ^vieja^nueva ejecuta el comando más reciente ejecuta el comando más reciente que comience por cadena. ejecuta el comando asociado al número #. ejecuta el comando más reciente, pero substituyendo la aparición de la cadena vieja por la cadena nueva La figura 14 muestra un ejemplo del uso del history. En este ejemplo, pedimos que se vuelva a ejecutar el último comando que empezaba por c, y el resultado será la compilación del fichero programa.c. angren% history . . . 19: cc -o programa programa.c 20: vi programa.c angren% !c cc -o programa programa.c Figura 14: Ejemplo del uso del history 4.1.5. Alias El mecanismo de alias permite que el usuario asocie durante una sesión sus propios nombres a los comandos. Cuando la primera palabra que aparece en la línea de comandos se corresponde con un alias, el C-shell la substituye por la definición que el usuario le haya asociado y ejecuta el comando resultante. Este mecanismo permite que un usuario utilice nombres que le resultan más cómodos o fáciles de recordar, así como modificar el comportamiento por defecto de un determinado comando. Para la definición de un alias podemos utilizar el comando alias, pasándole como argumento el nombre que le queremos dar y a continuación su definición. En la figura 15 vemos dos ejemplos de la definición y uso de los alias. El primero crea un alias llamado isop, cuya definición consiste en cambiar al directorio de isop. Con un alias de este tipo evitamos tener que recordar el directorio de la asignatura, y además con este alias será necesario escribir menos cada vez que queramos movernos a ese directorio. El segundo ejemplo hace que cuando el usuario introduzca rm, el shell lo substituya por rm -i, haciendo que por defecto el intérprete le pida confirmación antes de borrar un Práctica 1: Uso del Shell 9 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC fichero. angren% pwd /u2/est/e3432065 angren% alias isop cd /u2/practiques/isop angren% isop angren% pwd /u2/practiques/isop angren% alias rm ‘rm -i’ Figura 15: Definición de alias Para saber qué alias hay definidos en una sesión, se puede introducir el comando alias, sin ningún argumento. Para eliminar la definición de un alias, se puede utilizar el comando unalias, seguido del alias que queremos eliminar. La definición de un alias sólo tiene efecto durante la ejecución del shell en la que se hacen. Por lo tanto, cuando el usuario sale de la sesión el alias se pierde, y será necesario volver a definirlo la próxima vez que el usuario entre en el sistema. Si queremos que cada vez que entremos en el sistema se definan automáticamente los alias que nos interesan (sin preocuparnos de hacerlo explícitamente), podemos incluir los comandos de alias en el fichero .cshrc. De esta manera, cada vez que se lance un C-shell, éste definirá los alias que le indiquemos. 4.1.6. Caracteres de control En UNIX es posible programar el teclado para que realice ciertas funciones como detener la salida por pantalla o abortar el comando actual. A continuación se explican algunas de estas funciones junto con los caracteres que habitualmente las realizan. función tecla habitual erase DEL/ctrl-h stop ctrl-s start ctrl-q intr ctrl-c eof ctrl-d Descripción borra la tecla introducida anteriormente detiene la salida de datos por el terminal reanuda la salida de datos por el terminal aborta el comando que se ejecuta en ese instante señala el fin de fichero El comando stty se utiliza para configurar las características de la entrada y salida de datos por el terminal desde el que se trabaja. Entre otras cosas, permite consultar o cambiar el carácter o grupo de caracteres que realiza una función. Por ejemplo, para hacer que la tecla de borrado pase a ser la tecla DEL, habría que introducir: stty erase DEL. Otras características que permite modificar son por ejemplo el número de columnas o filas que forman la pantalla, desactivar el echo, etc. Para consultar la configuración actual del terminal se puede introducir stty sin ningún argumento. 4.1.7. Ejecución de comandos Cuando el usuario introduce un comando, el shell tiene que localizar en qué lugar del sistema de ficheros se encuentra ese comando (es decir, qué fichero ejecutable está intentando lanzar). Si el nombre que ha recibido contiene el carácter /, el shell asume que el usuario ha introducido el directorio donde se encuentra el comando, y que tiene que buscarlo en ese punto. Si no lo encontrara allí daría un mensaje de error. Si por el contrario, el usuario sólo introduce el nombre del comando, el shell comprobará primero si hay definido algún alias con ese nombre. De ser así, substituirá el alias por su definición, y ejecutará esta definición. Si no se trata de un alias, entonces el shell buscará el comando en los directorios indicados por la variable path, de izquierda a derecha. La búsPráctica 1: Uso del Shell 10 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC queda finaliza cuando encuentre un comando con el nombre que busca. Es decir, si existen varios ficheros ejecutables con el mismo nombre, el shell ejecutará el que se encuentre en el directorio que aparece más a la izquierda en la variable path. Si no encuentra un ejecutable con ese nombre en ninguno de los directorios del path, entonces el shell dará un mensaje de error. Si el comando introducido es externo, el shell crea un proceso separado que ejecutará ese comando. Si el comando que se intenta lanzar es un script, el shell creará otro shell que será el encargado de interpretar el script, mientras que el shell inicial se quedará a la espera de que finalice la ejecución del script. Una vez lanzado el comando, puede ser que el shell se quede a la espera de que acabe su ejecución antes de aceptar una nueva petición del usuario. Si esto es así, se dice que es una ejecución en primer plano (foreground). Si un comando no necesita intercambiar datos con el usuario, y además se espera que va a tardar bastante tiempo en ejecutarse, el usuario puede querer aprovechar el tiempo y ejecutar otros comandos al mismo tiempo (esto es posible porque Unix es un sistema multiprogramado: permite tener varios programas ejecutándose al mismo tiempo). Para ello, se puede lanzar el comando en segundo plano (background), usando el carácter & al final de la línea. Cuando se lanza un comando en background, el shell crea el proceso que lo va a ejecutar, informa al usuario del identificador de proceso que el sistema operativo le ha asignado (pid), a continuación muestra el prompt y queda a la espera de la siguiente petición del usuario. Cuando el proceso en background finalice, el shell informará al usuario mostrando su pid y un mensaje que indique la finalización. El comando fg, se utiliza para hacer que un proceso que se está ejecutando en background pase a ejecutarse en foreground (es decir, el intérprete de comandos quedará a la espera de que el comando finalice la ejecución). Si por el contrario, queremos que un proceso lanzado en foreground pase a estar en background debemos en primer lugar parar momentáneamente su ejecución (suspender la ejecución mediante ctrl-z), y a continuación ejecutar el comando bg. Si un comando lanzado en background necesita mostrar por el terminal algún mensaje, es necesario que el terminal esté configurado para permitirlo (la opción tostop tiene que estar desactivada: stty -tostop). Si no es así, el shell parará la ejecución del proceso cuando intente escribir en el terminal, y no podrá continuar hasta que el usuario ordene su ejecución en foreground. Además de estos dos comandos existen otros dos comandos relacionados con la gestión de procesos que conviene conocer. El primero es ps, que nos permite saber todos los procesos que en ese instante estamos ejecutando. El segundo comando es kill, que utilizado con la opción -9 nos permite abortar la ejecución de todos los procesos cuyo identificador le pasemos como argumento. Un usuario sólo puede abortar los procesos que él haya lanzado. 4.2. Usar el resultado de un comando como argumento Usando las comillas invertidas (`) podemos hacer que el resultado de la ejecución de un comando pase a ser un argumento de otro comando. Por ejemplo, el comando cat muestra por pantalla el contenido de los ficheros que se le pasan como argumentos. Si queremos mostrar por pantalla el contenido de todos los ficheros del directorio actual, cuyo nombre empieza por t, podríamos introducir: cat ‘ls t*‘. Ante esta petición, el shell ejecutará el comando ls t*. El resultado de esta ejecución será el nombre de todos los ficheros del directorio actual cuya primera letra sea t, y, seguidamente el shell ejecutará el comando cat pasándole como argumentos todos estos nombres. Práctica 1: Uso del Shell 11 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC 4.3. Redireccionamiento de entrada/salida En Unix, todos los procesos tienen asociado un dispositivo que actúa como entrada de datos estándar, otro como salida estándar y un tercero que hace de salida de errores estándar. Si no se indica lo contrario, el shell hace que el terminal en el que se trabaja sea tanto la entrada como la salida de datos estándar, y por tanto siempre que el proceso intente leer de su entrada estándar lo hará del terminal (esto es, del teclado). Es posible modificar este comportamiento sin necesidad de reescribir el código del programa. El shell permite asociar un fichero a la entrada estándar del proceso, haciendo que pase a leer los datos del fichero asociado en lugar de hacerlo del teclado. De la misma manera se puede cambiar la asociación de la salida y del error estándar. Esto es lo que se llama redirección de entrada/salida. Para redireccionar la entrada estándar del proceso se puede utilizar el símbolo <, seguido del fichero que queremos asociar a la entrada de datos. Para redireccionar la salida se utiliza el símbolo > y para redireccionar la salida de errores se utiliza >&. En el caso de la redirección de la salida, hay que decir que si el fichero que se está usando ya existía se perderá su contenido. Si queremos conservarlo y añadir al final los datos del proceso podemos utilizar el símbolo >>. Pipe Utilizando el mecanismo de redirección de la entrada y la salida estándar, el shell permite conectar dos procesos, haciendo que la salida estándar de uno de ellos pase a ser la entrada estándar del otro. Esto se hace utilizando una pipe (tubería), cuyo símbolo en el shell es |. La manera de utilizarlo es situando la pipe como separador entre los dos comandos que se quieren conectar. El shell creará dos procesos, uno para cada comando, que se ejecutarán al mismo tiempo, y hará que el proceso que ejecuta el comando situado a la izquierda de la pipe utilice como salida de datos estándar la entrada de datos del proceso que ejecuta el comando situado a la derecha. Esta es una manera sencilla que permite que haya comunicación de datos entre dos procesos. Todos los comandos conectados de esta manera en una sola línea de comandos se llaman pipeline. El siguiente ejemplo permite saber cuantos ficheros hay en el directorio actual puesto que ls escribe en su salida estándar una línea por fichero y wc cuenta las líneas que encuentra en su entrada estándar. angren% ls -a | wc Figura 16: Ejemplo de pipeline Filtros Se llaman filtros a los comandos que leen datos de su entrada estándar, realizan sobre ellos un tratamiento, y muestran el resultado de ese tratamiento sobre su salida estándar. El comando dejará de aceptar datos de entrada cuando lea el carácter de final de fichero. Normalmente pueden aceptar un argumento que actúe como su entrada de datos, en lugar de su entrada estándar. En este caso, el comando no se verá afectado si se redirecciona su entrada estándar (porque no la utiliza). A continuación se describen brevemente algunos filtros muy comunes (para más información sobre alguno de ellos podéis consultar el man): more cat head [-n count] tail [-n count] Práctica 1: Uso del Shell Muestra página a página por su salida estándar lo que lee por su entrada estándar. Muestra por salida estándar lo que recibe por su entrada estándar. Muestra por salida estándar las primeras líneas que recibe por su entrada estándar. Por defecto intenta mostrar 10 líneas. Muestra por salida estándar las últimas líneas que ha recibido por entrada estándar. Por defecto son 10 líneas. 12 ISOP - Documentación para las Prácticas de Laboratorio sort wc grep patrón cut campos tee EUPVG - UPC Ordena los datos que le llegan por la entrada estándar y los escribe por la salida estándar. Por defecto ordena alfabéticamente en orden ascendente pero también puede ordenar en orden descendente, numérico y por alguna columna que no sea la primera. Cuenta las líneas, palabras y caracteres de la entrada estándar. Busca el patrón en los datos de entrada y muestra por salida estándar la línea que lo contiene. Considera cada línea que recibe por su entrada estándar dividida en campos, y selecciona el campo que se le indica. Por defecto el carácter que separa campos es el tabulador, pero se le puede indicar que sea otro mediante la opción -d. envía su entrada estándar hacia su salida estándar pero además escribe la entrada estándar en el fichero que se le indique. 5. El C-shell: programación de shell-scripts El shell no sólo actúa como intérprete de comandos interactivo sino que además se puede utilizar como lenguaje de programación, en el que cada sentencia ejecuta un comando. Los programas escritos para el shell se llaman scripts. Para ejecutar un script es necesario tener permiso tanto para leer como para ejecutar el fichero que los contiene. Cuando el usuario ejecuta un script, el shell crea un subshell que será el encargado de interpretar los comandos del script. Una vez finalice el script, el subshell también acabará la ejecución. Es importante tener esto en cuenta porque las modificaciones que haga el script sobre las variables del shell o del entorno desaparecerán una vez finalizado el script. La primera línea de un script suele utilizarse para indicar el shell que queremos utilizar para interpretarlo. Esta línea tiene que empezar con los caracteres #!, y a continuación debe contener el pathname absoluto del shell que queremos utilizar. Si la primera línea del fichero no tiene este formato, se utilizará el Bourne Shell para interpretar el script. El carácter # a principio de línea también se utilizar para marcar toda la línea como un comentario. 5.1. Variables y parámetros En un script se pueden usar tanto las variables del shell, como cualquier otra variable que el usuario necesite definir. Estas variables no necesitan una sentencia de declaración explícita, sino que se declaran en el momento de asignarles el primer valor. Los valores de las variables son cadenas de caracteres, aunque es posible asignar el resultado de una operación numérica mediante el operador @ (@ nombre_var=expresión, nótese el espacio en blanco entre el operador @ y el nombre de la variable). Para asignar a una variable cualquier cadena de caracteres se puede utilizar el comando set, tal y como se ha explicado para las variables del shell. Para obtener el valor de una variable, es necesario preceder el nombre de la variable por el carácter $. Un script puede recibir parámetros en la variable del entorno argv. Esta variable es equivalente a un array que almacena cada uno de los parámetros que se le pasan (el índice del primer elemento es 1). Una manera de referenciar el valor de los parámetros dentro del script es utilizando el carácter $ seguido del índice que le corresponde en el array de parámetros argv (esto es equivalente a $argv[indice]). Para saber el número de elementos que contiene una variable de este tipo se puede utilizar la expresión $#nombre_var. El comando shift se utiliza para desplazar los parámetros del script una posición a la izquierda Práctica 1: Uso del Shell 13 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC dentro de la variable argv (el parámetro que en ese momento ocupaba la primera posición se pierde). 5.2. Expresiones Los operadores disponibles para construir expresiones son prácticamente iguales a los del lenguaje C. Los operadores de igualdad (==, !=) consideran que los operandos son cadenas de caracteres. Unos operadores que en C no existen son =~ y !~, equivalentes a == y != pero tomando el operando del lado derecho como un patrón de caracteres. El resto de operadores actúan sobre números (+, -, *, /, %, <, >,...). Los operadores tienen que estar rodeados de espacios en blanco. También soporta unos operadores sobre ficheros que permiten saber, por ejemplo, si existe un fichero con ese nombre ( -e nombre_fichero ), si se tiene un determinado permiso de acceso (-r nombre_fichero, -w nombre_fichero, -x nombre_fichero), etc. 5.3. Estructuras de control A continuación se describen brevemente algunas estructuras de control que ofrece el C-shell: las repetitivas y las condicionales. Repetitivas El C-shell ofrece tres estructuras de control que permiten la repetición de varios comandos. La más sencilla es repeat, que permite ejecutar tantas veces como se le indique un comando simple (no puede ser un pipeline, ni un alias, ni una lista de comandos). Esta estructura de control y el comando tiene que aparecer en una sola línea. También tenemos un bucle similar al de cualquier lenguaje de alto nivel, en el que se repiten los comandos mientras la expresión no evalúe a 0. Las líneas que marcan el inicio y el fin del bucle no pueden contener otros comandos. Por último la estructura de control foreach ejecuta tantas iteraciones como valores haya en la lista de palabras (wordlist), y en cada iteración asigna uno de esos valores a la variable name. Igual que en el caso anterior las líneas de inicio y fin del bucle no pueden contener otros comandos. repeat count command while (expression) . . . end foreach name (wordlist) . . . end Figura 17: Estructuras de control repetitivas Condicionales Las estructuras condicionales de las que disponemos son unas equivalentes al if y al switch del lenguaje C. La primera que vamos a describir es un condicional sencillo, en el que si la expresión evalúa a cierto se ejecuta un comando simple. Toda la sentencia tiene que ocupar una sola línea. Además de este condicional sencillo, también podemos utilizar pares de else-if, como se ve en la figura 18, marcando el final de la estructura con la palabra endif (uno solo para todas las condiciones anidadas). Las palabras else y endif tienen que aparecer al principio de línea, y la sentencia del if tiene que estar sola en la línea (o precedida por un else). También disponemos de un switch, en el que para decidir la rama que se ejecuta se hace pat- Práctica 1: Uso del Shell 14 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC tern matching entre el string del switch y cada uno de los casos contemplados. if (expression) command if (expression1) then . . . else if (expression2) then . . . else . . . endif switch (string) case str1: . . . breaksw . . . default: . . . endsw Figura 18: Estructuras de control condicionales 5.4. Ejemplo: which_name En la figura 19 y en la figura 20 podemos ver dos shell script que implementan el mismo trabajo. Los dos aceptan como parámetros una lista de usernames y nos devuelven para cada usuario el comentario que tiene asociado en el fichero de passwords (típicamente, su nombre). La diferencia básica entre los dos scripts es la estructura de control que hemos utilizado. En la figura 19 hemos usado un bucle while, cuya condición de finalización es que el número de iteraciones sea igual al número de parámetros que le hemos pasado. En la figura 20 utilizamos un bucle foreach, lo que nos ha simplificado el control del bucle. _ #!/bin/csh # queremos utilizar el C-shell para ejecutar el script @ I = 0 @ NUMARG = $#argv # la variable I hara de contador en el bucle y NUMARG sera el limite del bucle while ($I < $NUMARG) grep $1 /etc/passwd | cut -d: -f5 @ I=$I + 1 shift # el comando shift desplaza una posicion a la izquierda el contenido de argv, # asi que el parametro que ocupaba la posicion 2, pasa a estar en la posicion 1: $1 end Figura 19: which_name: bucle while En ambos casos, para cada username utilizamos el comando grep para buscar ese username en el fichero de passwords. Si existe, este comando mostrará por su salida estándar toda la línea asociada al username, pero a nosotros nos interesa sólo la parte del comentario. Para seleccionar únicamente esa parte utilizamos el comando cut, que recibe por su entrada estándar la línea que le pasa el comando grep (gracias a la pipe). Al comando cut le decimos que el carácter separador de campos es el : (-d:), y que nos interesa seleccionar el campo número cinco (-f5), que es el que se corresponde con el comentario asociado al username. #!/usr/bin/csh foreach NAME ($argv) grep $NAME /etc/passwd | cut -d: -f5 end Figura 20: which_name: bucle foreach Práctica 1: Uso del Shell 15 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC 6. Manuales El comando man permite consultar el manual del sistema de forma interactiva. Mediante este comando podemos consultar la ayuda a cualquier otra utilidad disponible en el sistema (figura 21). angren% man nombre_comando Figura 21: Ayuda sobre El manual está organizado en varias secciones, identificadas con un número. Típicamente los sistemas incluyen las secciones que se muestran en la figura 22, numeradas de la misma manera (el resto de secciones y de numeración varía entre sistemas). (1) Comandos y programas de aplicación (2) Llamadas al sistema (3) Subrutinas y funciones de librería (4) Formatos de ficheros (5) Miscelánea .............. Figura 22: Secciones del manual de Unix La búsqueda en el manual se inicia en las secciones de número más bajo y finaliza en el momento de encontrar información sobre la palabra introducida. Si se quiere información de una determinada sección hay que introducir el número de sección antes del nombre del comando. Es posible que una misma palabra se encuentre en varias secciones (porque pueda tratarse tanto de un comando como una llamada a sistema, por ejemplo). Por ejemplo, hay un comando del shell llamado read, y por lo tanto tiene una página en la sección 1 del manual, y también existe una llamada a sistema con el mismo nombre, que por lo tanto tiene una página en la sección 2 del manual. Si introducimos man read, obtendremos ayuda sobre el comando que se llama así. Si lo que queremos es obtener ayuda sobre la llamada a sistema deberíamos introducir man 2 read. El manual también nos puede ayudar a encontrar un comando capaz de realizar la operación que nos interesa (figura 23). Para ello podemos utilizar la opción -k seguida de una palabra clave, y el angren% man -k palabras_clave Figura 23: Comandos relacionados con palabra_clave resultado de la operación será una lista de todos aquellos comandos en cuya descripción aparezca la palabra clave. Por ejemplo, si queremos saber los editores de texto que hay en el sistema, podríamos introducir la consulta que se muestra en la figura 24. angren% man -k “text editor” ed, red(1) - line-oriented text editor ex, edit(1) - extended line-oriented text editor sed(1) - stream text editor vi, view, vedit(1) - screen-oriented (visual) text editor Figura 24: Buscando comandos relacionados con las palabras text editor Práctica 1: Uso del Shell 16 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC 7. El editor vi El editor vi viene con todas las distribuciones UNIX, y por eso es conveniente estar familiarizado con su uso. Es un editor sencillo aunque no tiene un interfaz muy amigable. Este editor tiene dos posibles estados: modo inserción y modo comando. Si el estado activo es el de inserción, entonces se puede introducir el texto que se quiere editar. Si por el contrario se encuentra en modo comando, las teclas introducidas se interpretan como operaciones de control. Cuando se entra en el editor el modo inicial es comando, y para pasar a modo inserción existen varios comandos, por ejemplo el comando i. Si se esta en modo inserción y se desea pasar de nuevo a modo comando basta con pulsar la tecla ESC (en los vt100 ctrl-3). Si se introduce el comando :set showmode, aparecerá siempre en pantalla el estado en el que se encuentra el editor (para desactivar esta opción el comando es :set noshowmode). A continuación se describen algunos comandos básicos: i I a A o O x dw cw C dd d#d yy y#y p P u G ctrl-f ctrl-b ctrl-g Pasa a modo inserción. Los caracteres que se introduzcan aparecerán antes de la posición actual del cursor Pasa a modo inserción y sitúa el cursor al inicio de la línea en la que se encuentra Pasa a modo inserción y sitúa el cursor en la siguiente posición a la que tiene en ese momento Pasa a modo inserción situando el cursor al final de la línea actual Pasa a modo inserción, abre una nueva línea debajo de la actual, y sitúa el cursor al inicio de esta nueva línea Pasa a modo inserción, abre una nueva línea encima de la actual, y sitúa el cursor al principio de la nueva línea Borra el carácter que se encuentra debajo del cursor Borra la palabra sobre la que está el cursor Reemplaza la palabra actual por lo que se introduzca hasta que se pase a modo comando Reemplaza todas las palabras desde la posición actual hasta el final de la línea por lo que se introduzca hasta pasar a modo comando Borra la línea en la que se encuentra el cursor (y la guarda en el buffer intermedio) Borra # líneas a partir de la línea actual, incluida ésta (y las guarda en el buffer intermedio) Copia en un buffer intermedio el contenido de la línea actual Copia en un buffer intermedio el contenido de # líneas a partir de la actual (incluída ésta) Pega debajo de la línea actual el contenido del buffer intermedio Pega encima de la línea actual el contenido del buffer intermedio Deshace los efectos de la última acción Mueve el cursor al final del fichero Mueve el cursor a la siguiente página Mueve el cursor a la página anterior Muestra el nombre del fichero, el número de línea actual, y el número de líneas del fichero El comando :set number permite ver el numero de cada línea (para desactivar esta opción se utiliza :set nonumber). Para ir a una línea determinada basta con introducir el comando :i, donde i es el número de línea deseado, y para ir a la última línea el comando :$. El comando para grabar el contenido del fichero es :w. Para salir del editor grabando las últimas modificaciones :wq o :x. Si se quiere salir sin conservar los últimos cambios :q!. Un usuario puede guardar la configuración del editor que le interesa utilizar en un fichero de Práctica 1: Uso del Shell 17 ISOP - Documentación para las Prácticas de Laboratorio EUPVG - UPC nombre .exrc en su home directory, en el que puede registrar los comandos tipo set que le interese. angren% vi .exrc set showmode set number ..... Figura 25: Ejemplo de fichero de configuración del vi Práctica 1: Uso del Shell 18