Una Introduccion a la C Shell
Este tutorial es una traduccion carinosamente wikificada por ~peron del manual "An introduction to C Shell" redactado por Bill Joy, y publicado originalmente por la Univeresidad de California Berkeley en agosto de 1983 como segundo capitulo de su "UNIX Programmer's Manual" de 4.2BSD "UNIX".
Introduccion
Una shell es un interprete de lenguaje de ordenes. Csh es el nombre de un interprete de ordenes particular de la Distribucion de Software UNIX la Universidad de Berkeley (BSD). El objetivo principal de csh es traducir las ordenes escritas en la linea de comandos por medio de un terminal en acciones de computo o invocaciones a otros programas. Csh es realmente un programa de usuario similar a cualquier otro del sistema. Con suerte... csh sera un programa muy util para interactuar con el sistema UNIX.
Junto a este documento, querras consultar una copia del Manual del programador de UNIX. La documentacion de csh en el manual ofrece descripcion completa de todas las caracteristicas de la shell y oficia de referencia para todas las interrogantes sobre la misma. Muchas palabras de este documento aparecen en cursiva; son palabras importantes como nombres de ordenes, y palabras que tienen un significado especial al hablar de la shell y UNIX. Muchas de las palabras estan definidas en un glosario al final de este documento. Si no sabes lo que significa una palabra, consulta el glosario.
1. Uso de C shell en la Terminal
Normalmente la shell que estaras usando cuando inicies sesion a
texto-plano.xyz sera /bin/ksh
. De hecho, la mayoria de lo
dictado en este tutorial aplica a ella, asi tambien como a
/bin/sh
,
C Shell ya no es el interprete de ordenes por defecto en texto-plano.xyz, pero podras utilizarla anidada en otra shell cualquiera para seguir este tutorial historico. Ingresa a ella primero:
csh
Normalmente, recibiras el prompt por defecto de csh: ''% ''. ¡Ya podras continuar con el tutorial! Cuando termines y desees cerrar la C Shell y volver a tu interprete anterior, asegurate de ingresar el comando:
exit
La nocion basica de comandos
Si bien tiene un conjunto de funciones integradas que puede realizar directamente, una shell en UNIX actua principalmente como un medio a traves del cual se invocan otros programas. La mayoria de las ordenes provocan la ejecucion de programas que, de hecho, son externos al shell. La shell se distingue asi de los interpretes de comandos de otros sistemas por el hecho de que es simplemente un programa de usuario. y por el hecho de que se utiliza casi exclusivamente como mecanismo para invocar otros programas.
Las ordenes del sistema UNIX - de ahora en mas, llamadas comandos - constan de una lista de cadenas o palabras interpretadas como el nombre de comando seguido de argumentos. De esta forma, el comando
mail bill
consta de dos palabras. La primera palabra mail
llama al
comando que a ejecutar (en este caso el programa de correo que envia
mensajes a otros usuarios). La shell recurre al nombre del comando para
cargarlo en memoria e iniciar su respectiva ejecucion por usted. En este
caso, buscara en varios directorios un archivo con el nombre
mail
, el cuaol usted espera que contenga el programa de
correo electronico.
Las demas palabras suministradas junto al comando se interpretan como
sus argumentos, propicios para su ejecucion especifica. En este
caso hemos tambien provisto el argumento bill
, el cual el
programa de correo electronico mail interpreta
como un nombre de usuario al cual se remitira una pieza de
correspondencia. Atendiendo a la impresion regular del terminal, el
empleo del comando mail
podria aparecer de la siguiente
manera:
% mail bill
Tengo una pregunta en lo que refiere a la documentacion de csh.
A mi documento pareceria faltarle la seccion 5.
Existe dicha seccion?
EOT
%
Aqui se mecanografio un mensaje para remitirselo a Bill,
concluyendoselo mediante un ^D
(lo que transmite un mensaje
de fin de fichero al programa de correo mail).
De ahora en mas... la notacion "
^x
" debe leerse "Ctrl+x" y representa presionar la tecla x mientras se mantiene presionada la tecla Ctrl.
A consecuencia, el programa de correo hizo eco del caracter de control "EOT" y remitio nuestro mensaje. Los caracteres ''% '' antes y despues del comando de correo fueron impresos por la C shell para indicar una solicitud de entrada.
Tras presentar el mensaje ''% '', la shell leera la entrada de
ordenes desde nuestra terminal. Escribimos un comando completo
mail bill
. Luego, la shell ejecuto el programa de correo
mail con el argumento bill
y permanecio inactiva a
la espera de su cumplimiento. A continuacion, el programa de correo leyo
la entrada de nuestra terminal hasta que le indicamos fin del
fichero mecanografiando un ^D, tras lo cual la
shell noto que el correo se habia completado y nos indico que se
encontraba presta para leer nuevamente desde la terminal, presentando
inmediatamente otro ''% ''.
El patron esencial de toda interaccion con UNIX a traves del shell es este ciclo: se escribe una orden completa completa en el terminal, la shell ejecuta el comando y cuando su ejecucion fue cumplida, solicita un nuevo comando. Si usas el editor durante una hora, la shell esperara pacientemente que termine de editar y aguardara obedientemente nueva orden suya una vez que cumplida la edicion.
Un comando util que puede ejecutar ahora como ejemplo es el comando
tset, encargado de establecer los caracteres predeterminados
para borrar y eliminar en su terminal: el caracter de
borrar borra el ultimo caracter que escribio y el caracter de
eliminar elimina toda la linea que ha ingresado hasta el
momento. Por defecto. el caracter borrar es #
y el caracter
eliminar es @
. La mayoria de las personas que usan
pantallas CRT prefieren usar el caracter de retroceso
(^H) como caracter de borrado, ya que permite mayor
facilidad para ver lo que ha escrito hasta ahora. Puede llevar a cabo
este accionar mediante
tset -e
que ordena al programa tset configurar el caracter borrar, y su opcion por defecto para este caracter es un caracter retroceder.
Argumentos de Opcion
Una nocion util en UNIX es la del argumento de opcion
("flag"). Si bien muchos argumentos de comandos especifican a
nombres de archivo o nombres de usuario, existen ciertos argumentos
capaces de especificar una capacidad opcional del comando (la cual
desearias invocar en lugar de las opciones regulares). Por convencion,
tales argumentos flag comienzan con el caracter guion medio
-
. Por tanto, el comando
ls
producira un listado de ficheros encontrados en el directorio de
trabajo actual. El flag -s
define a la opcion
de tamano, por tanto
ls -s
ordena que ls tambien informe el tamano (en bloques de 512 caracteres) de cada uno de los ficheros del listado. El Manual de Referencia de UNIX proporciona las opciones disponible para cada comando en su respectiva Seccion. El comando ls - por ejemplo - consta de una gran cantidad de opciones utiles e interesantes. La mayoria de los demas comandos carecen de flags, o solo disponen de una o dos. Como no resulta sencillo recordar las opciones de aquellos comandos que no se usan a menudo, la mayoria de las utilidades de UNIX suelen preferir realizar solo una o dos funciones, evitando contar con muchas opciones dificiles de memorizar.
Salida a Ficheros
Los comandos que normalmente leen su entrada o escriben se salida en el terminal, tambien pueden ejecutarse con esta entrada y/o salida suplida a/desde un fichero.
Supongamos que deseamos guardar la fecha actual en un archivo llamado "ahora".
La orden date
imprime la fecha actual en el terminal.
Esto se debe a que la salida estandar predeterminada para el
comando date es nuestro terminal, por lo tanto, eso es lo que
cumple. El shell nos permite redirigir la salida estandar de un
comando por medio de una notacion, si se recurre al
metacaracter >
y se invoca el nombre de fichero
donde se colocara el resultado de salida. Por tanto, el comando
date > ahora
ejecuta el comando de date de modo que su salida
estandar se dirija al fichero ahora
en lugar de
hacerlo al terminal. Por lo tanto, el resultado del comando cumple en
colocar la fecha y hora actuales en el fichero ahora
. Es
importante saber que el comando date no sabe que su salida ira
a un fichero en lugar de hacerlo al terminal: la shell realiza la
redireccion antes que el comando comience a
ejecutarse.
Otra aspecto a tener en cuenta aqui es innecesario generar el fichero
ahora
previamente a la ejecucion del comando date;
si el fichero no existe, la shell lo crea. ¿Y si el archivo existiera?
En tal caso, ¡sus contenidos previos habrian sido sido desechados!
Existe una opcion de shell llamada noclobber
para evitar
que esto accidentalmente suceda; se analiza en la seccion 2.2.
Normalmente, el sistema guarda los ficheros creados por usted con
>
junto a todos los demas ficheros. Por lo tanto, el
resultado predeterminado es que los ficheros sean permanentes. Si desea
crear un fichero para su eliminacion automatica, puede comenzar su
nombre de fichero con un caracter #
. Dicho caracter
"numeral" denota el hecho de que el fichero sera un fichero
borrador. El sistema eliminara dichos archivos tras un par de dias
(o mas pronto si el espacio resulta limitado). Por lo tanto, al ejecutar
el comando date como se indico anteriormente, no querriamos que
almacene la salida para siempre realmente, de manera que mas
probablemente usariamos:
date > #ahora
Metacaracteres en la Shell
Como el caso del >
indicado anteriormente, la shell
tiene una gran variedad de caracteres especiales que ordenan
funcionalidades especiales. Decimos que para la shell, dichas notaciones
guardan significado sintactico y semantico. Por lo
general, para la shell la mayoria de los caracteres que no son letras o
digitos guardan un significado especial.
Los metacaracteres normalmente surten efecto solo cuando la shell lee nuestra entrada [N.d.T. el teclado del terminal]. Como consecuencia, no debemos preocuparnos al usar metacaracteres en la redaccion de una pieza que enviaremos a traves de mail, o al mecanografiar textos o introducimos datos en cualquier otro programa. Debemos tener presente que la shell solo lee entrada cuando ha senalado su prompt ''% ''.
Mas adelante habremos de aprender brevemente una manera de citar que nos permite recurrir a metacaracteres sin que la shell los trate de forma especial.
Entrada desde archivos; canerias
Ya hemos aprendido como redirigir la salida estandar de un comando a un fichero. Es posible tambien redirigir la entrada estandar de un comando desde un fichero. Esto a menudo es innecesario pues la mayoria de los comandos leen desde un fichero siempre que su nombre sea provisto como argumento. Podemos dar el comando
sort < datos
para ordenar ejecucion del programa sort redirigiendo su
entrada estandar - el lugar desde donde el comando leera
normalmente su entrada -- desde el fichero datos
.
Normalmente, recurririamos a:
sort datos
Dejando que el comando sort abra un fichero
datos
por si mismo para proveerse la entrada que necesita
(ya que esto es mas facil de mecanografiar). Debemos notar que si solo
mecanografiamos:
sort
entonces el programa sort ordenaria las lineas desde la entrada estandar. Como en tal caso no redirigimos la entrada estandar, el reordenamiento de lineas se producira tras que mecanografiar las lineas consecutivamente en el terminal, al presionar ^D para indicar Fin de Fichero.
Una funcionalidad mas util es la posibilidad combinatoria que da redirigir la salida estandar de un comando con la entrada estandar de otro, por ejemplo para ejecutar los comandos en una secuencia conocida como caneria. Por ejemplo, el comando
ls -s
produce normalmente un listado de los ficheros de nuestro directorio, especificando el tamano de cada uno (en bloques de 512 caracteres). Si nos interesa aprender cual de nuestros ficheros es el mas grande, podriamos querer ordenar este listado segun su tamano, en lugar de hacerlo alfabeticamente segun su nombre (la cual es la manera en la que ordena el listado el programa ls por defecto). Podemos observar las muchas opciones de ls para ver si consta de alguna opcion que haga esto, pero eventualmente descubririamos que no existe ninguna [N.d.T., en la version de 1983 de ls; las versiones actuales la tienen, por supuesto]. En lugar de ello, podremos usar un par de flags simples del comando sort combinandolas con ls para lograr lo que nosotros queremos.
La opcion -n
de sort, especifica un
ordenamiento numerico en lugar de un ordenamiento alfabetico. Por
tanto
ls -s | sort -n
especifica que la salida del comando ls argumentado con la
opcion -s
deba ser entubado al comando
sort argumentado segun su opcion de ordenamiento numerico. Esto
nos proporcionara una lista de nuestros ficheros ordenada por tamano
(pero listando primero el mas pequeno). Luego podremos utilizar la
opcion flag de ordenamiento inverso -r
de
sort, junto al comando head en combinacion con aquel,
ingresando:
ls -s | sort -n -r | head -5
Hemos tomado aqui un listado de nuestros ficheros ordenados
alfabeticamente (cada uno con su tamano en bloques), hemos pasado esto a
la entrada estandar del comando sort solicitando que las ordene
de forma numerica invertida (la mas grande primero). Dicha salida ha
sido vertida luego al comando head, encargado de presentar solo
las primeras pocas lineas (en este caso, las primeras 5
).
Como resultado, este comando cumple en presentar los nombres y tamano de
nuestros 5 ficheros mas grandes.
La notacion introducida anteriormente se conoce como mecanismo de
cano. Los comandos separados por caracteres |
resultan conectados entre si por la shell y la salida estandar
de cada uno de ellos es pasado a la entrada estandar del
siguiente. El comando mas a la izquierda en la caneria generalmente
recibe su entrada estandar del teclado del terminal, y el comando mas a
la derecha volcara su salida estandar en el presentador del terminal.
Mas adelante ofreceremos ejemplos de canerias adicionales cuando
discutamos el mecanismo historial; un uso importante de caneria alli
ilustrado es el de redirigir informacion al impresor de lineas.
Nombres de Ficheros
Para ser ejecutados, muchos comandos necesitan estar argumentados con
nombres de fichero. Los nombres de ruta de UNIX
consisten en una cantidad de componentes separados por un
caracter de barra /
. Con excepcion del ultimo componente,
cada uno de ellos nombran un directorio en el cual reside el
siguiente componente, denotando - en efecto - la ruta de
directorios que debe seguirse para alcanzar el fichero en cuestion. Por
lo tanto, la ruta
/etc/motd
especifica un fichero en el directorio etc
que es un
subdirectorio del directorio raiz /
. Dentro de
este directorio se encuentra el fichero denominado motd
(significa "mensaje del dia"). Se dice que un nombre de ruta
comenzado con una barra /
es absoluto pues que
cuenta con la definicion jerarquica completa desde su raiz. En
tanto, los nombres de ruta que no comienzan con /
se interpretan como parte del directorio de trabajo actual -
por defecto se trata de su directorio home aunque puede
cambiarlo dinamicamente con el comando de cambio de directorio
cd. Se dicen que estos nombres de ruta son
relativos al directorio de trabajo, ya que se descubren
iniciando una busqueda desde el directorio de trabajo, descendiendo a
los niveles inferiores de la jerarquia de directorio siguiendo cada
componente del nombre de ruta. Si el nombre de ruta carece de
/
alguna, entonces el fichero esta contenido en el
directorio de trabajo (y la ruta simplemente consiste en el nombre
de fichero en el directorio de trabajo). Los nombres de ruta
absolutos no tienen relacion al directorio de trabajo.
La mayoria de los nombre de ficheros constan de una cantidad
de caracteres alfanumericos y puntos .
. De hecho, es
posible usar todos los caracteres impresos exceptuando la barra
/
en los nombres de archivo. Es inconveniente
emplear la mayoria de los caracteres no alfabeticos en los nombres de
fichero pues muchos de ellos guardan significado especial para la shell.
El caracter punto .
no es un metacaracter de la shell, y se
utiliza a menudo para separar la extension del nombre de
archivo de la base del nombre. Por tanto
prog.c prog.o prog.errs prog.out
son cuatro ficheros relacionados. Comparten la porcion base
(una porcion base es la parte del nombre que queda por delante del
.
final, y que sigue a los caracteres que no sean
.
). El fichero prog.c
podria ser el codigo
fuente de un programa de lenguaje C, el fichero prog.o
podria el fichero de codigo objeto correspondiente, el fichero
prog.errs
los errores resultantes de una compilacion del
programa, y el fichero prog.output
la salida de un programa
compilado.
Si deseamos referirnos a los cuatro ficheros a la vez en un comando, podriamos utilizar la notacion
prog.*
Esta palabra antes que el comando del cual es un argumento sea
ejecutado, resulta formulaicamente expandida por la shell a una
lista de nombres que comienzan con prog
, El caracter
*
aqui coincide con "cualquier secuencia de caracteres en
un nombre de fichero" (incluyendo un secuencia vacia). Los nombres que
coinciden resultan ordenados alfabeticamente y puestos en la lista
de argumentos del comando. Por lo tanto el comando
echo prog.*
dara eco a los nombres
prog.c prog.errs prog.o prog.out
Note que los nombres estan ordenados aqui, y en un orden distinto al que listamos arriba. El comando echo recibe cuatro palabras como argumentos, incluso aunque solo mecanografiemos directamente una palabra como argumento. Las cuatro palabras fueron generados por la expansion de nombre de fichero a partir de una unica palabra de entrada.
Tambien hay disponibles otras notaciones para expansion de nombre
de fichero. El caracter ?
coincide con "cualquier
caracter unico en un nombre de fichero". Por tanto
echo ? ?? ???
Dara como eco una linea de nombres de fichero; primero aquellos con nombres de un solo caracter, luego aquellos con nombres de dos caracteres, y finalmente aquellos con nombres de tres caracteres. Los nombres de cada longitud de caracteres reciben un orden independiente.
Otro mecanismo consiste en en entrecorchetado, esto es, una
secuencia de caracteres entre [
y ]
. Esta
metasecuencia coincide con "cualquier caracter unico del conjunto
entrecorchetado". Por tanto
prog.[co]
coincidira con
prog.c prog.o
del ejemplo anterior. Tambien podemos poner dos caracteres rodeando
un -
en una notacion para "caracteres definidos por rango".
Por tanto
cap.[1-5]
podria concidir los ficheros
cap.l cap.2 cap.3 cap.4 cap.5
si existiesen. Esto es la abreviacion para
cap.[12345]
y es, por ello, equivalente en todo sentido.
Otro punto importante a notar es que si una lista de palabras argumento de comando (una lista argumental) contiene sintaxis de expansion de nombre de fichero, y si esta sintaxis de expansion de nombre de fichero falla en hacer coincidir cualquier nombre de fichero existente, en tal caso la shell considerara esto como error y presentara un diagnostico "Sin coincidencia":
No match.
y no cumplira la ejecucion del comando.
Otro punto muy importante es que los ficheros con el caracter de
barra invertida \
al comienzo del nombre son tratados
especialmente. Ni el *
ni el ?
o el mecanismo
de entrecorchetado [
y ]
encontraran
coincidencia. Esto impide coincidir accidentalmente los nombres
.
y ..
del directorio de trabajo - los cuales
tienen significados especiales para el sistema - asi como otros ficheros
tales como .cshrc
, que no son visibles normalmente. El rol
especial del fichero .cshrc sera discutido mas adelante.
Otro mecanismo de expansion de nombre de fichero ofrece acceso
abreviado al nombre de ruta del directorio home de otros
usuarios. Esta notacion consiste en el caracter tilde ~
,
seguida por el nombre de login de otro usuario. Por ejemplo, la palabra
~bill
dirigira al nombre de ruta /usr/bill
(siempre que el directorio home del tal Bill sea
/usr/bill
, claro esta [N.d.T. actualmente,
/home/bill
]). Como en grandes sistemas, los usuarios pueden
contar con varios directorios de login distribuidos a lo largo de muchos
volumenes de disco diferentes, con diferente nombres de directorio
prefijo, esta notacion otorga una forma confiable de acceder a los
ficheros de otros usuarios.
Un caso especial que incumbe a esta notacion es el de un tilde
~
aislado, por ejemplo ~/mbox
. Esta notacion
resulta expandida por la shell para representar al fichero
mbox
en su propio directorio home, por ejemplo, en
/usr/bill/mbox
para mi en Ernie Co-vax, la maquina VAX del Dpto. de Ciencias del Computo de la
universidad de California Berkeley, donde fue preparado este documento.
Esto puede resultar muy util si usted ha usado cd para cambiar
a otro directorio, y ha encontrado un fichero que desea copiar con
cp. Si doy el comando
cp esefichero ~
la shell expandira dicho comando a
cp esefichero /usr/bill
ya que mi directorio home es /usr/bill
.
Existe tambien un mecanismo que usa los caracteres de abrir llave
{
y cerrar llave }
para abreviar un "conjunto
de palabras que constan de partes comunes" pero no pueden ser abreviadas
a traves de los mecanismos anteriormente nombrados ya que no son
ficheros, o son nombres de ficheros que aun no existen y por lo tanto no
pueden ser descriptos convenientemente aun. Tal mecanismo sera descripto
bastante mas adelante, en la seccion 4.2,
pues se recurre a ellas con poca frecuencia.
Citado
Ya hemos visto cierta cantidad de metacaracteres respetados por la shell. Dichos metacaracteres tienen una complicacion dificultosa: no podemos usarlos directamente como partes de palabras. Por lo tanto el comando
echo *
no dara como eco el caracter *
. O bien dara como eco una
lista ordenada de nombres de ficheros del directorio de trabajo
actual, o bien imprimira el mensaje No match
si en el
directorio de trabajo no existen ficheros.
El mecanismo recomendado para poner aquellos caracteres que no son
numeros, digitos /
, .
, o -
en una
palabra argumento de un comando es apostrofarlo (encerrarlo entre
apostrofos '...
'.. Por ejemplo:
echo '*'
Existe un caracter especial signo cierre de admiracion !
que es es utilizado por el mecanismo historial de la shell que
no puede ser anulado apostrofando de la forma anterior:
'!'
. Para anular su significado especial de la
shell, tanto el signo de cierre de admiracion !
como el
caracter apostrofo '
en si deben ser precedidos por un
unico caracter de barra invertida \
. Por tanto
echo \'\!
imprime
'!
Estos dos mecanismos resultan suficientes para poner cualquier caracter impreso en una palabra que sin que sean considerados un argumento de un comando de la shell. Pueden combinarse, como en
echo \''*'
lo que imprime
'*
ya que la primer barra invertida \
ha anulado el primer
apostrofo '
, mientras que el asterisco *
aparecia entre apostrofos '
.
Suspension de Comandos
Existen varias maneras de forzar su interrupcion tras ejecutar un comando y mientras la shell aguarda su cumplimiento. Por ejemplo, si ingresa el comando
cat /etc/passwd
El sistema presentara en su terminal una copia de un listados de todos los usuarios del sistema. Es probable que esta presentacion continue por varios minutos, a no ser que la interrumpa [N.d.T en la VAX 11/750 de Berkeley no era particularmente veloz y tenia miles de usuarios!]. Puede enviar una senal INTERRUPT al comando cat presionando la tecla DEL o RUBOUT de su terminal. La senal INTERRUPT provocara su finalizacion, pues cat no ha sido implementado para considerar dicha senal de otra manera diferente. La shell nota que el comando cat ha finalizado y ofrecera nuevamente un prompt ''% ''. Si presiona INTERRUPT nuevamente, la shell simplemente repetira su prompt en lugar de interrumpirse (lo que tendria el efecto de cerrar su sesion, al igual que cat). La shell interpreta las senales INTERRUPT y escoge continuar ejecutando comandos en lugar de finalizar.
Otra manera en la cual muchos programas pueden finalizar su ejecucion
es recibiendo un mensaje de fin-de-fichero a traves de su
entrada estandar. Este es el motivo por el cual el programa
mail del primer ejemplo anterior terminaba cuando presionabamos
^D, lo que genera un fin-de-fichero
desde
la entrada estandar [N.d.T: el teclado]. La shell tambien finaliza
cuando obtiene un fin-de-fichero
, a lo que presentara el
mensaje logout
; UNIX entonces cierra su sesion de terminal.
Esto significa que si ingresa demasiadas ^D, podria
accidentalmente desligarse del sistema, la shell cuenta con un mecanismo
para impedirlo. Esta opcion ignoreeof
sera discutida en la
seccion 2.2
Si la entrada estandar de un comando se ha redirigido desde un fichero, entonces por lo general finalizara cuando el fichero que funciona de entrada llegue al final. Por tanto, si ejecutamos
mail bill < preparado.txt
el comando mail finalizara sin que ingresemos
^D. Esto se debe a que leera el fichero
preparado.txt
hasta alcanzar su fin-de-fichero, el
cual pusimos un mensaje para Bill con un programa editor. Tambien
podriamos haber hecho
cat preparado.txt | mail bill
pues el comando cat habria ofrecido el texto a traves del entubado a la entrada estandar del comando mail. Una vez cumplido en presentar el fichero, el comando cat habria finalizado - clausurando la tuberia - y el comando mail recibiria un fin-de-fichero desde el, finalizando su operacion. Si bien el resultado es el mismo, esta forma de utilizar un cano parece mas complicado que redirigir un fichero a la entrada, de modo que es mas probable usar la primer forma. Estos comando podrian tambien haber sido detenidos enviandoles un INTERRUPT.
Otra posibilidad para suspender un comando es suspender su ejecucion de forma temporal, lo que nos posibilita continuar su ejecucion posteriormente. Esto se realiza enviando una senal STOP por medio de ^Z. Esta senal provoca que la suspension de todos los comandos en ejecucion en el terminal - usualmente uno, pero podrian ser varios en caso de haberse recurrido a la ejecucion de una tuberia. En otro aspecto, no resultan afectados de manera alguna por la senal STOP.
Una vez que el comando original permanece suspendido, puede recurrirse a ejecutar otros comandos cualquiera. El comando suspendido puede ser continuado por medio del comando fg sin argumentos. La shell informa entonces cual es el comando a continuar, y proseguira con su ejecucion. La suspension no afecta de manera alguna la ejecucion del comando, a no ser que cualquier fichero de entrada en uso del comando suspendido sea alterado de alguna manera durante la suspension. Esta funcionalidad puede resultar muy util durante la edicion, de necesitarse revisar otro fichero antes de continuar. A continuacion, se da un ejemplo de una suspension de comando:
% mail haroldo
Alguien acaba de copiar un fichero enorme a mi directorio de usuario.
El fichero se denomina
^Z
Stopped
% ls
tremenda_broma
prog.c
prog.o
% jobs
[1] + Stopped
mail haroldo
% fg
mail haroldo
"tremenda_broma". ¿Sabes quien lo hizo?
EOT
%
En este ejemplo, un usuario esta redactando una pieza de correo a
haroldo pero olvida el nombre de un fichero que queria mencionarle.
Suspende el comando mail ingresando ^Z. Una
vez que la shell nota la suspension del programa de correo, presenta
Stopped
y le ofrece un prompt, aguardando un nuevo comando.
Acto seguido el individuo ingresa el comando ls para recordar
el nombre del fichero en cuestion. Usa el comando jobs para
descubrir cual comando se encuentra suspendido, y consecuentemente
ingresa el comando fg para continuar la ejecucion del unico
programa detenido, el mail a haroldo. Luego continua dando
entrada a mail, y una vez concluida la redaccion, la finalizo
con ^D, que denota el final del mensaje, ante lo cual
el programa mail presenta EOT
de
fin-de-transmision.
jobs
El comando jobs presenta un listado de los comandos se encuentran suspendidos. La orden ^Z solo deberia mecanografiarse al comienzo de linea, pues de enviarse dicha senal desde el teclado en cualquier otro lugar diferente al comienzo de linea, la shell descarta ("borra") toda la linea. Esto mismo sucede con las senales INTERRUPT y QUIT. Se ofrece mayor informacion sobre suspender trabajos y controlarlos se ofrece en la seccion 2.6.
Puede ser necesario recurrir a una interrupcion de manera poco ortodoxa si se escribe o ejecuta programas que no estan completamente depurados. Tal medida puede lograrse mecanografiando *^*, lo cual envia la senal QUIT. No resultaria extrano que esto provoque que la shell presente un mensaje similar a :
Quit (Core dumped)
denotando la creacion de un fichero core
conteniendo
informacion sobre el estado del programa a.out
, hecho
sucedido no bien la senal QUIT produjo la terminacion. Podra examinar
este fichero por usted mismo, o bien enviar informacion al autor del
programa informandole donde esta el fichero core
.
Si corre comandos en segundo plano (segun lo explicado en la seccion 2.6), entonces dichos comandos ignoraran las senales INTERRUPT y QUIT del terminal. Para detenerlas debe utilizar el comando kill.
more
Si desea examinar detenidamente la salida de un comando sin hacer que el mismo desaparezca de la pantalla (como cuando recurrimos a interrumpir la salida del comando cat /etc/passwd), podra usar el comando
more | /etc/passwd
El programa paginador more pausa tras cada pantallazo,
presentando el mensaje - - More- -
, en cuyo momento usted
podra presionar la barra espaciadora para obtener otro
pantallazo, o Retorno para obtener otra linea, o una
q para finalizar el programa more. Tambien
puede usar more como filtro. Por ejemplo
cat /etc/passwd | more
funciona de forma similar al comando more de ejecucion directa indicado anteriormente.
Para detener la presentacion en el terminal de los comandos sin involucrar a more, podra recurrir a las teclas ^S para pausar la salida. Podra continuar la presentacion en el terminal mediante ^Q (o cualquier otra tecla, si bien por lo general se utiliza ^Q pues esta solo reinicia la presentacion de salida, sin convertirse en entrada de otros comandos posibles del programa que esta usted ejecutando).
Podra operar este metodo adecuadamente si usa terminales de baja velocidad, pero con 9600 baudios o mas probablemente encontrara dificultoso ingresar ^S y ^Q lo suficientemente rapido como para pausar adecuadamente la salida. Este es el motivo por el cual por lo general se recurre al programa more.
Una posibilidad adicional es recurrir al caracter de descartar-salida ^O. Al tipear este caracter, toda la salida del comando actual se descarta (rapidamente) hasta que se produzca la siguiente lectura de entrada, o bien hasta la aparicion del siguiente prompt. Puede utilizar descartar-salida para permitir la finalizacion de un comando sin tener que cumplir la impresion de grandes cantidades texto de salida en un terminal lento. ^O opera como un conmutador, de manera que es posible conmutar dicho descarte ingresando nuevamente ^O mientras se produce el descarte de salida.
¿Ahora que?
Hasta ahora hemos considerado varios mecanismos de la shell y aprendimos bastante sobre la manera en la cual esta funciona. Las secciones restantes avanzaran mas en el funcionamiento interno de la shell, pero seguramente querra probar usando la shell Csh antes de continuar.
La siguiente seccion introducira muchas funcionalidades particulares a Csh, de modo que debe cambiarse la shell a csh antes de comenzar a leerla.
Ahora esta listo para intentar usar csh.
2. Detalles de la Shell para Usuarios de Terminal
Inicio de la shell y terminacion
Al iniciar sesion en el sistema, este comenzara la ejecucion de una
shell. Esta ingresara a su directorio home y comenzara por leer
los comandos de un fichero llamado .cshrc
localizado en
dicho directorio. Todas las C shells que eventualmente inicie anidadas
durante su sesion de terminal leeran este fichero tambien. Mas adelante
veremos que tipos de comandos es util colocar alli, por ahora no es
necesario contar con este fichero, y la C shell no se quejara por su
ausencia.
La shell de login - aquella que se ejecuta tras su inicio de
sesion al sistema leera los comandos contenidos en el fichero
.cshrc
ya mencionado - tras lo cual leera comandos
contenidos en un fichero .login
(tambien en su directorio
home). Este fichero contiene comandos que usted desea ejecutar
cada vez que inicie una sesion del sistema UNIX. Mi fichero
.login
contiene lo siguiente:
set ignoreeof
set mail=(/usr/spool/mail/bill)
echo "${prompt}users" ; users
alias ts \
'set noglob ; eval `tset -s -m dialup:vt100 -m plugboard:?hp2621nl *`';
ts; stty intr ^C kill ^U crt
set time=15 history=10
msgs -f
if (-e $mail) then
echo "Tiene correo! ${prompt}"
mail
endif
Este fichero contiene varios comandos para que UNIX los ejecuta toda
vez que inicie mi sesion de terminal. El primero de ellos es un
set. Este comando es interpretado en forma directa por la C
Shell, y configura la variable de shell ignoreeof
, que
impide que la C shell desligue sesion al presionar ^D.
En lugar de ello utilizo el comando logout para cerrar la
sesion del sistema. Al establecer la variable mail
, le pido
a la C shell que revise los correos entrantes por mi; cada 5 minutos la
C shell revisa este fichero y me informa si me ha llegado correo
electronico. Una alternativa en lugar de usar tal set es
colocar en su lugar el comando
biff y
lo que hara que se me notifique inmediatamente al arribo de correo (mostrandome unas pocas primeras lineas del nuevo correo).
Posteriormente configure la variable de shell time
a
15
, lo que hace que la shell imprima automaticamente lineas
de estadistica para aquellos comandos cuya ejecucion requiera al menos
15 segundos de tiempo de CPU. La variable history
se
establece en 10
, indicando que deseo que la C shell
recuerde los ultimos 10 comandos mecanografiados en su listado de
historial (descripto mas adelante).
Cree un alias "ts" que ejecuta un comando tset
estableciendo los modos de terminal. Los parametros de tset
indican los tipos de terminal que uso a menudo cuando no estoy en el
puerto normal de la VAX. Luego ejecuto ts
y tambien uso el
comando stty para cambiar el caracter de interrupcion a
^C y el caracter de eliminar linea a
^U.
Luego corro el programa msgs que me provee cualquier mensaje
de sistema que no hubiese visto antes; la opcion -r
le
impide decirme cualquier cosa si no hay mensajes nuevos. Finalmente, de
existir mi fichero de casilla de correo, entonces ejecuto el programa
mail para procesar mi correspondencia.
Cuando los programas mail y msgs terminan, la shell
finalizara de procesar mi fichero .login
y comenzara a leer
comandos desde el terminal, saludando a cada uno de ellos con el prompt
''% ''. Cuando cierro sesion (ejecutando el comando logout) la
C shell imprimira logout
y ejecutara comandos del fichero
.logout
si este esta presente en mi directorio
home. Cumplido aquello, la shell terminara y UNIX me
desconectara del sistema. Si el sistema no se apaga, recibire un nuevo
mensaje de inicio de sesion. En cualquier caso, luego del mensaje
logout
la shell estara conminada a terminar, y no tomara en
cuenta otra entrada desde mi terminal.
Variables de la Shell
La shell mantiene un conjunto de variables. Vimos
anteriormente las variables history
y time
que
tenian valores de 10
y 15
. De hecho, cada
variable de shell cuenta como valor un arreglo de cero o mas
cadenas. Las variables de la shell pueden recibir valores por
medio del comando set. Tiene varias formas, la mas util de las
cuales fue indicada anteriormente y es
set nombre=valor
Las variables de shell pueden utilizarse para almacenar valores que deben ser usados por posteriores comandos a traves del mecanismo de sustitucion. Las variables de shell mas comunmente referenciadas son aquellas a las cuales la shell misma refiere. Sin embargo, al modificar los valores de dichas variables, uno puede afectar directamente el comportamiento de la shell.
Una de las variables mas importantes es la variable
path
. Ella contiene una secuencia de nombres de directorio
que la shell analizara al buscar comandos. El comando set sin
argumentos presenta los valores asignados de todas las variables
definidas (usualmente decimos "establecidas") en la shell. Los valores
por defecto de path presentados por set son
% set
argv ()
cwd /usr/bill
home /usr/bill
path (. /usr/ucb /bin lusr/bin)
prompt %
shell /bin/csh
status 0
term vt100
user bill
%
Esta salida indica que la variable path
apunta al
directorio actual .
, y luego a los directorios
/user/ucb
, /bin
y /usr/bin
. Los
comandos que pueden escribir podrian estar en .
(usualmente
uno de sus directorios). Los comandos desarrollados en Berkeley residen
en /usr/ucb
, mientras que los comandos desarrollados en los
Laboratorios Bell residen en
/bin
y /usr/bin
.
Cierta cantidad de programas desarrollados localmente en el sistema
residen en el directorio /usr/local
. Si deseamos que todas
las shells que invocamos tengan acceso a estos nuevos programas,
podremos establecerlos en nuestro fichero .cshrc
en nuestro
directorio home mediante el comando
set path=(. /usr/ucb /bin /usr/bin /usr/local)
Intente hacer esto y tras cerrar sesion y volver a iniciarla, ejecute set nuevamente para comprobar que el valor establecido a path ha cambiado.
Una cosa que deberia tener presente es que la shell examina cada
directorio que ha establecido en su path y determina cuales
comandos estan alli contenidos. Exceptuando el directorio actual
.
- el cual la shell trata especialmente - esto significa
que si se agregan comandos a un directorio en su path
posteriormente a la iniciar la shell, no seran necesariamente
encontrados por la shell. De desear utilizar un comando que agregado de
esta manera, debera indicar primero el comando
rehash
para refrescar que la shell, recalculando su tabla interna de
locaciones de comandos, que se utiliza en la busqueda de los comandos
agregados recientemente. Ya que toda vez que ejecuta un comando, la
shell pesquisa en el directorio actual .
, establecerlo en
la path al principio de la especificacion opera por lo general
de forma equivalente, reduciendo los tiempos de espera.
Otras variables incorporadas utiles consisten en las variables
home
, que muestra su directorio home,
cwd
que contiene su directorio de trabajo actual, y la
variable ignoreeof
(que dice a la shell que no se desligue
al recibir un caracter fin de fichero desde un terminal (como
se menciono anteriormente, lo cual puede establecerse en su fichero
.login
). La variable ignoreeof
es una de
aquellas en las que la shell hace caso omiso de su valor asignado, solo
analiza si dice set
o unset
denotando si esta
establecida o no, respectivamente. Por lo tanto, para establecer esta
variable se indica simplemente
set ignoreeof
y para desestablecerla, se indica
unset ignoreeof
Los comandos anteriores no otorgan valor alguno a la variable
ignoreeof
, pues esta no los requiere ni los desea en lo absoluto.
Finalmente, algunas de las otras variables incorporadas de la shell
en uso son las variables noclobber
y mail
.
La metasintaxis
> fichero
que redirige la salida estandar de un comando, sobrescribira y
destruira los contenidos previos de fichero
, y como tal
es posible sobrescribir accidentalmente un fichero valioso. Si prefiere
que la shell no sobrescriba ficheros de esta manera tan liviana, puede
indicar en su fichero .login
set noclobber
Tras loguearse nuevamente, intente ordenar
date > ahora
y comprobara un diagnostico de error si ahora
ya existe. Para producir la sobrescritura de ahora
si
realmente lo desea, debe mecanografiar
date >! ahora
El >!
es una metasintaxis especial que indica que
esta bien "darle una paliza" a fichero
[NdT: clobber
significa "apalear", y es la jerga utilizada para denotar la
sobrescritura inadvertida del fichero
].
El espacio en blanco entre el
!
y la palabraahora
es critico, ya que!ahora
invocaria las mecanismo de historial, que tiene un efecto completamente distinto.
Historial de la shell
La C Shell puede mantener una lista de historial en la cual coloca las palabras de los comandos ya ejecutados. Es posible reutilizar comandos o palabras de los comandos en la formacion de nuevos comandos a traves de una notacion. Tal mecanismo puede utilizarse para repetir comandos previos o corregir errores de mecanografiado menores en los comandos.
A continuacion se ofrece una sesion de ejemplo que involucra el uso
tipico del mecanismo de historial de la C Shell. En tal ejemplo se
recurre a un terminal para revisar con cat el contenido del
fichero bug.c
, un programa muy simple en lenguaje C que
contiene uno o dos bugs. Luego intentamos correr el compilador CC con el,
refiriendo el fichero nuevamente a traves de la orden !$
(lo que significa "el ultimo argumento del comando anterior").
Aqui,
!
es el metacaracter de invocacion al mecanismo historial, mientras que$
refiere al "argumento final", en analogia al uso de$
en el editor ed, que significa "final de la linea").
La C Shell da eco del comando tal como se hubiese mecanografiado sin
recurrir al mecanismo de historial, y luego lo ejecuta. Ya que la
compilacion produce un diagnostico de error, se ejecuta el
editor en el fichero que se intento compilar, se corrige el bug, y se
ordena repetir la ejecucion del compilador de C con el mecanismo de
historial. Para ello, esta vez se simplemente indicamos el comando
!c
, que significa "repetir el ultimo comando historiado que
comenzaba con la letra c
"). De haber existido otros
comandos historiados que comenzaban con la letra c
, se
podria haber tenido que recurrir a especificar mas detalladamente, ya
sea con !cc
, o incluso !cc:p
(que hubiese
ordenado "imprime el ultimo comando que comenzaba con cc
,
sin ejecutarlo").
% cat bug.c
main ()
{
printf("hola);
}
% cc !$
cc bug.c
"bug.c", line 4: newline in string or char constant
"bug.c", line 5: syntax error
% ed !$
ed bug.c
29
4s/);/"&/p
printf("hola");
w
30
q
% !c
cc bug.c
% a.out
hello% !e
ed bug.c
30
4s/la/la\\n/p
printf("hola\n");
w
32
q
% !c -o bug
cc bug.c -o bug
% size a.out bug
a.out: 2784+364+1028 = 4176b = 0x1050b
bug: 2784+364+1028 = 4176b = 0xl050b
% ls -l !*
ls -1 a.out bug
-rwxr-xr-x 1 bill 3932 Dec 19 09:41 a.out
-rwxr-xr-x 1 bill 3932 Dec 19 09:42 bug
% bug
hola
% num bug.c | spp
spp: Command not found.
% ^spp^ssp
num bug.c | ssp
1 main()
3 {
4
printf("hola\n");
5 }
% !! | lpr
num bug.c | ssp | lpr
%
Tras recompilar, se ejecuto el fichero a.out
resultante,
y se noto que un bug aun persistia, por lo cual se corrio nuevamente el
editor. Tras corregir el programa se corrio el compilador C nuevamente,
asociado esta vez al comando extra -o bug
, que indica al
compilador colocar el binario resultante en un fichero bug
(en lugar de utilizar el nombre a.out
al que recurre el
compilador por defecto).
El mecanismo de historial puede usarse por lo general en cualquier lugar de la formacion de nuevos comandos y pueden colocarse otros caracteres antes y despues de los comandos sustituidos.
Luego se ejecuto el comando size para comprobar cuan grande
eran las imagenes binarias resultantes de los programas compilados, y
luego se recurrio al comando ls -l
con la misma lista
argumental, denotando la lista argumental .
.
Finalmente, se ejecuto el programa bug
para comprobar si su
salida es de hecho correcta.
Se ejecuto el comando num con el fichero bug.c
para obtener un listado numerado del programa. Acto seguido se quiso
entubar la salida de num a traves del filtro ssp a fin
de comprimir las lineas en blanco, pero se cometio un error de
mecanografia, escribiendo erroneamente spp
. Para corregirlo
el yerro se recurrio a un mecanismo de sustitucion de shell, colocando
la cadena erronea y la correcta entre caracteres ^
(pues
opera de igual forma al comando "sustituir" del editor ed). Finalmente,
se reitero el mismo comando con !!
, pero enviando su salida
a la impresora de linea.
Existen otros mecanismos para reiterar comandos. El comando history imprime cierta cantidad de comandos previos asociados a una numeracion, por medio de la cual se los puede referenciar.
Hay una manera de referir a un comando previo buscandolo a por medio de una cadena que aparezca en el, y existe otra - menos util - para seleccionar argumentos a incluir en un nuevo comando. Se ofrece en las paginas man de csh una descripcion completa de todos estos mecanismos, asi como en el Manual de Programador de UNIX.
Alias
La C shell cuenta con un mecanismo de alias que puede ser utilizado para efectuar transformaciones en los comandos de entrada. Puede utilizarse este mecanismo para simplificar los comandos que interesan, supliendo a dichos comandos los argumentos deseados por defecto, o bien para operar transformaciones a los comandos y sus argumentos.
La funcionalidad de alias es similar a una facilidad de macros.
Algunas de las funcionalidades obtenidas por medio de alias tambien pueden lograrse usando ficheros de interprete de comandos, pero estas se llevaran a cabo en otra instancia anidada de la C shell, y no podran afectar directamente el ambiente de la shell actual o involucrar comandos tales como cd (que deben correr en la shell actual).
Por ejemplo, supongamos que existe una nueva version del programa de
correo en el sistema que se llama newmail, la cual deseamos
utilizar en lugar del programa de correo estandar mail. Si en
su fichero .cshrc
dispone el siguiente comando de shell
alias mail newmail
la C shell transformara una linea de entrada dada segun la forma
mail bill
en una llamada de ejecucion a newmail
.
En concreto, supongamos ahora que deseamos el comando ls
presente siempre los tamanos de los ficheros en su listado (o sea, que
ls utilice siempre el argumento -s
). Podemos
usar
alias ls ls -s
o incluso
alias dir ls -s
creando una nueva sintaxis de comando llamada dir
que
ejecute un comando ls -s
. Si decimos
dir ~bill
entonces la C shell lo traducira como
ls -s /mnt/bill
De esta forma, el mecanismo de alias puede usarse para proveer nombres cortos a comandos, para proveer argumentos por defecto, y para definir nuevos comandos cortos en relacion a otros comandos. Tambien es posible definir alias que contengan multiples comandos o canerias, mostrando donde se encuentran los argumentos del comando original para sustituirlos por medio de las facilidades del mecanismo de historial. Por tanto la definicion
alias cd 'cd \!* ; ls '
haria un comando ls tras efectuar todo cambio de directorio
con el comando cd. Apostrofamos entre caracteres '
la definicion entera del alias a fin de impedir que se produzcan la
mayoria de las substituciones, y que el caracter :
sea
reconocido como un metacaracter. En este caso, al introducir el comando
de alias, este !
resulta anulado con una \
para impedir que resulte interpretado. Aqui, el \!*
sustituye la lista argumental completa del comando cd
previo al alias (sin dar un error si no tuviese argumentos). El
;
que separa los comandos aqui se utiliza para indicar que
se debe cumplir un comando, y luego el siguiente. De forma similar, la
definicion
alias whois 'grep \!^ /etc/passwd'
define un comando que busca su primer argumento en el fichero de contrasenas.
Cuidado: La shell lee el fichero .cshrc
cada vez
que inicia. Si dispone alli una cantidad excesiva de comandos, las
shells tenderan a tener un inicio lento. Se encuentra en desarrollo un
mecanismo util capaz de guardar el ambiente de la shell luego de leer el
fichero .cshrc
y restaurarlo rapidamente , pero por ahora
debe intentar limitar la cantidad de alias establecidos a un numero
razonable... 10 o 15 es razonable; 50 o 60 seran casuales de notables
retrasos de inicializacion de las shells, con lo que el sistema se
volvera lento al ejecutar comandos anidados desde el interior del editor
u otros programas.
Mas redirecciones: >> y >&
Aun existen varias notaciones adicionales utiles para el usuario de terminal que no fueron explicadas: las redirecciones adicionales.
Ademas de la salida estandar ya tratada, los comandos cuentan tambien con una salida de diagnostico que normalmente esta dirigida al terminal - incluso si la salida estandar esta redirigida a un fichero o a un cano. En ocasiones es deseable dirigir la salida de diagnostico junto con la salida estandar. Por ejemplo, si desea redirigir la salida de un comando de larga ejecucion a un fichero, y desea contar con un registro de cualquier diagnostico de error que produzca ("log"), puede ingresar
comando >& fichero
Aqui el >&
le solicita a la C shell que redirija
tanto la salida de diagnostico como la salida estandar
a fichero
.
De manera similar, puede dar la orden comando
comando |& lpr
para redirigir tanto la salida estandar y la salida de diagnostico a traves de un cano al demonio de impresion lpr.
Finalmente, es posible usar la forma
comando >> fichero
para colocar la salida al final de un fichero
existente.
Noclobber
Existe una forma de comando
comando >&! fichero
Esta se usa cuando la variable
noclobber esta activada y fichero
ya existe. Sstri
noclobber
se encuentra activada, da como resultado un error
fichero no existe
. De otro modo la shell creara
fichero
si este no existe. La forma
comando >>! fichero
hara no dara error si fichero
es inexistente y la
variable de shell noclobber
se encuentra establecida
(activada).
Trabajos; Segundo Plano, Primer Plano, o Suspendido
Al introducir uno o mas comandos juntos siguiendo la forma de
caneria (separados por |
) o secuencia de
comandos (separados por punto y coma ;
), la shell
genera un unico trabajo ("job"), que consiste en dichos
comandos unificados. Un comando aislado (sin |
o
;
genera) un trabajo simple. Por lo general, cada linea
mecanografiada a la shell genera un trabajo. Algunas de las ordenes que
generaron trabajos (uno por linea) fueron
sort < datos
ls -s | sort -n | head -5
mail haroldo
Toda vez que al final de la orden se mecanografie el metacaracter
&
, entonces la misma se iniciara en la forma de
trabajo en segundo plano. Esto significa que la shell no
aguardara su cumplimiento, sino que quedara inmediatamente a la espera
de otro comando. Dicho trabajo se ejecutara en segundo plano.
Esto guarda diferencia con aquellos trabajos normales - llamados
trabajos en primer plano - que siguen siendo leidos y
ejecutados por la shell de uno a la vez. Por lo tanto
du > uso_de_disco &
corre el programa du, encargado de reportar el uso de disco
de su directorio de trabajo (asi como el de cualquier directorio por
debajo de el), coloca su salida en el fichero uso_de_disco
y vuelve inmediatamente la atencion a usted indicando un prompt para
eventual disposicion de un ulterior comando nuevo (sin aguardar la
finalizacion de du). Mientras tanto el programa du
continuara su ejecucion en segundo plano hasta finalizar, incluso aunque
usted mecanografie y solicite cumplimiento de otros comandos. Al
finalizar un trabajo en segundo plano, la shell presentara un mensaje
informando tal cosa justo antes de su prompt siguiente.
En el siguiente ejemplo, el trabajo du finalizo en cierto momento durante la ejecucion del comando mail, y su finalizacion se reporto justo antes de que el trabajo mail finalizase.
% du > uso_de_disco &
[1] 503
% mail bill
¿Como es posible saber cuando un trabajo en segundo plano ha finalizado?
EOT
[1] - Done du > uso_de_disco
%
Ante una finalizacion anormal de un trabajo, el mensaje
Done
podria intercambiarse por otro diferente, tal como
Killed
. Si usted desea que se le reporten aquellas
finalizaciones de programas en segundo plano al mismo momento en que se
producen - eventualmente incluso interrumpiendo la salida de terminal de
otros trabajos que pudiese tener en primer plano - puede establecer la
variable notify
. Siguiendo el ejemplo anterior, podria
implicar que el mensaje Done
eventualmente aparezca
directamente en medio de la redaccion del correo a Bill.
Los trabajos en segundo plano no resultan afectados por ninguna senal del teclado tales como las senales STOP, INTERRUPT, o QUIT mencionadas anteriormente.
La C shell inicia el alta los trabajos en una tabla de trabajos incorporada, y finaliza su baja con su cumplimiento. En esta tabla de trabajos la C shell tabula el nombre de comando, argumentos, numero de identificador proceso (PID) de todos los comandos, asi como el directorio de trabajo donde se inicio cada trabajo. Cada trabajo tabulado puede estar
- En ejecucion en primer plano (la C shell aguarda su finalizacion),
- En ejecucion en segundo plano,
- suspendido.
Solo puede estar en ejecucion en primer plano un trabajo por vez, pero es posible contar con varios trabajos suspendidos o corriendo en segundo plano a la vez. Los numeros de trabajo siguen siendo los mismos hasta que el trabajo finalice, y luego son reutilizados.
Al ordenar ejecucion de un trabajo en segundo plano por medio del
sufijo &
, antes que la shell le presente un prompt para
ingresar un nuevo comando le informara el numero de cola de
trabajos - asi como los numeros de identificador de proceso de
todos sus comandos de nivel superior - . Por ejemplo,
% ls -s | sort -n > uso_de_disco &
[2] 2034 2035
%
ejecuta el programa ls con la opcion -s
, entuba
su salida al programa sort con la opcion -n
, y
coloca la salida resultante en el fichero uso_de_disco
.
Debido al sufijo &
al final de la linea ambos programas
se iniciaron conjuntamente en la forma de un trabajo en segundo
plano. Una vez iniciado el trabajo, la shell presento el numero
de cola de trabajos entre corchetes - [2]
en este caso
- seguido por el numero de proceso de cada programa iniciado en
el trabajo en cuestion. Inmediatamente despues, la shell ofrece un
prompt para ingresar eventualmente una nueva orden, dejando al trabajo
en ejecucion concurrente (en simultaneo).
Como se menciono en la seccion 1.8, los trabajos en primer plano se suspenden mecanografiando ^Z, lo cual envia una senal STOP al trabajo en ejecucion en primer plano. Puede suspender un trabajo en ejecucion en segundo plano mediante comando stop que se describira mas adelante. Una vez que los trabajos se suspenden, su ejecucion se detiene hasta ser iniciados nuevamente - ya sea en el primer o en el segundo plano. La shell toma nota de la suspension de un trabajo, e informa el hecho de manera bastante similar a los informes de finalizacion de trabajo en segundo plano. En el caso del trabajo en primer plano, esto guarda una apariencia similar a
% du > uso_de_disco
^Z
Stopped
%
en donde la shell presenta el mensaje Stopped
al notar
que el programa du se ha suspendido. Al usar el comando
stop para detener los trabajos en segundo plano, se
presenta
% sort uso_de_disco &
[1] 2345
% stop %1
[1] + Stopped (signal) sort uso_de_disco
%
De necesitar alternar temporalmente entre lo que estamos haciendo, puede ser util suspender los trabajos en primer plano, ejecutar otros comandos, y retornar al trabajo suspendido. Asimismo, es posible suspender los trabajos en primer plano y continuar su ejecucion en la forma de trabajos en segundo plano por medio del comando bg. Esto permite continuar un trabajo distinto, poniendo el segundo plano en espera hasta que el trabajo en primer plano finalice. Por tanto
% du > uso_de_disco
^Z
Stopped
% bg
[1] + uso_de_disco &
%
inicia du en el primer plano, lo detiene antes de finalizar, usa bg para pasarlo a ejecucion en segundo plano, permitiendo asi eventuales nuevos comandos de ejecucion en primer plano. Esto se presenta especialmente util cuando un trabajo en primer plano demanda un tiempo de ejecucion mayor que el previsto, por lo que usted hubiese deseado iniciarlo en segundo plano desde un principio.
Comandos de control de trabajos
Comando de Control de Trabajos | Resultado |
---|---|
jobs | Presenta la cola de trabajos |
fg | |
bg | Pasa un trabajo suspendido a segundo plano |
stop, Ctrl+Z | Suspende un trabajo en ejecucion en segundo plano |
kill | termina un trabajo suspendido o en segundo plano o |
notify | notifica inmediatamente cuando finalizan los comandos |
Todos los comandos de control de trabajos de la C shell
pueden recibir un argumento que identifica el trabajo particular. Los
argumentos de nombre de trabajo deben indicarse con el caracter
%
, si bien alguno de los comandos de control del trabajo
tambien aceptan numeros de identificador de proceso PID (presentados por
el comando ps). El trabajo por defecto (si no se le proveen
argumentos) es llamado trabajo actual y es identificado por un
+
in la salida del comando jobs, el cual muestra
cuales son los trabajos que tiene. Cuando solo existe un trabajo en
ejecucion o detenido en el segundo plano (el caso usual), el mismo
siempre sera el trabajo actual, y por tanto no necesita
argumentos. Si un trabajo ha sido detenido en el primer plano, se
transforma en el trabajo actual y el trabajo actual existente
se convierte en el trabajo previo. Cuando es dado, el argumento
es tanto %-
(que denota trabajo previo;
%#
donde el #
es el numero de trabajo;
%pref
(donde pref es algun prefijo unico del nombre de
comando y argumentos de uno de los trabajos; o ?%
seguido
por alguna cadena encontrada en solo uno de los trabajos, presenta la
tabla de trabajos con los tipos de comandos de trabajos, los comandos,
el numero de trabajo, y estado de status (Stopped
o
Running
) de cada trabajo en segundo plano o trabajo
suspendido. Sumando la opcion -l
tambien presenta los
numeros de identificador de proceso PID.
% du > uso_de_disco &
[1] 3398
% ls -s | sort -n > mifichero &
[2] 3405
% mail bill
^Z
Stopped
% jobs
[1] - Running du > uso_de_disco
[2] Running ls -s | sort -n > mifichero
[3] + Stopped mail bill
% fg
% ls
ls -s | sort -n > mifichero
% more mifichero
El comando fg pasa un trabajo suspendido o en segundo plano a ejecucion en primer plano. Se utiliza para reiniciar un trabajo previamente suspendido, o cambiar un trabajo suspendido para que corra en el primer plano (permitiendo el arribo de senales o entrada desde el terminal). En el caso anterior, se utilizo fg para pasar el trabajo ls desde el segundo plano al primer plano pues se deseaba aguardar su finalizacion antes de estudiar su fichero de salida. El comando bg reinicia ejecucion de un trabajo suspendido en el segundo plano. Usualmente se utiliza tras suspender con la senal STOP el actual trabajo en ejecucion en primer plano (combinar la senal STOP con el comando bg conmuta un trabajo de ejecucion en primer plano a un trabajo en ejecucion en segundo plano). El comando stop suspende un trabajo en segundo plano.
El comando kill %nro_de_cola
extermina
inmediatamente un trabajo en segundo plano o trabajo suspendido. Ademas
de los trabajos, puede recibir como argumento numeros de procesos, tal
como son presentados por ps. Por tanto, en el ejemplo anterior,
al correr el comando du podria haber sido terminado por el
comando
% kill %1
[1] Terminated du > uso_de_disco
%
El comando notify (no es la variable notify
mencionada anteriormente) indica que debe informar inmediatamente el
cumplimiento y finalizacion de un trabajo especifico inmediatamente (en
lugar de aguardar el siguiente prompt).
Si un trabajo en ejecucion en segundo plano intenta leer entrada del
terminal, resulta detenido automaticamente. Posteriormente, al pasar
dicho trabajo a primer plano, podra recibir entrada. Si se lo desea, es
posible enviar nuevamente el trabajo al segundo plano hasta que este
solicite nuevamente entrada. Dicho procedimiento se ilustra en la
siguiente secuencia de sesion, donde el comando s
del
editor de texto puede requerir un tiempo para cumplir.
% ed ficherogigante
120000
1,$s/estapalabra/otrapalabra
^Z
Stopped
% bg
[1] ed ficherogigante &
%
... algunos comandos en segundo plano
[1] Stopped (tty input) ed ficherogigante
% fg
ed ficherogigante
w
120000
q
%
Por tanto, tras indicar el comando s
, el trabajo
ed fue detenido con ^Z y luego pasado al
segundo plano usando bg. Cierto tiempo despues el comando
s
finalizo, ed intento leer otro comando y resulto
detenido pues los trabajos en segundo plano no pueden leer desde el
terminal. El comando fg devolvio al primer plano el trabajo
ed, donde una vez mas pudo aceptar comandos desde el
terminal.
El comando
stty tostop
detiene todos los trabajos en segundo plano que intenten escribir
salida en el terminal. De esta manera se impide que los mensajes de los
trabajos en segundo plano interrumpan la salida de los trabajos en
primer plano, y permiten correr un trabajo en segundo plano sin perder
la salida de terminal. Tambien puede utilizarse con programas
interactivos que presentan a menudo periodos largos sin interaccion. Por
tanto, se detendra cada vez que una salida solicite mayor entrada del
terminal, antes de presentar el prompt de solicitud. Podra entonces
correrlo en primer plano por medio fg a fin de proveer tal
entrada, y si es necesario detenerlo y pasarlo al segundo plano. Si no
desea tener salida de trabajos en segundo plano que interrumpan su
trabajo, podra encontrar util insertar este comando stty en su
fichero .login
. Tambien puede reducir la necesidad de
redirigir la salida de los trabajos en segundo plano, y si la salida no
es muy grande:
% stty tostop
% wc ficherogigante &
[1] 10387
% ed texto
... algun tiempo despues
q
[1] Stopped (tty output) wc ficherogigante
% fg wc
wc ficherogigante
13371 30123 302577
% stty - tostop
En el ejemplo anterior, se ejecuto el comando wc (que
resulta en una salida de solo una linea que cuenta lineas, palabras y
caracteres de un fichero). Como este requeriria aguardar un cierto
tiempo, se detuvo su salida antes que intentara imprimir en el terminal,
y nos pusimos a editar el fichero texto
para aprovechar el
tiempo. Luego, se ha permitido que wc - con su trabajo cumplido
- imprima su linea en el terminal, exactamente en el momento en que
estabamos listos para observar la salida. Aquellos programas que
intentan cambiar el modo del terminal tambien se bloquearan toda vez que
no se encuentren en primer plano (ya sea que tostop este
establecido o no, ya que seria muy inconveniente que un programa en
segundo plano quiera cambiar el estado del terminal).
Como los comandos de trabajos solo presentan aquellos trabajos iniciados en la shell de ejecucion actual, desconocen de los trabajos en segundo plano iniciados en otras sesiones de login, o aquellos trabajos ocurridos dentro de los ficheros de la shell. En este caso puede usarse ps para descubrir los trabajos en segundo plano no iniciados durante la instancia actual de shell.
Directorios de Trabajo
Como se menciono en seccion 1.6, la shell siempre se encuentra activa en un directorio de trabajo particular. El comando de "cambio de directorio" chdir (tambien puede usarse su abreviatura cd) modifica el directorio de trabajo de la shell - esto es, cambia el directorio en el cual usted se encuentra actualmente.
Es util crear un directorio para cada proyecto en el que desea trabajar, y disponer en el todos los ficheros relacionados a dicho proyecto. El comando "crear directorio" mkdir genera un directorio nuevo. El comando pwd ("imprimir directorio de trabajo") informa la ruta absoluta de directorio del directorio de trabajo de la shell - esto es, el directorio en el cual se encuentra actualmente. Por lo tanto, en el siguiente ejemplo
%pwd
/usr/bill
% mkdir periodico
% chdir periodico
%pwd
/usr/bill/periodico
%
el usuario ha creado y se ha posicionado en el directorio
periodico
, donde - por ejemplo - podria colocar un grupo de
ficheros relacionados. Siempre podra volver a su directorio
home
de inicio de sesion - sin importar donde se ha movido
en la jerarquia de directorios - simplemente ingresando
cd
sin mas argumentos. El nombre ..
siempre significa "el
directorio por encima de la jerarquia del directorio actual", por lo
tanto
cd ..
Cambia el directorio de trabajo de la shell al directorio por encima
del actual. El nombre .
puede ser usado en cualquier
nombre de ruta. Por lo tanto
cd ../programas
significa cambiar al directorio programas
contenido en
el directorio por encima del directorio actual. Si tiene varios
directorios para diferentes proyectos - por debajo, por tomar de
ejemplo, de su directorio home
- esta notacion abreviada le
permitira cambiar rapidamente entre ellos.
La shell siempre recuerda el nombre de ruta de directorio de trabajo
actual a traves de suvariable cwd
. La shell tambien puede
ser requerida de recordar el directorio previo, al cambiar a un nuevo
directorio de trabajo. Si en lugar del comando cd se utiliza el
comando "empujar directorio" pushd, la shell guardara el nombre
del directorio de trabajo actual en un listado de pila directorios
de uso previo al cambio de directorio actual. Podra consultar dicho
listado en cualquier momento ingresando el comando "directorios",
dirs.
% pushd periodico/referencias
-/pediodico/referencias -
% pushd /usr/lib/tmac
/usr/lib/tmac ~/periodico/referencias ~
% dirs
/usr/lib/tmac ~/periodico/referencias ~
% popd
~/periodicos/referencias ~
%.popd
%
El listado de la pila de directorios de uso previo resultara impreso
en una linea horizontal, que se lee de izquierda a derecha, con una
abreviatura de su directorio home
(en este caso
/usr/bill
) en forma de tilde (~
). La pila de
directorio se imprime toda vez que hay mas de una entrada en ella, y se
ve alterada. Tambien es impresa por un comando dirs.
Generalmente dirs resulta mas rapido e informativo que
pwd ya que junto al directorio de trabajo actual, muestra como
cualquier otro directorio registrado en la pila de directorios de uso
previo.
El comando pushd sin argumentos alterna el directorio
actual, con el primer directorio en la pila de directorios de uso
previo. El comando "traer directorio" popd sin argumento lo
devuelve al directorio donde se encontraba con anterioridad al actual,
descartando el directorio de trabajo previo de la pila de directorios.
Si ingresa popd
varias veces en serie, retrocedera entre
los directorios que ha estado navegando (caminando hacia atras) por el
comando pushd. Existen otras opciones a pushd y
popd para manipular los contenidos de la pila de directorios de
uso previo, y cambiar a directorios que no se encuentran en la parte
superior de la pila; para mas detalles consulte las manpages de Csh.
Como la shell recuerda el directorio de trabajo en el cual ha comenzado cada trabajo, le advertira de manera practica cuando podria confundirse al reiniciar un trabajo en el primer plano que tiene un directorio de trabajo diferentes que el directorio de trabajo actual de la shell. Por lo tanto, al iniciar un trabajo en segundo plano, esta cambiara entonces el directorio de trabajo de la shell y luego conmutara a ejecucion en primer plano el trabajo que se encontraba en segundo plano. La shell advertira entonces que el directorio de trabajo del trabajo en actual ejecucion en primer plano es diferente al tabulado en la tabla de trabajos de la C shell.
% dirs -l
/mnt/bill
% cd miproyecto
% dirs
~/miproyecto
% ed prog.c
1143
^Z
Stopped
% cd ..
% ls
miproyecto
fichero_texto
% fg
ed prog.c (wd: ~/miproyecto)
De esta forma la shell le advierte con wd: directorio
en
el momento que exista discrepancia por un cambio de directorio de
trabajo, incluso aunque no se haya utilizado un comando cd. En
el ejemplo anterior, el trabajo suspendido ed
aun se
encontraba en /mnt/bill/proyectos
, incluso aunque la shell
se habia cambiado a mnt/bill
. Una advertencia similar se
ofrece cuando tal trabajo en primer plano finaliza o resulta suspendido
(usando la senal STOP). Retornar nuevamente a la shell implica cambiar
de directorio de trabajo nuevamente, indicado por
wd now: directorio
.
% fg
ed prog.c (wd: -/miproyecto)
... tras alguna edicion
q
(wd now: ~)
%
Estos mensajes resultan algo confusos si usted usa programas que
cambian sus propios directorios de trabajo, pues C shell solo recuerda
desde cual directorio un trabajo se ha iniciado, y asume su permanencia
alli. La opcion -l
de jobs presentara el
directorio de trabajo de los trabajos suspendidos o en segundo plano
cuando discrepan del directorio de trabajo actual de la shell.
Presentar un prompt con el directorio actual de trabajo en la shell - como es usanza en los BSD modernos - solventa en gran medida estas problematicas. En los 80s se evitaba pues el comando cwd requeria un par de segundos en los lentos discos de la VAX, y enlentecia todo el sistema multiusuario [N.d.T.].
Comandos Incorporados utiles
Ahora ofrecemos algunos pocos comandos utiles incorporados en la shell, y describimos como se usan.
El comando alias descripto arriba se usa para asignar nuevos alias y para mostrar los alias existentes. Si argumentos, presenta los alias existentes. Tambien puede darsele solo un argumento tal como
alias ls
El comando echo imprime sus argumentos. A menudo es usado en guiones de shell o como un comando interactivo para veer que expansiones de nombre de fichero producira.
El comando history mostrara los contenidos de su listado
de historial. Los numeros dados a los eventos del historial pueden
usarse para referencia eventos anteriores que son dificiles de
referenciar utilizando los mecanismos contextuales introducidos
anteriormente. Existe tambien una variable de shell denominada
prompt
.
Al colocar un caracter !
en su valor, la shell
substituira el numero del comando actual con el del listado de
historial. Puede usar este numero para referir a este comando en
una substitucion de historia. Por lo tanto, podria
set prompt='\!% '
Tenga presente que el !
debe ser anulado aqui
incluso entre caracteres de apoostrofo '
.
El comando limit se use para restringir el uso de recursos de computo. Sin argumentos, presentara las limitaciones actuales:
cputime unlimited
filesize unlimited
datasize 5616 kbytes
stacksize 512 kbytes
coredumpsize unlimited
Los limites pueden establecerse, de esta manera:
limit coredumpsize 128k
Funcionaran la mayoria de las abreviaciones razonables; vea el man de csh para mas detalles.
El comando logout puede utilizarse para terminar una sesion
de shell que tiene establecido la variable ignoreeof
.
El comando rehash provoca que la shell recompute una tabla
de localizacion de comandos. Esto es necesario si se agrego un comando a
un directorio en la ruta de busqueda path
de la shell
actual, y se desea que la shell proceda a encontrarla, de otro modo el
algoritmo de deteccion podria decirle a la shell que el comando no se
encontraba en los comandos indicados cuando la tabla de hash fue
computada originalmente.
El comando repeat puede utilizarse para repetir un comando
varias veces. Por tanto, para realizar cinco copias del fichero
uno
hasta el fichero cinco
, podria hacer
repeat 5 cat uno >> cinco
El comando setenv puede usarse para establecer variables de ambiente. Por tanto
setenv TERM=adm3a
establecera el valor de la variable de ambiente TERM
a
la cadena adm3a
.
Un programa de usuario llamado printenv existe, que imprimira el ambiente. Podria mostrar
% printenv
HOME=/usr/bill
SHELL=/bin/csh
PATH=:/usr/ucb:/bin:/usr/bin:/usr/local
TERM=adm3a
USER=bill
%
El comando source puede usarse para forzar a la shell actual a leer comandos desde un fichero. Por tanto
source .cshrc
puede usarse luego de editar un cambio en el fichero
.cshrc
el cual desea que surta efecto antes de la siguiente
vez que inicie sesion.
El comando time puede usarse para cronometrar un comando, sin emportar cuantos ciclos de computadora requiera. Por tanto
% time cp /etc/rc /usr/bill/rc
0.0u 0.1s 0:01 8% 2+lk 3+2io 1pf+0w
% time wc /etc/rc /usr/bill/rc
52 178 1347 /etc/rc
52 178 1347 /usr/bill/rc
104 356 2694 total
0.1u 0.1s 0:00 13% 3+3k 5+3io 7pf+Ow
%
indica que el comando cp uso una cantidad de tiempo de
usuario negligible (u
) y aproximadamente 1/10 del tiempo de
sistema (s
); el tiempo transcurrido fue de 1 segundo
(0:01
). Hubo un uso promedio de memoria de 2k bytes de
espacio de programas, y 1k bytes de espacio de datos sobre el tiempo de
CPU involucrado (2+1k
), el programa hizo tres lecturas de
disco y dos escrituras (3+2io
), y tomo un paginado y no
hizo uso de memoria de intercambio (1pf+0w
). El comando de
conteo de palabras wc por otro lado utilizo un decimo de
segundos de tiempo de usuario (0.1
), menos de un segundo
del tiempo transcurrido en el sistema. El porcentale de 13%
indica que durante dicho periodo el comando activo wc
uso
un 13 porciento de los ciclos de computadora disponibles ne la
maquina.
Los comandos unalias y unset pueden usarse para remover alias y definiciones de variable de la shell, y unsetenv quita variables del ambiente.
¿Que mas?
Esto concluye la discusion basica de la shell para los usuarios de terminal. Existen mas funcionalidades de la shell que las explicadas aqui, y todas las funcionalidades de la shell se tratan en las manpages. Una funcionalidad util que se discutira luego es el comando incorporado foreach que puede utilizarse para ejecutar la misma secuencia de comandos con una cantidad de argumentos distintos.
Si usted desea utilizar UNIX, deberia revisar el resto de este documento y las manpages de la shell para familiarizarse con las otras funcionalidades que tiene disponibles.
3. Estructuras de Control de Shell y Guiones de Comandos
Introduccion
Es posible colocar comandos en ficheros y hacer que la shell se involucre leyendo y ejecutando los comandos que contienen tales ficheros, a los cuales se les llamara script de shell. Detallamos aqui aquellas funcionalidades de la shell que resutan utiles a quienes escriben tales scripts.
Make
Es importante recalcar primero para que son utiles los guiones de
shell. Existe un programa llamado make que es muy util para
mantener un grupo de ficheros relacionados, desarrollando un conjunto de
operaciones sobre ellos. Por ejemplo, un gran programa consistente en
uno o mas ficheros puede tener sus dependencias descriptas en un
makefile
, un fichero que guarda definiciones de comandos
utilizados para crear esos ficheros diferentes cuando las definiciones
cambian. Estas definiciones facilitan los medios para imprimir listados,
limpiar el directorio en el cual residen los programas, instalar el
programas resultante colocando mas apropiadamente los ficheros definidos
en este makefile. Tal formato es superior y preferible a manejar un
grupo de procedimientos de shell afrotar dicha tarea.
De la misma manera, al realizar preparaciones documentales puede
crearse un makefile
para definir como crear las versiones
diferentes del documento y cuales opciones de rooff o troff son
adecuadas para su utilizacion.
Invocacion y variable argv
Puede interpretar un guion de C Shell diciendo
% csh guion ...
donde guion
es el nombre del fichero que contiene un
grupo de comandos de Csh y ...
se reemplaza por una
secuencia de argumentos. La shell coloca dichos argumentos en la
variable argv
y luego comienza a leer los comandos del
guion. Estos parametros estan puestos a disponibilidad a traves de los
mismos mecanismos que se usan para referenciar cualquier otra variable
de shell.
Si hace el fichero guion
ejecutable haciendo
chmod 755 guion
y coloca un comentario de shell, al comienzo del guion de shell (ej,
comienza el fichero con un caracter de #
), seguido de
/bin/csh
, esta automaticamente sera invocada para ejecutar
el guion cuando ingrese
guion
Si el fichero no comienza con #
entonces se utilizara la
shell estandar /bin/sh
para ejecutarlo. Esto le permitira
convertir sus guiones mas antiguos para interpretarlos con csh segun lo
crea conveniente.
Sustitucion de Variables
Una vez que cada linea de entrada es dividida en palabras y se
realizan las substituciones de historia, se desmenuza la linea de
entrada en comandos univocos. Antes de ejecutar cada comando, en las
palabras se lleva a cabo un mecanismo conocido como sustitucion de
variables. Indicado por el caracter $
, esta
sustitucion reemplaza los nombres de las variables por sus valores. Por
tanto el
echo $argv
colocado en un guion de comandos, provocara que el valor de la
variable argv
sea presentada como salida del eco del guion
de shell. Si argv
no se encuentra establecida en este
momento, provocara un error.
Estan previstas varias notaciones de acceso a componentes y atributos de variables. La notacion
$?nombre
se expande a 1
si nombre
esta
establecida (set
), o en 0
si no
esta establecida (unset
). Este es el mecanismo
fundamental para evaluar si las variables particulares han sido
asignadas a valores. Todas las demas formas de referencia a variables no
definidas provocaran errores.
La notacion
$#nombre
se expande al numero de elementos en la variable nombre
.
Por tanto
% set argv=(a b c)
% echo $?argv
1
% echo $#argv
3
% unset argv
% echo $?argv
0
% echo $argv
Undefined variable: argv.
%
Tambien es posible acceder a componentes de una variable que cuenta de varios valores. POr tanto
$argv[l]
da el primer componente de argv
o - en el ejemplo
anterior - a
. De igual forma
$argv [$#argv]
dara c
, mientras que
$argv[1-2]
dara a b
. Otras notaciones utiles en guiones de shell
son
$n
siendo n
un entero que oficia de abreviacion de
$argv[n]
el n
° parametro, y
''$*''
que as la abreviacion de
$argv
La forma
$$
expande al numero de proceso de la shell actual. Como dicho numero de proceso es unico en el sistema, bien puede utilizarse para generar nombres de fichero temporales unicos. La forma
$<
es bastante especial y es reemplazada por la siguiente linea de entrada, a ser leida desde la entrada estandar de la shell (no del guion que estaba siendo leido). Esto resulta muy util para escribir guiones de shell interactivos, que leen comandos de la terminal, o incluso escribir guiones de shell que actuan como un filtro, capaz de leer lineas de su fichero de entrada. Por tanto, la secuencia
echo '¿si o no?\c'
set r- ($<)
Presentara la solicitud ¿si o no?
, sin presentar una
nueva linea, y luego leera una respuesta dada, colocandole como variable
r
. En este caso $#r
sera 0
si se
mecanografia tanto una linea en blanco o un fin-de-fichero
(^D).
Debemos llamar la atencion de una diferencia menor entre
$n
y $argv[n]
. La forma $argv[n]
dara un error si n
no se encuentra en el rango
1-$#argv
, mientras que $n
nunca dara un error
de suscripto fuera de rango. Esto se hace para retener compatibilidad
con la manera con la cual los shells mas antiguos
manejaban los parametros.
Otro punto importante es que nunca es un error dar un subrango en la
forma de n-
; si hay menos de n
componentes de
una variable dada entonces no se substituye palabra alguna. Un rango en
la forma m-n
de la misma forma devolvera un vector vacio
sin dar error, toda vez que m
exceda el numero de elementos
de la variable dada, provisto que el suscripto n
se
encuentre dentro del rango.
Expresiones
Para poder construir guiones de shell interesantes, debe ser posible
evaluar expresiones en la shell basadas en los valores de variables. De
hecho, todas las operaciones aritmeticas del lenguaje C estan
disponibles en la shell con la misma precedencia que tienen en el
lenguaje C. En particular, las operaciones ==
y
!=
comparan cadenas y los operadores
&&
, y ||
implementan las operaciones
de logica de Boole AND/OR
. Los operadores especiales
='
y !'
son similares a ==
y
!=
., excepto que la cadena en el lado derecho puede tener
caracteres de coincidencia de patrones como *
,
?
, o []
, y la evaluacion consistira en si la
cadena de la izquierda coincide con el patron de la derecha.
LKa shell tambien permite solicitudes de ficheros segun la forma
-? nombre_fichero
donde ?
es reemplazado por una cifra numerica de un
caracteres simples. Por ejemplo, la expresion primitiva
-e nombre_fichero
informara si el fichero nombre_fichero
existe. Otras
primitivas evaluan por acceso de lectura, escritura y ejecucion de un
fichero., si este es un directorio, o si tiene longitud no cero.
Es posible evaluar si un comando termina normalmente, por una
primitiva de la forma de { comando }
, que recorna
verdadero (ej, 1
) si el comando tiene exito en
salir normalmente con status 0
de salida, o 0
si el comando termina anormalmente o con un status de salida distinto a
cero. De requerirse mayor informacion detallada sobre el status de
ejecucion de un comando, puede ser ejecutado con la variable
$status
examinada en el siguiente comando. Como
$status
esta establecida por todos los comandos, en muy
demostrativa. Puede ser salvada si es inconveniente de usarla solo una
sola vez inmediatamente por el comando que sigue.
Si desea un listado completo de componentes de expresion dispoible, observe dicha seccion del manual de la shell.
Ejemplo de guion de shell
A continuacion, un guion de shell de ejemplo, que hace uso de mecanismo de expresion de la shell y alguna de sus estructuras de control
% cat copyc.sh
#
# Copyc.sh copia los programas en la lista especificada
# al directorio ~/respaldo si difieren de los ficheros
# que ya estan en ~/respaldo
#
set noglob
foreach i ($argv)
if ($i !* *.c) continue # no es un fichero .c de modo que no hace nada
if (! -r ~/respaldo/Si:t) then
echo $i:t no en respaldo ... no cp\'ado
continue
endif
cmp -s $i ~/respaldo/$i:t # para establecer $status
if ($status ! = 0) then
echo nuevo respaldo de $i
cp $i ~/respaldo/Si:t
endif
end
Este guion hace uso del comando foreach, que provoca que la
C Shell ejecute los comandos dispuestos entre el foreach
y
su correspondiente end
para cada uno de los valores dados
entre la (
y la )
con la variable nombrada, en
este caso i
establecida en valores sucesivos de la lista.
Dentro de este bucle podemos usar el comando break para detener
la ejecucion del bucle y continuar para terminar prematuramente una
iteracion y comenzar con la siguiente. Luego del bucle foreach
la variable de iteracion (i
en este caso) tendra el valor
de la ultima iteracion.
Establecemos la variable noglob aqui para impedir que la
expansion de nombre de ficheros de los miembros de argv
.
Esto es generalmente una buena idea si los argumentos de un guion de
shell son nombres de ficheros que han sido ya expandidos, o si los
argumentos podrian contener metacaracteres de expansion de nombres de
fichero. Tambien es posible citar cada uso de una variable de expansion
$
, pero esto es mas dificil y menos confiable.
La otra estructura de control empleada aqui es una declaracion de la forma
if ( expresion ) then
comando
...
endif
La colocacion de las palabras claves aqui utilizada no es flexible, debido a la actual implementacion de la shell (ver limitaciones.
La shell tiene otra forma de la declaracion if que guarda la forma
if ( expresion ) comando
La cual - si es una linea muy larga - puede escribirse
if ( expresion ) \
comando
Aqui anulamos la nueva linea por estetica. El comando no debe
involucrar un |
, &
o ;
, y no
debe ser otro comando de control. La segunda forma, requiere que la
\
final preceda inmediatamente al fin-de-linea (o
sea, no de debe dejarse espacio).
La declaracion if mas general anterior tambien admite la
secuencia o pares else-if
seguidos por un unico
else
y un endif
, por ejemplo
if ( expression ) then
comandos
else If (expression ) then
comandos
else
comandos
endif
Otro mecanismo importante usado en guiones de shell es el modificador
:
. Podemos usar el modificar :r
aqui para
extraer una raiz de un nombre de fichero, o :e
para extraer
la extension. Por tanto, si la variable i
tiene el valor
/mnt/foo.bar
, entonces
% echo $i $i:r Si:e
/mnt/foo.bar /mnt/foo bar
%
muestra como el modificador :r
quita el
.bar
final de la cadena, y el modificador :e
deja solo el bar
. Otros modificadores quitan el ultimo
componente de un nombre de ruta, dejando la cabeza
:h
o dejando todo a excepcion del ultimo componente del
nombre de ruta dejando la cola :t
. Estos
modificadores son descriptos al completo en las manpages de csh, en el
manual del programador. Es tambien posible usar el mecanismo de
sustitucion de comandos descriptos en la siguiente seccion para
desarrollar modificaciones en cadenas y luego reingresar el ambiente de
la shell. Como cada uso de este mecanismo involucra la creacion de un
nuevo proceso, es mucho mas costoso en terminos computacionales que usar
la modificacion :
. Finalmnet,e debemos notar que el
caracter #
introduce lexicamente un comentario de shell en
los guiones de shell (por no desde la terminal). Todos los caracteres
subsecuentes en la linea de entrada a posterior del #
resultan descartados por la shell. Este caracter puede ser citado usando
*
o \
para ponerlo en una palabra
argumento.
Limitaciones
Las siguiente dos formas no son aceptables por C shell:
if ( expresion ) # No funciona!
tben
comando
...
endif
y
if ( expresion ) then comando endif # No funciona
Tambien es importante notar que la implementacion de la C shell
limita el numero de modificadores :
en una sustitucion
$
a solo 1. Por tanto
% echo $i $i:h:t
/a/b/c /a/b:t
%
no hace lo que uno esperaria.
Otras estructuras de control
La C shell tambien cuenta con estructuras de control
while
y switch
similares a las del lenguaje C.
Estas guardan las formas
while ( expresion )
comandos
end
y
switch ( palabra )
case str1:
comandos
breaksw
...
case stm:
comandos
breaksw
default:
comandos
breaksw
endsw
Para mayores detalles, consulte la seccion del manual de csh. Los
programadores de C deben notar que usamos breakslt
para
salir de un switch, mientras que break sale de un bucle while o
foreach. Un error comun de cometer en los guiones de csh es
usar los conmutadores break
en lugar de
breaksw
.
Finalmente, csh permite declaraciones goto
con etiquetas
que son similares alas de C. Ejemplo:
bucle:
comandos
goto bucle
Suplir entrada a comandos
Los comandos ejecutados desde guiones de shell reciben por defecto la entrada estandar de la shell que corre el guion. Esto es distinto a shells precias que corren en UNIX. Esto permite a los guiones de C shell de participar completamente en tuberias, pero obligan a realizar notacion extra para los comandos que deben tomar datos en linea.
Por lo tanto necesitamos una metanotacion para proveer datos en linea a los comandos situados en guiones de C shell. Como ejemplo, considere este guion que corre el editor para borrar los caracteres iniciales en blanco desde las lineas en cada fichero argumento.
% cat deblanqueador.sh
# deblanqueador.sh - Quita los caracteres en blanco al inicio de una linea
foreach i ($argv)
ed - Si << 'EOF'
1,$s/^[ ]*//
w
q
'EOF'
end
%
La notacion << 'EOF'
significa que la entrada
estandar para el comando ed debe provenir del texto en el
fichero de guion de shell en si, hasta la siguiente linea, lo que
literalmente consiste en "'EOF'
". El hecho de que
EOF
se encuentre apostrofado (citado) provoca que la shell no sustituya
variables en las lineas intervinientes. En general, si alguna parte de
la palabra que sigue a <<
- la cual la shell usa para
terminar el texto a ser dado al comando - se cita, entonces se anula la
substitucion de tales substituciones. En este caso, como en nuestro
guion de edicion hemos utilizado la forma 1,$
, debemos
asegurar que este $
no sufra sustitucion como variable.
Podriamos haberlo asegurado tambien por medio de preceder dicha
$
con un caracter de \
. Por ejemplo:
1,\$s/^[ ]*//
sin embargo, citar el terminador 'EOF'
es una manera mas
confiable de lograr lo mismo.
Atrapando interrupciones
Si nuestro guion de shell crea ficheros temporales, podriamos querer atrapar interrupciones del guion de shell de modo de poder limpiar dichos ficheros. Podremos entonces hacer
onintr label
donde etiqueta
es una etiqueta en nuestro programa. Si
la shell recibe una interrupcion, hara un goto
etiqueta
y podremos remover los ficheros temporales y
luego hacer un comando exit
(que esta incorporado en la
shell) para salir del guion de shell. Si deseamos salir con status
no cero, podremos hacer
exit(1)
por ejemplo, para salir con status 1
.
¿Que mas?
Existen otras funcionalidades de la shell utiles a quienes escriben
procedimientos de shell. las opciones verbose
y
echo
y sus respectivas opciones de linea de comandos
relacionadas -l
y -x
, pueden usarse para
asistir en al rastreo de acciones de shell. La opcion -n
hace que la shell solo lea comandos sin ejecutarlos, y en
ocasiones puede resultar de suma utilidad.
Es importante notar que csh no ejecuta guiones de shell que no
comiencen con el caracter #
- lo que significa que los
guiones de C shell comienzan con un comentario. De forma similar, el
/bin/sh
de su sistema podria bien diferir del
csh
en la interpretacion de guiones de shell que comiencen
con #
.
Esto es lo que permite que tanto los guiones de shell y la C shell vivan en armonia.
Tambien existe otro mecanismo de citado que usa "
, que
permiten que se ejecuten solo algunos mecanismos de expansion que hemos
discutido hasta el momento en la cadena citada, y sirve para hacer que
esta cadena se convierta en una palabra unica, como lo hace
'
.
4. Otras funcionalidades de la shell menos utilizadas
Bucles en la terminal; variables como vectores
En ocasiones es util usar la estructura de control foreach
en el terminal para asistir en la realizacion de iteraciones de comandos
similares. Por ejemplo, en el sistema Cory UNIX en Cory Hall hubo en un
momento tres shells en uso: /bin/sh
, /bin/nsh
,
y /bin/csh
. Para contar la cantidad de personas que usaban
cada shell, uno podria haber enviado los comandos
% grep -c csh$ /etc/passwd
27
% grep -c nsh$ /etc/passwd
128
% grep -c -v sh$ /etc/passwd
430
%
Ya que estos comandos son muy similares, podremos usar un foreach para simplificar.
% foreach i ('sh$' 'csh$' -v 'sh$')
? grep -c $i /etc/passwd
? end
27
128
430
%
Tenga aqui presente que al leer desde el cuerpo de un bucle, la shell
le solicita entrada con un ?
. Las variables son de utilidad
en los bucles, pues puede asignarle listas de nombres de ficheros u
otras palabras. Por ejemplo, podria hacer
% set a=('ls')
% echo $a
csh.n csh.rm
% ls
csh.n
csh.rm
% echo $#a
2
%
Aqui el comando set da a las variables de una lista
a
cuyo valor es todos los nombres de fichero del directorio
actual. Podemos luego iterar sobre dichos nombres para realizar
cualquier funcion que escojamos.
La salida de un comando apostrofado entre caracteres '
es convertida por la shell a una lista de palabras. Tambien puede
entrecomillar la cadena citada entre caracteres "
para
considerar cada linea no vacia como un componente de variable
(impidiendo que las lineas resulten divididas como palabras a lo largo
de sus caracteres de espacio en blanco o tabuladores. Existe un
modificador -x
que puede ser utilizado luego para expandir
cada componente de variable en otra variable, dividiendola en palabras
separadas a lo largo de sus caracteres de espacio en blanco o
tabuladores.
Llaves {...} en expansion de argumentos
Otra forma de expansion de nombre de fichero aludida anteriormente
involucra el uso de caracteres de lave abierta {
y llave
cerrada }
. Dichos caracteres especifican que la cadena
contenida entre llaves, separada por comas ,
, debe ser
sustituida consecutivamente en los caracteres contenidos entre llaves, y
los resultados, expandidos de izquierda a derecha. Por lo tanto
A{cad1,cad2... stm}B
se expande a
Acad1B Acad2B... AstmB
Esta expansion ocurre antes que otras expansiones de nombre de fichero, y puede aplicarse recursivamente (si se encuentra anidada). Los resultados de cada cadena expandida se ordenan por separado, preservandose el orden de izquierda a derecha. No se requiere que existan los nombres de fichero resultante si no se emplean mecanismos de expansion ulteriores. Esto significa que este mecanismo puede utilizarse para generar argumentos que no sean nombres de fichero, pero que tengan partes en comun.
Un uso tipico de esto seria
mkdir ~/{hdrs,retrofit,csh}
que crea los subdirectorios hdrs
, retrofit
y csh
en su directorio home
. Este mecanismo es
mas util cuando el prefijo comun es mas largo que el dado en este
ejemplo, por ejemplo:
chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}
Sustitucion de Comandos
Un comando encerrado en caracteres '''' resulta reemplazado justo antes de que se expandan los nombres de cihero, por la salida de dicho comando. Por lo tanto, es posible hacer
set pwd='pwd'
para salvar el directorio actual en la variable pwd
, o
bien hacer
ex 'grep -l TRACE *.c'
para ejecutar el editor ex proveyendolo de aquellos ficheros cuyos
nombres finalicen en .c
y que contengan la cadena
TRACE
como argumento.
La expansion de comandos tambien ocurre en la entrada redirigida con
<<
y dentro de citado entrecomillado con""
. Refierase al manual de la shell para los detalles completos
Otros detalles no cubiertos
En circunstancias particulares puede ser necesario conocer la naturaleza y orden exacto de las diferentes sustituciones desarrolladas por la shell. El significado exacto de ciertas combinaciones de citas es importante ocasionalmente. Estas se detallan al completo en su repsectiva seccion del manual. La shell tiene una cantidad de opciones flags que se emplean mayoritariamente para escribir programas de UNIX, y depurar guiones de shell. Consulte la seccion de manual de la shell para obtener un listado de dichas opciones.
Caracteres Especiales
La siguiente tabla lista los caracteres especiales de csh y el sistema UNIX.
Metacaracteres sintacticos
Metacaracter sintactico | Sintactica |
---|---|
; |
separa comandos para ejecutarlos secuencialmente |
| |
separa comandos en un cano |
() |
entre parentesis de expresiones y valores de variables |
& |
continua ejecutando los siguientes comandos sin esperar completar la ejecucion |
Metacaracteres de nombre de fichero
Metacaracter de nombre de fichero | Accion |
---|---|
/ |
separa los componentes de una ruta de fichero |
? |
expansion de caracter coincidente con cualquier caracter unico |
* |
expansion de caracter coincidente con cualquier secuencia de caracteres |
[] |
expansion de secuencia coincidente con cualquier caracter unico de un conjunto |
~ |
usado al comienzo del nombre de fichero para indicar directorios $home |
{} |
usado para especificar grupos de argumentos con partes comunes |
Metacaracteres de citado
Metacaracter de citado | Accion |
---|---|
/ |
impide el metasignificado del siguiente caracter unico |
' |
impide el metasignificado de un grupo de caracteres |
* |
como ' pero permite variables y expanision de comandos |
Metacaracteres de entrada y salida
Metacaracter de redireccion | Redireccionado |
---|---|
< |
indica redireccion de entrada |
> |
indica redireccion de salida |
Expansion/substitucion de metacaracteres
Metacaracter | Accion |
---|---|
$ |
indica substitucion de variables |
! |
indica subsitucion de historial |
: |
precede modificadores de sustitucion |
^ |
usado en formas especiales de sustitucion de historial |
``` | indica sustitucion de comandos |
Otros metacaracteres
Metacaracter | Resultado |
---|---|
$ |
comienza garabateo de nombres de fichero; indica comentarios de shell |
! |
opcion de refijado (flag) argumentos a comandos. |
prefija especificaciones de nombre de trabajo |