Aprendiendo_Korn_Shell/Secciones/Capitulo3.tex

723 lines
70 KiB
TeX

Un sinónimo común de shell Unix, o de la interfaz que presenta cualquier programa informático, es entorno. Un \emph{entorno} suele ser un conjunto de conceptos que expresan lo que hace un ordenador en términos diseñados para ser comprensibles y coherentes, y con un aspecto y un tacto cómodos.
Por ejemplo, tu escritorio en el trabajo es un entorno. Los conceptos relacionados con el trabajo de oficina suelen incluir notas, llamadas telefónicas, cartas, formularios, etc. Las herramientas que utilizas para ello son papel, grapas, sobres, bolígrafos, teléfono, calculadora, etc. Cada uno de estos elementos tiene una serie de características. Cada una de estas herramientas tiene una serie de características que expresan cómo las utilizas; estas características van desde la ubicación en tu escritorio o en un cajón (en el caso de las herramientas sencillas) hasta cosas más sofisticadas como a qué números están configurados los botones de memoria de tu teléfono. En conjunto, estas características conforman el aspecto de tu escritorio.
Puedes personalizar el aspecto de tu escritorio colocando los bolígrafos donde te resulte más fácil alcanzarlos, programando los botones del teléfono, etc. En general, cuanto mayor sea el grado de personalización, más adaptado estará a tus necesidades personales y, por tanto, más productivo será tu entorno.
De forma similar, los shells de Unix te presentan conceptos como ficheros, directorios y entrada y salida estándar, mientras que el propio Unix te da herramientas para trabajar con ellos, como comandos de manipulación de ficheros, editores de texto y colas de impresión. El aspecto de tu entorno Unix viene determinado por tu teclado y pantalla, por supuesto, pero también por cómo configuras tus directorios, dónde colocas cada tipo de fichero y qué nombres das a los ficheros, directorios y comandos. También hay formas más sofisticadas de personalizar tu entorno shell.
Los medios más básicos de personalización que proporciona el shell Korn son estos:
\begin{description}
\item[Alias:] Sinónimos de comandos o cadenas de comandos que puede definir para mayor comodidad.
\item[Opciones:] Controles para varios aspectos de tu entorno, que puedes activar y desactivar.
\item[Variables:] Marcadores de posición para información que indica al shell y a otros programas cómo comportarse en diversas circunstancias.
\end{description}
También hay formas más complejas de personalizar tu entorno, principalmente la capacidad de programar el shell, que veremos en capítulos posteriores. En este capítulo, cubrimos las técnicas listadas arriba.
Mientras que la mayoría de las personalizaciones obtenibles con las técnicas anteriores son sencillas y se aplican al uso diario de Unix, otras son más bien arcanas y requieren un profundo conocimiento técnico para entenderlas. La mayor parte de este capítulo se concentra en las primeras. Debido a que queremos explicar las cosas desde la perspectiva de las tareas que puede querer realizar, más que desde la de las características específicas del shell Korn, puede que se nos escapen algunos pequeños detalles (como opciones varias para ciertos comandos). Le sugerimos que busque este tipo de información en el Apéndice B.
\section{El Archivo .profile}
Si quieres personalizar tu entorno, lo más importante es que conozcas un fichero llamado \emph{.profile} en tu directorio home (login). Este es un archivo de comandos de shell, también llamado script de shell, que el shell Korn lee y ejecuta cada vez que inicias sesión en tu sistema.
Si utilizas una máquina grande en una oficina o departamento, lo más probable es que el administrador del sistema ya haya configurado un fichero \emph{.profile} para ti que contiene algunas cosas estándar. Este es uno de los ficheros <<ocultos>> mencionados en el \hyperref[sec:Chapter1]{Capítulo 1}; otros ficheros ocultos comunes son \emph{.xinitrc} (para el sistema X Window), \emph{.emacs} (para el editor GNU Emacs) y \emph{.mailrc} (para el programa de correo de Unix).
Tu \emph{.profile}, junto con el fichero de entorno que discutiremos al final de este capítulo, será la fuente de prácticamente todas las personalizaciones que discutiremos aquí y en capítulos posteriores. Por lo tanto, es muy importante que te sientas cómodo con un editor de texto como emph{vi} o Emacs para que puedas probar cualquier técnica de personalización que te apetezca.
Ten en cuenta, sin embargo, que si añades comandos a tu \emph{.profile}, no tendrán efecto hasta que cierres la sesión y vuelvas a entrar, o escribas el comando \texttt{login}. \footnote{Esto tiene el mismo efecto que cerrar la sesión y volver a iniciarla, aunque en realidad sustituye tu sesión de inicio de sesión por una nueva sin terminar explícitamente la sesión anterior.} Por supuesto, no necesitas añadir inmediatamente comandos de personalización a tu .profile - siempre puedes probarlos escribiéndolos tú mismo. (Asegúrate de probar tus cambios: ¡es posible configurar cosas en tu .profile de tal manera que no puedas volver a entrar! Pruebe sus cambios antes de cerrar la sesión, iniciándola de nuevo, quizás desde una nueva ventana o consola virtual).
\begin{lstlisting}[language=bash]
PATH=/sbin:/usr/sbin:/usr/bin:/etc:/usr/ucb:/local/bin:
stty stop ^S intr ^C erase ^?
EDITOR=/usr/local/bin/emacs
SHELL=/bin/ksh
export EDITOR
\end{lstlisting}
Estos comandos configuran un entorno básico para ti, así que probablemente no deberías cambiarlos hasta que aprendas lo que hacen -- lo cual harás al final de este capítulo. Cuando edites tu \emph{.profile}, simplemente pon tus líneas adicionales después.
\subsection{El Archivo \texttt{/etc/profile}}
Cada usuario tiene un archivo \emph{.profile} personal en el directorio home. Aunque es posible que el administrador del sistema le haya proporcionado un archivo \emph{.profile} inicial cuando se configuró su cuenta por primera vez, usted es libre de personalizarlo como considere oportuno.
Existe un archivo de personalización adicional, para todo el sistema, conocido como \emph{/etc/profile}. Si este archivo existe, el shell Korn lo lee y ejecuta como la primera cosa que hace, incluso antes de leer su archivo personal \emph{.profile}. Aquí es donde el administrador del sistema coloca los comandos que deben ser ejecutados por cada usuario al iniciar sesión, y donde él o ella coloca los valores por defecto de todo el sistema, tales como añadir directorios adicionales a la variable \texttt{PATH} (que, como verá más adelante en este capítulo, le dice al shell dónde buscar programas para ejecutar).
ale la pena estar al tanto de este archivo, ya que puede contener configuraciones que usted podría desear anular en su propio archivo \emph{.profile}. (Si el archivo existe, será legible y contendrá comandos de shell, al igual que su \emph{.profile}. Puede valer la pena examinar la versión en su sistema; puede aprender algo de esa manera.
\section{Alias}
Quizás el tipo de personalización más fácil y popular es el \emph{alias}, que es un sinónimo de un comando o cadena de comandos. Esta es una de varias características del shell Korn que fueron apropiadas del shell C.\footnote{Los usuarios del shell C deberían tener en cuenta que la característica alias del shell Korn no soporta argumentos en las expansiones de alias, como hacen los alias del shell C.} Usted define un alias introduciendo (o añadiendo a su \emph{.profile}) una línea con la siguiente forma:
\begin{lstlisting}[language=bash]
alias new=original
\end{lstlisting}
(Observe que no hay espacios a ambos lados del signo igual (=); es la sintaxis requerida). El comando \emph{alias} define \emph{new} como un alias de \emph{original}; cada vez que escriba \emph{new}, el shell Korn sustituirá internamente a \emph{original}. (No puede utilizar ninguno de los caracteres especiales del shell, como *, \$, =, etc., en los nombres de alias).
Existen varias formas básicas de utilizar un alias. La primera, y más sencilla, es utilizar un nombre más mnemotécnico para una orden existente. Muchos comandos Unix de uso común tienen nombres que son mnemotécnicos pobres y por lo tanto son excelentes candidatos para alias; el ejemplo clásico es:
\begin{lstlisting}[language=bash]
alias search=grep
\end{lstlisting}
\emph{grep}, la utilidad de búsqueda de archivos de Unix, deriva su nombre del comando <<g/re/p>> del editor de texto \emph{ed} original, que hace esencialmente lo mismo que grep. (El código de búsqueda de expresiones regulares se eliminó de ed para crear un programa independiente).\footnote{Gracias a Dennis Ritchie y Brian Kernighan, de los laboratorios Bell, por verificarlo. ADR.} Este acrónimo puede significar algo para un informático, pero probablemente no para el administrador de una oficina que tiene que encontrar a \texttt{Fred} en una lista de números de teléfono. Si tiene que encontrar a \texttt{Fred}, y tiene la palabra \emph{search} definida como alias de \emph{grep}, puede escribir:
\begin{lstlisting}[language=bash]
search Fred phonelist
\end{lstlisting}
Otro alias popular evita \emph{exit} en favor de un comando más utilizado para finalizar una sesión de inicio de sesión:
\begin{lstlisting}[language=bash]
alias logout=exit
\end{lstlisting}
Si eres un usuario del shell C, puede que estés acostumbrado a tener un fichero \emph{.logout} de comandos que el shell ejecuta justo antes de cerrar la sesión. El shell Korn no tiene esta característica como tal, pero puedes imitarla fácilmente usando un alias:
\begin{lstlisting}[language=bash]
alias logout='. ~/.ksh\_logout; exit'
\end{lstlisting}
Esto ejecuta los comandos en el archivo \emph{.ksh\_logout} en su directorio home y luego cierra su sesión. El punto y coma actúa como separador de sentencias, permitiéndote tener más de un comando en la misma línea.
Fíjese en las comillas que rodean el valor completo del alias; son necesarias si la cadena a la que se asigna el alias consta de más de una palabra.\footnote{[37] Esto contrasta con los alias del shell C, en los que las comillas no son necesarias.}
Es posible que desee el archivo \emph{.ksh\_logout} para <<limpiar>> sus archivos de historia, como discutimos en el último capítulo. Recuerde que creamos archivos de historial con nombres como \emph{.hist.42}, lo que garantiza un nombre único para cada línea serie o ventana. Para eliminar estos archivos cuando los shells salgan, simplemente ponga esta línea en su archivo \emph{.ksh\_logout}:
\begin{lstlisting}[language=bash]
rm ~/.hist.*
\end{lstlisting}
A algunas personas que no son especialmente buenas mecanógrafas les gusta utilizar alias para los errores tipográficos que cometen a menudo. Por ejemplo:
\begin{lstlisting}[language=bash]
alias emcas=emacs
alias mali=mail
alias gerp=grep
\end{lstlisting}
Esto puede ser útil, pero creemos que probablemente es mejor sufrir con el mensaje de error y tener la ortografía correcta bajo los dedos. Otra forma habitual de utilizar un alias es como abreviatura de una cadena de comandos más larga. Por ejemplo, puede que tengas un directorio al que necesites ir a menudo. Está enterrado profundamente en tu jerarquía de directorios, así que quieres establecer un alias que te permita ir allí sin teclear (o incluso recordar) la ruta completa:
\begin{lstlisting}[language=bash]
alias cdcm='cd ~/work/projects/devtools/windows/confman'
\end{lstlisting}
Como antes, las comillas alrededor del comando \emph{cd} completo son necesarias, porque la cadena que se está aliaseando tiene más de una palabra.
Por ejemplo, una opción útil del comando \emph{ls} es \emph{-F}: pone una barra (/) después de los archivos de directorio y un asterisco (*) después de los archivos ejecutables. (Dependiendo de tu sistema, también puede añadir otros caracteres después de otros tipos de archivos). Como escribir un guión seguido de una letra mayúscula es incómodo, a mucha gente le gusta definir un alias de esta manera:
\begin{lstlisting}[language=bash]
alias lf='ls -F'
\end{lstlisting}
Es importante recordar algunas cosas sobre los alias. En primer lugar, el shell Korn hace una sustitución textual del alias por aquello a lo que está poniendo el alias; puede ayudar imaginarse a ksh pasando su comando a través de un editor de texto o procesador de textos y emitiendo un comando <<change>> o <<substitute>> antes de interpretarlo y ejecutarlo.
Esto, a su vez, significa que cualquier carácter especial (como comodines como * y ?) que resulten cuando se expande el alias son interpretados correctamente por el shell. Esto lleva a un corolario importante: los comodines y otros caracteres especiales no pueden utilizarse en los nombres de los alias, es decir, a la izquierda del signo igual. Por ejemplo, para facilitar la impresión de todos los archivos de su directorio, podría definir el alias:
\begin{lstlisting}[language=bash]
alias printall='pr * | lp'
\end{lstlisting}
En segundo lugar, ten en cuenta que los alias son recursivos, lo que significa que es posible poner un alias a otro alias. Una objeción legítima al ejemplo anterior es que el alias, aunque mnemotécnico, es demasiado largo y no ahorra teclear lo suficiente. Si queremos mantener este alias pero añadir una abreviatura más corta, podríamos definir:
\begin{lstlisting}[language=bash]
alias pa=printall
\end{lstlisting}
El alias recursivo hace posible establecer un <<bucle infinito>> de definiciones, en el que un alias termina (quizás después de varias búsquedas) siendo definido como sí mismo. Por ejemplo, el comando:
\begin{lstlisting}[language=bash]
alias ls='ls -l'
\end{lstlisting}
crea un posible bucle infinito. Por suerte, el shell tiene un mecanismo para protegerse de estos peligros. El comando anterior funciona como se espera (al teclear ls se obtiene una larga lista con permisos, tamaños, propietarios, etc.). Incluso funcionan situaciones más patológicas, como éstas:
\begin{lstlisting}[language=bash]
alias listfile=ls
alias ls=listfile
\end{lstlisting}
Si escribes \emph{listfile}, ls se ejecuta.
Los alias sólo pueden utilizarse al principio de una cadena de comandos, aunque con algunas excepciones. En el ejemplo anterior de cd, es posible que desee definir un alias sólo para el nombre del directorio, no para todo el comando. Pero si define:
\begin{lstlisting}[language=bash]
alias cm=work/projects/devtools/windows/confman
\end{lstlisting}
y luego escriba \texttt{cd cm}, el shell Korn probablemente imprimirá un mensaje como \texttt{ksh: cd: cm: [No such file or directory]}.
Una característica oscura y bastante fea de la facilidad de alias del shell Korn -- que no está presente en la característica análoga del shell C -- proporciona una forma de evitar este problema. Si el valor de un alias (el lado derecho del signo igual) termina en un espacio o en un tabulador, entonces el shell Korn intenta hacer la sustitución del alias en la siguiente palabra de la línea de órdenes. Para hacer que el valor de un alias termine en un espacio, debe rodearlo con comillas.
Esta característica existe para que sea posible tener alias para comandos que a su vez ejecutan otros comandos, como \emph{nohup} y \emph{nice}. Por ejemplo, \emph{nohup} tiene el alias \texttt{'nohup '}. De esta forma, cuando escribes
\begin{lstlisting}[language=bash]
nohup my_favorite_alias somefile
\end{lstlisting}
el shell expandirá \texttt{my\_favorite\_alias} tal como lo haría cuando se escribe sin el comando \emph{nohup} precedente. (El comando \emph{nohup} se describe en el \hyperref[sec:Chapter8]{Capítulo 8}.)
A continuación se muestra cómo utilizar esta capacidad para permitir alias para nombres de directorio, al menos para su uso con el comando \emph{cd}:
\begin{lstlisting}[language=bash]
alias cd='cd '
\end{lstlisting}
Esto hace que el shell Korn busque un alias para el argumento de nombre de directorio a \emph{cd}, que en el ejemplo anterior le permitiría expandir el alias \emph{cm} correctamente.
El shell Korn proporciona una característica de eficiencia llamada <<alias rastreados>>. Retrasaremos la discusión de estos hasta la Sección 3.4.2.8. Además, varios alias están predefinidos por el shell; están listados en el Apéndice B.
Por último, existen algunos complementos útiles para el comando \emph{alias} básico. Si escribe \texttt{alias name} sin un signo igual (=) y value, el shell imprime el valor del alias o \emph{name:} \texttt{alias not found} si no está definido. Si escribe \emph{alias} sin ningún argumento, obtendrá una lista de todos los alias que haya definido, así como de varios que están incorporados. Si escribe \texttt{alias -p}, el shell imprime todos sus alias, con cada uno precedido por la palabra clave \emph{alias}. Esto es útil para guardar todos sus alias de forma que el shell pueda volver a leerlos en otro momento. El comando \texttt{unalias} \emph{name} elimina cualquier definición de alias para su argumento. Si escribe \texttt{unalias -a}, el shell elimina todos los alias.
Los alias son muy útiles para crear un entorno cómodo, pero en realidad son sólo cosas de niños en comparación con técnicas de personalización más avanzadas como scripts y funciones, que veremos en el próximo capítulo. Éstas te dan todo lo que hacen los alias y mucho más, así que si te vuelves experto en ellas, puede que descubras que ya no necesitas los alias. Sin embargo, los alias son ideales para los novatos que encuentran a Unix como un lugar bastante prohibitivo, lleno de tersura y desprovisto de buenos mnemotécnicos.
\section{Opciones}
Aunque los alias le permiten crear nombres convenientes para los comandos, en realidad no le permiten cambiar el comportamiento del shell. Las \emph{opciones} son una forma de hacerlo. Una opción del shell es un ajuste que puede estar <<activado>> o <<desactivado>>. Mientras que varias opciones se relacionan con características arcanas del shell que son de interés sólo para los programadores, las que cubrimos aquí son de interés para los programadores.
Los comandos básicos relacionados con las opciones son \emph{set -o optionnames} y \emph{set +o optionnames}, donde \emph{optionnames} es una lista de nombres de opciones separados por espacios en blanco. El uso de los signos más (+) y menos (-) es contradictorio: el - activa la opción nombrada, mientras que el + la desactiva. La razón de esta incongruencia es que el guión (-) es la forma convencional de Unix de especificar opciones a un comando, mientras que el uso de + es una ocurrencia posterior.
La mayoría de las opciones también tienen abreviaturas de una letra que pueden utilizarse en lugar del comando \emph{set -o}; por ejemplo, \emph{set -o noglob} puede abreviarse \emph{set -f}. Estas abreviaturas provienen del shell Bourne. Al igual que otras características <<extra>> del shell Korn, existen para asegurar la compatibilidad; de lo contrario, no se recomienda su uso.
\newpage
La Tabla \ref{tab3.1} lista las opciones que son útiles para los usuarios generales de Unix. Todas ellas están desactivadas por defecto excepto cuando se indica lo contrario.
\begin{table}[h]
\center
\caption{Opciones básicas del shell}
\label{tab3.1}
\begin{tabular}{|m{2cm}|m{13cm}|} \hline
\textbf{Opción} & \textbf{Descripción} \\ \hline
\emph{bgnice} & Ejecutar trabajos en segundo plano con prioridad baja (activada por defecto). \\\hline
\emph{emacs} & Entra en el modo de edición emacs. \\\hline
\emph{ignoreeof} & No permitir el uso de CTRL-D para cerrar la sesión; requerir el comando exit. \\\hline
\emph{markdirs} & Al expandir los comodines de nombre de archivo, añada una barra (/) a los directorios. \\\hline
\emph{noclobber} & No permita que la redirección de salida (>) destruya un archivo existente. \\\hline
\emph{noglob} & No expanda los comodines de nombre de archivo como * y ? (la expansión de comodines a veces se denomina globbing). \\\hline
\emph{nounset} & Indicar un error al intentar utilizar una variable que no está definida. \\\hline
\emph{trackall} & Activar el seguimiento de alias. (En realidad, el shell ignora la configuración de esta opción; el seguimiento de alias siempre está activado. Esto se discute en la Sección 3.4.2.8, más adelante en este capítulo). \\\hline
\emph{vi} & Entra en el modo de edición vi. \\\hline
\end{tabular}
\end{table}
Existen otras opciones (22 en total; el Apéndice B las enumera). Para comprobar el estado de una opción, escriba \emph{set -o}. El shell Korn imprime una lista de todas las opciones junto con su configuración. No existe un comando para probar opciones individuales, pero aquí hay una simple función del shell para hacerlo:
\begin{lstlisting}[language=bash]
function testopt {
if [[ -o $1 ]] ; then
print Option $1 is on.
else
print Option $1 is off.
fi
}
\end{lstlisting}
Las funciones de shell se tratan en el próximo capítulo. Por ahora, sin embargo, si desea utilizar la función \emph{testopt}, simplemente escríbala en su archivo \emph{.profile} o de entorno (consulte la Sección 3.5.2, más adelante en este capítulo), escriba \texttt{login} o \texttt{. .profile}. (Sí, el punto, o <<dot>>, es en realidad un comando; vea la \hyperref[sec:4.1]{Sección 4.1} en el \hyperref[sec:Chapter]{Capítulo 4}.) Luego puede escribir \emph{testopt optionname} para comprobar el estado de una opción.
\section{Variables de Shell}
Hay varias características de su entorno que puede que desee personalizar pero que no pueden expresarse como una opción on/off. Las características de este tipo se especifican en variables de shell. Las variables de shell pueden especificarlo todo, desde la cadena del prompt hasta la frecuencia con la que el shell comprueba si hay correo nuevo.
Al igual que un alias, una variable de shell es un nombre que tiene un valor asociado. El shell Korn mantiene un registro de varias variables del shell incorporadas; los programadores del shell pueden añadir las suyas propias. Por convención, las variables incorporadas tienen nombres en mayúsculas. La sintaxis para definir variables es similar a la sintaxis para alias:
\begin{lstlisting}[language=bash]
varname=value
\end{lstlisting}
No debe haber ningún espacio a ambos lados del signo igual, y si el valor tiene más de una palabra, debe ir entre comillas. Para utilizar el valor de una variable en un comando, preceda su nombre con un signo de dólar (\$).
Puede eliminar una variable con el comando \texttt{unset varname}. Normalmente, esto no es útil, ya que se supone que todas las variables que no existen son nulas, es decir, iguales a la cadena vacía ''''. Pero si utiliza la opción \texttt{nounset} (véase la Tabla 3-1), que hace que el shell indique un error cuando encuentra una variable indefinida, puede interesarle unset.
La forma más sencilla de comprobar el valor de una variable es utilizar el comando integrado \texttt{print}\footnote{El shell Korn soporta el antiguo comando \texttt{echo}, que hace prácticamente lo mismo, por razones de compatibilidad con versiones anteriores. Sin embargo, recomendamos encarecidamente \texttt{print} porque sus opciones son las mismas en todos los sistemas Unix, mientras que las opciones de \texttt{echo} difieren entre las diferentes versiones de Unix. No es probable que esto cambie; el estándar POSIX dice que las opciones de \texttt{echo} están definidas por la implementación.}. Todo lo que hace \texttt{print} es imprimir sus argumentos, pero no hasta que el shell los haya evaluado. Esto incluye -- entre otras cosas que se discutirán más adelante -- tomar los valores de las variables y expandir los comodines de nombre de archivo. Así, si la variable \emph{fred} tiene el valor \texttt{bob}, escribir lo siguiente hace que el shell simplemente imprima \texttt{bob}:
\begin{lstlisting}[language=bash]
print "$fred"
\end{lstlisting}
Si la variable no está definida, el shell imprime una línea en blanco. Una forma más verbosa de hacer esto es:
\begin{lstlisting}[language=bash]
print "The value of \$varname is \"$varname\"."
\end{lstlisting}
El primer signo de dólar y las comillas dobles interiores se escriben con barra diagonal inversa (es decir, precedidas de \ para que el shell no intente interpretarlas; véase el \hyperref[sec:Chapter1]{Capítulo 1}) para que aparezcan literalmente en la salida, que para el ejemplo anterior sería:
\begin{lstlisting}[language=bash]
The value of $fred is "bob".
\end{lstlisting}
\subsection{Variables y Citas}
Fíjese en que hemos utilizado comillas dobles alrededor de las variables (y de las cadenas que las contienen) en estos ejemplos de \emph{impresión}. En el \hyperref[sec:Chapter1]{Capítulo 1} dijimos que algunos caracteres especiales entre comillas dobles se interpretan (mientras que ninguno se interpreta entre comillas simples).
Quizás el carácter especial más importante que <<sobrevive>> a las comillas dobles es el signo del dólar, que significa que las variables se evalúan. Es posible prescindir de las comillas dobles en algunos casos; por ejemplo, podríamos haber escrito la orden print anterior de esta forma:
\begin{lstlisting}[language=bash]
print The value of \$varname is \"$varname\".
\end{lstlisting}
Pero las comillas dobles son más generalmente correctas.
He aquí por qué. Supongamos que hacemos esto:
\begin{lstlisting}[language=bash]
fred='Four spaces between these words.'
\end{lstlisting}
Entonces, si introducimos el comando print \texttt{\$fred}, el resultado sería:
\begin{lstlisting}[language=bash]
Four spaces between these words.
\end{lstlisting}
¿Qué ha pasado con los espacios extra? Sin las comillas dobles, el shell divide la cadena en palabras después de sustituir el valor de la variable, como hace normalmente cuando procesa líneas de comandos. Las comillas dobles evitan esta parte del proceso (haciendo que el shell piense que toda la cadena entrecomillada es una sola palabra).
Por lo tanto el comando \texttt{print "\$fred"} imprime esto:
\begin{lstlisting}[language=bash]
Four spaces between these words.
\end{lstlisting}
Esto se vuelve especialmente importante cuando empezamos a tratar con variables que contienen entradas de usuario o archivos más adelante. En particular, cada vez es más común encontrar directorios disponibles en sistemas Unix a través de la red desde sistemas Apple Macintosh y Microsoft Windows, donde los espacios y otros caracteres inusuales son comunes en los nombres de archivos.
Las comillas dobles también permiten que funcionen otros caracteres especiales, como veremos en el \hyperref[sec:Chapter4]{Capítulo 4}, el \hyperref[sec:Chapter6]{Capítulo 6} y el \hyperref[sec:Chapter7]{Capítulo 7}. Pero por ahora, revisaremos la regla <<En caso de duda, use comillas simples>> del capítulo 1 añadiendo: ''...a menos que una cadena contenga una variable, en cuyo caso deberá usar comillas dobles>>.
\subsection{Variables Incorporadas}
Al igual que con las opciones, algunas variables incorporadas al shell son significativas para los usuarios generales de Unix, mientras que otras son arcanos para los programadores profesionales. Aquí veremos las más útiles en general, y dejaremos algunas de las más oscuras para capítulos posteriores. De nuevo, el Apéndice B contiene una lista completa.
\subsubsection{Edición de Variables de Modo}
Varias variables del shell se relacionan con los modos de edición de la línea de comandos que vimos en el capítulo anterior. Éstas se enumeran en la Tabla \ref{tab3.2}.
Las dos primeras son usadas a veces por editores de texto y otros programas orientados a pantalla, que dependen de que las variables estén configuradas correctamente. Aunque el shell Korn y la mayoría de los sistemas de ventanas deberían saber cómo establecerlas correctamente, debería mirar los valores de \texttt{COLUMNS} y \texttt{LINES} si tiene problemas de visualización con un programa orientado a pantalla.
\begin{table}[h]
\center
\caption{Variables del modo de edición}
\label{tab3.2}
\begin{tabular}{|m{3cm}|m{12cm}|} \hline
\textbf{Variable} & \textbf{Significado} \\ \hline
\texttt{COLUMNS} & Anchura, en columnas de caracteres, de su terminal. El valor estándar es 80 (a veces 132), aunque si está utilizando un sistema de ventanas como X, puede dar a una ventana de terminal cualquier tamaño que desee. \\\hline
\texttt{LINES} & Longitud de su terminal en líneas de texto. El valor estándar para terminales es 24, pero para monitores compatibles con IBM PC es 25; una vez más, si está utilizando un sistema de ventanas, normalmente puede cambiar el tamaño a cualquier cantidad. \\\hline
\texttt{HISTFILE} & Nombre del fichero histórico sobre el que operan los modos de edición. \\\hline
\texttt{EDITOR} & Nombre de ruta de tu editor de texto favorito; el sufijo (\texttt{macs}\tablefootnote{Este sufijo también funciona si tu editor es una versión diferente de Emacs cuyo nombre no termina en emacs.} o \texttt{vi}) determina qué modo de edición utilizar. \\\hline
\texttt{VISUAL} & Similar a \texttt{EDITOR}; si se establece, se utiliza con preferencia a \texttt{EDITOR} para elegir el modo de edición. \\\hline
\texttt{HISTEDIT} & Nombre de ruta del editor que se utilizará con el comando \emph{hist}. \\\hline
\end{tabular}
\end{table}
\subsubsection{Variables de Correo}
Dado que el programa de correo no se ejecuta todo el tiempo, no hay forma de que le informe cuando recibe correo nuevo; por lo tanto, el intérprete de órdenes hace esto en su lugar\footnote{El comúnmente disponible comando \emph{biff} hace un mejor trabajo en este sentido; mientras que el shell Korn sólo imprime mensajes de <<tiene correo>> justo antes de imprimir las indicaciones de comandos, \emph{biff} puede decirle de quién es el correo.}. El intérprete de órdenes no puede comprobar realmente si hay correo entrante, pero puede mirar su fichero de correo periódicamente y determinar si el fichero ha sido modificado desde la última comprobación. Las variables listadas en la Tabla \ref{tab3.3} le permiten controlar cómo funciona esto.
\begin{table}[h]
\center
\caption{Variables de correo}
\label{tab3.3}
\begin{tabular}{|m{3cm}|m{12cm}|} \hline
\textbf{Variable} & \textbf{Significado} \\ \hline
\texttt{MAIL} & Nombre del archivo para comprobar el correo entrante (es decir, su archivo de correo) \\\hline
\texttt{MAILCHECK} & Con qué frecuencia, en segundos, se comprueba si hay correo nuevo (por defecto 600 segundos, o 10 minutos) \\\hline
\texttt{MAILPATH} & Lista de nombres de archivo, separados por dos puntos (:), para comprobar el correo entrante \\\hline
\texttt{\_(guión bajo)} & Cuando se utiliza dentro de \texttt{\$MAILPATH}, nombre del archivo de correo que ha cambiado; véase el texto para otros usos \\\hline
\end{tabular}
\end{table}
En el escenario más simple, usted utiliza el programa de correo estándar de Unix, y su fichero de correo es \emph{/var/mail/yourname} o algo similar. En este caso, sólo tendría que establecer la variable \texttt{MAIL} a este nombre de archivo si desea que se compruebe su correo:
\begin{lstlisting}[language=bash]
MAIL=/var/mail/yourname
\end{lstlisting}
Si su administrador del sistema no lo ha hecho ya por usted, ponga una línea como ésta en su \emph{.profile}.
Sin embargo, algunas personas utilizan mailers no estándar que usan múltiples ficheros de correo; \texttt{MAILPATH} fue diseñado para acomodar esto. El shell Korn utiliza el valor de \texttt{MAIL} como el nombre del fichero a comprobar, a menos que se establezca \texttt{MAILPATH}, en cuyo caso el shell comprueba cada fichero de la lista \texttt{MAILPATH} en busca de correo nuevo. Puede utilizar este mecanismo para que el shell imprima un mensaje diferente para cada archivo de correo: para cada nombre de archivo de correo en \texttt{MAILPATH}, añada un signo de interrogación seguido del mensaje que desea imprimir.
Por ejemplo, supongamos que tienes un sistema de correo que ordena automáticamente el correo en archivos según el nombre de usuario del remitente. Tiene archivos de correo llamados \emph{/var/mail/usted/fritchie}, \emph{/var/mail/usted/droberts}, \emph{/var/mail/usted/jphelps}, etc. Usted define su \texttt{MAILPATH} como sigue:
\begin{lstlisting}[language=bash]
MAILPATH=/var/mail/you/fritchie:/var/mail/you/droberts:\
/var/mail/you/jphelps
\end{lstlisting}
Si recibe correo de Jennifer Phelps, el archivo \emph{/var/mail/you/jphelps} cambia. El shell Korn se da cuenta del cambio en 10 minutos e imprime el mensaje:
\begin{lstlisting}[language=bash]
you have mail in /var/mail/you/jphelps.
\end{lstlisting}
Si está en medio de la ejecución de un comando, el shell espera hasta que el comando finalice (o se suspenda) para imprimir el mensaje. Para personalizar esto aún más, puede definir \texttt{MAILPATH} como:
\begin{lstlisting}[language=bash]
MAILPATH=\
/var/mail/you/fritchie?You have mail from Fiona.:\
/var/mail/you/droberts?Mail from Dave has arrived.:\
/var/mail/you/jphelps?There is new mail from Jennifer.
\end{lstlisting}
Las barras invertidas al final de cada línea le permiten continuar su comando en la línea siguiente. Pero ten cuidado: no puedes sangrar las líneas siguientes. Ahora, si recibe correo de Jennifer, el shell imprime:
\begin{lstlisting}[language=bash]
There is new mail from Jennifer.
\end{lstlisting}
Dentro de las partes de mensaje de \texttt{MAILPATH}, puede utilizar la variable especial \_ (guión bajo) para el nombre del archivo que activa el mensaje:
\begin{lstlisting}[language=bash]
MAILPATH='/var/mail/you/fritchie?You have mail from Fiona in \$_.'
MAILPATH+=':/var/mail/you/droberts?Mail from Dave has arrived, check \$_.'
MAILPATH+=':/var/mail/you/jphelps?There is new mail from Jennifer, look at \$_.'
\end{lstlisting}
En realidad, el significado de \$\_ varía en función de dónde y cómo se utilice:
\emph{Inside the value of MAILPATH:} Como se acaba de describir, utilice \$\_ para el nombre del archivo que activa un mensaje en el valor de MAILPATH.
\emph{El último argumento del último comando interactivo:} Cuando se utiliza en una línea de comandos introducida de forma interactiva, \$\_ representa la última palabra de la línea de comandos anterior:
\begin{lstlisting}[language=bash]
$ print hi # Ejecutar un comando
hi
$ print _$ # Verificar la configuracion de $_
hi
$ print hello # Nuevo ultimo argumento
hello
$ print _$
hello
$ print "hi there" # El uso se basa en palabras
hi there
$ print _$
hi there
\end{lstlisting}
Este uso de \$\_ es similar a la función !\$ del mecanismo de historial del intérprete de comandos C.
\emph{Dentro de un script:} Cuando se accede desde dentro de un script de shell, \$\_ es la ruta completa utilizada para encontrar e invocar el script:
\begin{lstlisting}[language=bash]
$ cat /tmp/junk # Mostrar programa de pruebas
print _ is $_
$ PATH=/tmp:$PATH # Agregar directorio a PATH
$ junk # Ejecutar el programa
_ is /tmp/junk
\end{lstlisting}
\subsubsection{Variables de prompt}
Si has visto trabajar a suficientes usuarios experimentados de Unix, puede que ya te hayas dado cuenta de que el prompt del shell no está grabado en piedra. Parece que uno de los pasatiempos favoritos de los programadores profesionales de Unix es pensar en cadenas de prompt bonitas o innovadoras. Te daremos algo de la información que necesitas para hacer la tuya aquí; el resto viene en el próximo capítulo.
En realidad, el shell Korn utiliza cuatro cadenas de prompt. Se almacenan en las variables \texttt{PS1}, \texttt{PS2}, \texttt{PS3} y \texttt{PS4}. La primera de ellas se llama prompt primario; es el prompt habitual del shell, y su valor por defecto es \texttt{"\$ "} (un signo de dólar seguido de un espacio). A mucha gente le gusta establecer su prompt primario a algo que contenga su nombre de usuario. Esta es una forma de hacerlo:
\begin{lstlisting}[language=bash]
PS1="($LOGNAME)-> "
\end{lstlisting}
\texttt{LOGNAME} es otra variable incorporada en el shell, que se establece con tu nombre de usuario cuando te conectas\footnote{Algunos sistemas muy antiguos utilizan \texttt{USER} en su lugar. Afortunadamente, estos sistemas son cada vez más raros.}. Así, \texttt{PS1} se convierte en un paréntesis a la izquierda, seguido de tu nombre de usuario, seguido de \texttt{")-> "}. Si su nombre de usuario es fred, su prompt será \texttt{"(fred)-> "}.Si usted es un usuario del shell C y, como muchas de esas personas, está acostumbrado a tener un número de comando en su prompt string, el shell Korn puede hacer esto de forma similar al shell C: si hay un signo de exclamación en la prompt string, sustituye el número de comando. Así, si define su cadena de comandos como la siguiente, sus comandos se verán como \texttt{(fred 1)->}, \texttt{(fred 2)->} , y así sucesivamente:
\begin{lstlisting}[language=bash]
PS1="($LOGNAME !)->"
\end{lstlisting}
Quizás la forma más útil de configurar tu cadena de prompt es que siempre contenga tu directorio actual. Así no necesitará teclear \texttt{pwd} para recordar dónde se encuentra. Poner su directorio en el prompt es más complicado que los ejemplos anteriores, porque su directorio actual cambia durante su sesión de login, a diferencia de su nombre de usuario y el nombre de su máquina. Pero podemos acomodar esto aprovechando los diferentes tipos de comillas. He aquí cómo:
\begin{lstlisting}[language=bash]
PS1='($PWD)-> '
\end{lstlisting}
La diferencia son las comillas simples, en lugar de las dobles, que rodean la cadena a la derecha de la asignación. El truco está en que esta cadena se evalúa dos veces: una cuando se realiza la asignación a \texttt{PS1} (en su archivo \emph{.profile} o de entorno) y otra después de cada comando que introduzca. Esto es lo que hace cada una de estas evaluaciones:
\begin{enumerate}
\item{La primera evaluación observa las comillas simples y devuelve lo que hay dentro de ellas sin más procesamiento. Como resultado, \texttt{PS1} contiene la cadena \texttt{(\$PWD)-> }.}
\item{Después de cada comando, el shell evalúa \texttt{(\$PWD)->}. \texttt{PWD} es una variable incorporada que siempre es igual al directorio actual, por lo que el resultado es un prompt primario que siempre contiene el directorio actual\footnote{El intérprete de comandos también realiza sustituciones aritméticas y de comandos en el valor de \texttt{PS1}, pero aún no hemos tratado estas funciones. Véase el \hyperref[sec:Chapter6]{Capítulo 6}.}.} \\
\end{enumerate}
En el capítulo 7 analizaremos más a fondo las sutilezas de la cita y la evaluación diferida.
\texttt{PS2} se denomina cadena secundaria; su valor por defecto es ''> '' (un signo mayor que seguido de un espacio). Se utiliza cuando escribe una línea incompleta y pulsa ENTER, como indicación de que debe terminar su comando. Por ejemplo, suponga que comienza una cadena entrecomillada pero no cierra las comillas. Si pulsa INTRO, el shell imprime > y espera a que termine la cadena:
\begin{lstlisting}[language=bash]
$ x="This is a long line, # PS1 para los comandos
> which is terminated down here" # PS2 para la continuacion
$ # PS1 para el siguiente comando
\end{lstlisting}
\texttt{PS3} y \texttt{PS4} se refieren a la programación y depuración del shell, respectivamente; se explican en el \hyperref[sec:Chapter5]{Capítulo 5} y el \hyperref[sec:Chapter9]{Capítulo 9}.
\subsubsection{Uso de los Números de Comando del Historial}
El número del comando histórico actual está disponible en la variable de entorno \texttt{HISTCMD}. Puede ver el número de historial actual en su prompt colocando un ! (o \texttt{\$HISTCMD}) en algún lugar del valor de la variable \texttt{PS1}:
\begin{lstlisting}[language=bash]
$ PS1="command !> "
command 42> ls -FC *.xml
appa.xml appd.xml ch01.xml ch04.xml ch07.xml ch10.xml
appb.xml appf.xml ch02.xml ch05.xml ch08.xml colo1.xml
appc.xml ch00.xml ch03.xml ch06.xml ch09.xml copy.xml
command 43>
\end{lstlisting}
Para obtener un literal ! en el valor de su prompt, coloque !! en \texttt{PS1}.
\subsubsection{Tipo de Terminales}
Hoy en día, el uso más común del intérprete de comandos es desde dentro de una ventana de emulador de terminal que se muestra en la pantalla de alta resolución de una estación de trabajo o PC. Sin embargo, el programa emulador de terminal todavía emula las facilidades proporcionadas por los terminales CRT serie reales de antaño. Como tal, la variable de shell \texttt{TERM} es de vital importancia para cualquier programa que utilice toda su ventana, como un editor de texto. Tales programas incluyen editores de pantalla tradicionales (como \emph{vi} y Emacs), programas de paginación como \emph{more}, e innumerables aplicaciones de terceros.
Debido a que los usuarios pasan cada vez más tiempo dentro de los programas y cada vez menos utilizando el propio shell, es extremadamente importante que su \texttt{TERM} esté configurado correctamente. Es realmente el trabajo de su administrador del sistema para ayudarle a hacer esto (o hacerlo por usted), pero en caso de que necesite hacerlo usted mismo, aquí hay algunas pautas.
El valor de \texttt{TERM} debe ser una cadena de caracteres corta con letras minúsculas que aparezca como nombre de archivo en la base de datos \emph{terminfo}.\footnote{Las versiones de Unix no derivadas de System V utilizan \emph{termcap}, una base de datos de capacidades de terminal de estilo antiguo que utiliza el único archivo de texto \emph{/etc/termcap} para todas las descripciones de terminal. Los sistemas modernos suelen tener disponibles tanto el fichero \emph{/etc/termcap} como la base de datos \emph{terminfo}. Los sistemas BSD actuales usan una base de datos indexada de un solo fichero, \emph{/usr/share/misc/termcap.db}.} Esta base de datos es un directorio de dos niveles de archivos bajo el directorio raíz \emph{/usr/share/terminfo}.\footnote{Esta es la ubicación típica en los sistemas modernos. Los sistemas más antiguos lo tienen en \emph{/usr/lib/terminfo}.} Este directorio contiene subdirectorios con nombres de un solo carácter; éstos, a su vez, contienen archivos de información del terminal para todos los terminales cuyos nombres empiecen por ese carácter. Cada archivo describe cómo decirle al terminal en cuestión que haga ciertas cosas comunes como colocar el cursor en la pantalla, pasar a vídeo inverso, desplazarse, insertar texto, etc. Las descripciones están en formato binario (es decir, no legibles por humanos).
Los nombres de los archivos de descripción de terminales son los mismos que los del terminal que se está describiendo; a veces se utiliza una abreviatura. Por ejemplo, el DEC VT100 tiene una descripción en el archivo \emph{/usr/share/terminfo/v/vt100}; la consola basada en caracteres GNU/Linux tiene una descripción en el archivo \emph{/usr/share/terminfo/l/linux}. Una ventana de terminal xterm bajo el sistema X Window tiene una descripción en \emph{/usr/share/terminfo/x/xterm}.
A veces su software Unix no configura \texttt{TERM} correctamente; esto ocurre a menudo en terminales X y sistemas Unix basados en PC. Por lo tanto, debería comprobar el valor de \texttt{TERM} tecleando \texttt{print \$TERM} antes de seguir adelante. Si encuentra que su sistema Unix no está configurando el valor correcto para usted (especialmente probable si su terminal es de una marca diferente a la de su ordenador), necesitará encontrar el valor apropiado de \texttt{TERM} usted mismo.
La mejor manera de encontrar el valor \texttt{TERM} -- si no puede encontrar un gurú local que lo haga por usted -- es adivinar el nombre \emph{terminfo} y buscar un archivo con ese nombre bajo \emph{/usr/share/terminfo} usando \emph{ls}. Por ejemplo, si tu terminal es un Blivitz BL-35A, podrías probar:
\begin{lstlisting}[language=bash]
$ cd /usr/share/terminfo
$ ls b/bl*
\end{lstlisting}
Si tiene éxito, verá algo como esto:
\begin{lstlisting}[language=bash]
bl35a blivitz35a
\end{lstlisting}
En este caso, es probable que los dos nombres sean sinónimos de (enlaces a) la misma descripción de terminal, por lo que podría utilizar cualquiera de ellos como valor de \texttt{TERM}. En otras palabras, podría poner cualquiera de estas dos líneas en su \emph{.profile}:
\begin{lstlisting}[language=bash]
TERM=bl35a
TERM=blivitz35a
\end{lstlisting}
Si no tiene éxito, \emph{ls} no imprimirá nada, y tendrá que hacer otra suposición e intentarlo de nuevo. Si encuentra que \emph{terminfo} no contiene nada que se parezca a su terminal, no todo está perdido. Consulte el manual de su terminal para ver si el terminal puede emular un modelo más popular; hoy en día las probabilidades de que esto ocurra son excelentes.
Por el contrario, \emph{terminfo} puede tener varias entradas relacionadas con su terminal, para submodelos, modos especiales, etc. Si puede elegir qué entrada utilizar como valor de \texttt{TERM}, le sugerimos que pruebe cada una de ellas con su editor de texto o cualquier otro programa orientado a pantalla que utilice y vea cuál funciona mejor.
El proceso es mucho más sencillo si estás utilizando un sistema de ventanas, en el que tus <<terminales>> son porciones lógicas de la pantalla en lugar de dispositivos físicos. En este caso, el software dependiente del sistema operativo fue escrito para controlar su(s) ventana(s) de terminal, por lo que las probabilidades son muy buenas de que si sabe cómo manejar el cambio de tamaño de la ventana y el complejo movimiento del cursor, sea capaz de lidiar con cosas simples como \texttt{TERM}. El sistema X Window, por ejemplo, establece automáticamente <<xterm>> como valor para \texttt{TERM} en una ventana de terminal xterm.
\subsubsection{Ruta de Búsqueda de Comandos}
Otra variable importante es \texttt{PATH}, que ayuda al shell a encontrar los comandos que introduzcas.
Como probablemente sepas, cada comando que utilizas es en realidad un archivo que contiene código para que tu máquina lo ejecute.\footnote{A menos que sea un comando incorporado (como \emph{cd} y \emph{print}), en cuyo caso el código es simplemente parte del archivo ejecutable para todo el shell.} Estos archivos se denominan archivos ejecutables o simplemente ejecutables. Se almacenan en varios directorios. Algunos directorios, como \emph{/bin} o \emph{/usr/bin}, son estándar en todos los sistemas Unix; otros dependen de la versión concreta de Unix que estés utilizando; algunos son exclusivos de tu máquina; si eres programador, algunos pueden ser incluso tuyos. En cualquier caso, no hay ninguna razón por la que debas saber dónde está el archivo ejecutable de un comando para poder ejecutarlo.
Aquí es donde entra \texttt{PATH}. Su valor es una lista de directorios que el shell busca cada vez que usted introduce un nombre de comando que no contiene una barra; los nombres de directorio están separados por dos puntos (:), igual que los archivos en \texttt{MAILPATH}. Por ejemplo, si escribe print \texttt{\$PATH}, verá algo como esto:
\begin{lstlisting}[language=bash]
/sbin:/usr/sbin:/usr/bin:/etc:/usr/X11R6/bin:/local/bin
\end{lstlisting}
¿Por qué debería preocuparse por su path? Hay tres razones principales. Primero, hay aspectos de seguridad en su valor, que tocaremos en breve. En segundo lugar, una vez que haya leído los últimos capítulos de este libro e intente escribir sus propios programas shell, querrá probarlos y eventualmente reservar un directorio para ellos. Tercero, su sistema puede estar configurado para que los archivos ejecutables de ciertos comandos <<restringidos>> se mantengan en directorios que no están listados en \texttt{PATH}. Por ejemplo, puede haber un directorio \emph{/usr/games} en el que haya ejecutables que estén prohibidos durante las horas normales de trabajo.
Por lo tanto, es posible que desee añadir directorios al \texttt{PATH} por defecto que se obtiene al iniciar sesión. Digamos que ha creado un directorio \emph{bin} bajo su directorio de inicio de sesión para sus propios scripts y programas de shell. Para añadir este directorio a su \texttt{PATH} para que esté allí cada vez que inicie sesión, ponga esta línea en su \emph{.profile}:
\begin{lstlisting}[language=bash]
PATH="$PATH:$HOME/bin"
\end{lstlisting}
Esto establece \texttt{PATH} a lo que era antes, seguido inmediatamente por dos puntos y \texttt{\$HOME/bin} (su directorio \emph{bin} personal). Este es un uso bastante típico. (El uso de \texttt{\$HOME} permite al administrador del sistema mover tu directorio personal de lugar, sin que tengas que arreglar tu archivo \emph{.profile}).
Hay un detalle adicional importante que hay que entender sobre cómo funciona el \texttt{PATH}. Tiene que ver con los elementos vacíos (o <<nulos>>) en el \texttt{PATH}. Un elemento nulo puede ocurrir de tres maneras: colocando dos puntos al principio de la \texttt{PATH}, colocando dos puntos al final de la \texttt{PATH}, o colocando dos dos puntos adyacentes en medio de la \texttt{PATH}. El shell trata un elemento nulo en \texttt{PATH} como sinónimo de ''.'', el directorio actual, y busca en cualquier directorio en el que se encuentre en ese punto de la búsqueda de ruta.
\begin{lstlisting}[language=bash]
PATH=:$HOME/bin:/usr/bin:/usr/local/bin # Buscar primero en el directorio actual
PATH=$HOME/bin:/usr/bin:/usr/local/bin: # Ultima busqueda en el directorio actual
PATH=$HOME/bin::/usr/bin:/usr/local/bin # Segunda busqueda en el directorio actual
\end{lstlisting}
Por último, si necesita saber de qué directorio proviene un comando, no es necesario que busque en los directorios de su \texttt{PATH} hasta encontrarlo. El comando incorporado al shell \emph{whence} imprime la ruta completa del comando que le das como argumento, o sólo el nombre del comando si es un comando incorporado en sí mismo (como \emph{cd}), un alias o una función (como veremos en el \hyperref[sec:Chapter4]{Capítulo 4}).
\subsubsection{Consideraciones de Seguridad de PATH}
La configuración de la variable \texttt{PATH} puede tener importantes implicaciones para la seguridad.
En primer lugar, tener el directorio actual en su ruta es un verdadero agujero de seguridad, especialmente para los administradores del sistema, y la cuenta \texttt{root} nunca debe tener un elemento nulo (o punto explícito) en su ruta de búsqueda. ¿Por qué? Piensa en alguien que crea un script de shell llamado, por ejemplo, \emph{ls}, lo hace ejecutable y lo coloca en un directorio al que \texttt{root} podría acceder mediante \emph{cd}, como \emph{/tmp}:
\begin{lstlisting}[language=bash]
rm -f /tmp/ls # Ocultar las pruebas
/bin/ls "$@" # Ejecutar ls real
nasty stuff here # Ejecutar silenciosamente otras cosas como root
\end{lstlisting}
Si \texttt{root} tiene el directorio actual primero en \texttt{PATH}, entonces \texttt{cd /tmp; ls} hace lo que el malhechor quiera, y \texttt{root} no se entera. (Esto se conoce en el mundo de la seguridad como un <<troyano>>.) Aunque es menos grave para los usuarios normales, hay muchos expertos que todavía aconsejan no tener el directorio actual en \texttt{PATH}.
En segundo lugar, la forma más segura de añadir su papelera personal a \texttt{PATH} es al final. Cuando se introduce un comando, el shell busca en los directorios en el orden en que aparecen en PATH hasta que encuentra un archivo ejecutable. Por lo tanto, si tiene un script de shell o un programa cuyo nombre es el mismo que el de un comando existente, el shell utilizará el comando existente, a menos que escriba la ruta completa del comando para desambiguar. Por ejemplo, si ha creado su propia versión del comando \emph{more} en \texttt{\$HOME/bin} y su \texttt{PATH} tiene \texttt{\$HOME/bin} al final, para obtener su versión deberá escribir \texttt{\$HOME/bin/more} (o simplemente \texttt{\~{}/bin/more}).
La forma más temeraria de reajustar tu ruta es decirle al shell que busque primero en tu directorio poniéndolo antes de los otros directorios en tu \texttt{PATH}:
\begin{lstlisting}[language=bash]
PATH="$HOME/bin:$PATH"
\end{lstlisting}
Esto es menos seguro porque estás confiando en que tu propia versión del comando \emph{more} funcione correctamente. Pero también es arriesgado, ya que podría permitir caballos de Troya (similar al ejemplo de \emph{ls} que acabamos de ver). Si tu directorio \emph{bin} es escribible por otros en tu sistema, pueden instalar un programa que haga algo desagradable.
El uso apropiado de \texttt{PATH} es sólo uno de los muchos aspectos de la seguridad del sistema. Vea el \hyperref[sec:Chapter10]{Capítulo 10} para más detalles. En resumen, recomendamos dejar el directorio actual fuera de su \texttt{PATH} (tanto implícita como explícitamente), añadir su directorio \emph{bin} personal al final de \texttt{PATH}, y asegurarse de que sólo usted puede crear, eliminar o cambiar archivos en su directorio \emph{bin} personal.
\subsubsection{PATH y Alias Rastreados}
Vale la pena señalar que una búsqueda a través de los directorios en su \texttt{PATH} puede tomar tiempo. No morirá exactamente si aguanta la respiración durante el tiempo que la mayoría de los ordenadores tardan en buscar en su \texttt{PATH}, pero el gran número de operaciones de E/S de disco implicadas en algunas búsquedas en el \texttt{PATH} pueden llevar más tiempo que el que tarda en ejecutarse el comando invocado.
El shell Korn proporciona una forma de eludir las búsquedas \texttt{PATH}, llamada alias rastreado. En primer lugar, observe que si especifica un comando indicando su ruta completa, el intérprete de órdenes ni siquiera utilizará su \texttt{PATH}, sino que irá directamente al archivo ejecutable. Los alias rastreados hacen esto automáticamente. La primera vez que invocas un comando, el shell busca el ejecutable de la forma normal (a través de \texttt{PATH}). A continuación, crea un alias para la ruta completa, de modo que la próxima vez que invoque el comando, el intérprete de comandos utilice la ruta completa y no se preocupe por el \texttt{PATH}. Si alguna vez cambias tu \texttt{PATH}, el shell marca los alias rastreados como <<indefinidos>>, de modo que vuelve a buscar las rutas completas cuando invoques los comandos correspondientes.
De hecho, puedes añadir alias rastreados con el único propósito de evitar la búsqueda en el \texttt{PATH} de comandos que utilizas con especial frecuencia. Sólo tienes que poner un <<alias trivial>> de la forma \texttt{alias -t command} en tu archivo \emph{.profile} o de entorno; el shell sustituye el nombre de ruta completo por sí mismo.
Por ejemplo, la primera vez que invocas emacs, el shell hace una búsqueda \texttt{PATH}. Al encontrar la ubicación de emacs (digamos \emph{/usr/local/bin/emacs}), el shell crea un alias rastreado:
\begin{lstlisting}[language=bash]
alias -t emacs=/usr/local/bin/emacs # Alias de seguimiento automatico
\end{lstlisting}
La próxima vez que ejecute emacs, el intérprete de comandos expande el alias emacs en la ruta completa /usr/local/bin/emacs, y ejecuta el programa directamente, sin molestarse con una búsqueda PATH.
También puede definir alias rastreados individuales usted mismo, con la opción \emph{-t} del comando alias, y puede listar todos esos alias rastreados escribiendo \texttt{alias -t} por sí mismo. (Por compatibilidad con el shell Bourne del Sistema V, ksh predefine el alias \texttt{hash='alias -t --'}; el comando hash en ese shell muestra la tabla interna de comandos encontrados. El mecanismo de alias rastreado del shell Korn es más flexible).
Aunque la documentación del shell y la opción trackall indican que puede activar y desactivar el rastreo de alias, el comportamiento real del shell es diferente: el rastreo de alias siempre está activado. \emph{alias -t} lista todos los alias rastreados creados automáticamente. Sin embargo, \emph{alias -p} no imprime los alias rastreados. Esto se debe a que, conceptualmente, los alias rastreados son sólo una mejora del rendimiento; en realidad no están relacionados con los alias que se definen para la personalización.
\subsubsection{Ruta de Búsqueda de Directorios}
\texttt{CDPATH} es una variable cuyo valor, como el de \texttt{PATH}, es una lista de directorios separados por dos puntos. Su propósito es aumentar la funcionalidad del comando incorporado \emph{cd}.
Por defecto, \texttt{CDPATH} no está definido (es decir, es nulo), y cuando se escribe \emph{cd dirname}, el shell busca en el directorio actual un subdirectorio llamado \emph{dirname}. De forma similar a \texttt{PATH}, esta búsqueda se desactiva cuando \emph{dirname} comienza con una barra. Si establece \texttt{CDPATH}, le da al shell una lista de lugares donde buscar \emph{dirname}; la lista puede o no incluir el directorio actual.
He aquí un ejemplo. Considere el alias para el comando \emph{cd} largo de antes en este capítulo:
\begin{lstlisting}[language=bash]
alias cdcm="cd work/projects/devtools/windows/confman"
\end{lstlisting}
Ahora supongamos que hay unos cuantos directorios bajo este directorio a los que necesitas ir a menudo; se llaman \emph{src}, \emph{bin} y \emph{doc}. Usted define su \texttt{CDPATH} así:
\begin{lstlisting}[language=bash]
CDPATH=:~/work/projects/devtools/windows/confman
\end{lstlisting}
En otras palabras, defina su \texttt{CDPATH} como la cadena vacía (es decir, el directorio actual, dondequiera que se encuentre) seguida de \emph{\~{}/work/projects/devtools/windows/confman}.
Con esta configuración, si escribes \texttt{cd doc}, entonces el shell busca en el directorio actual un (sub)directorio llamado \emph{doc}. Asumiendo que no encuentra ninguno, busca en el directorio \emph{~{}/work/projects/devtools/windows/confman}. El shell encuentra el directorio doc allí, así que vas directamente a él.
Esto funciona para cualquier ruta relativa. Por ejemplo, si tienes un directorio \emph{src/whizprog} en tu directorio personal, y tu \texttt{CDPATH} es \texttt{:\$HOME} (el directorio actual y tu directorio personal), escribiendo cd \emph{src/whizprog} te lleva a \emph{\$HOME/src/whizprog} desde cualquier parte del sistema.
Esta característica le da otra forma de ahorrar tecleando cuando necesita \emph{cd} a menudo a directorios que están enterrados profundamente en su jerarquía de archivos. Usted puede encontrarse yendo a un grupo específico de directorios a menudo mientras trabaja en un proyecto en particular, y luego cambiando a otro grupo de directorios cuando cambia a otro proyecto. Esto implica que la característica \texttt{CDPATH} sólo es útil si la actualiza cada vez que cambien sus hábitos de trabajo; si no lo hace, puede encontrarse ocasionalmente donde no quiere estar.
\subsubsection{Variables Misceláneas}
Hemos cubierto las variables del shell que son importantes desde el punto de vista de la personalización. También hay varias que sirven como indicadores de estado y para otros propósitos diversos. Sus significados son relativamente sencillos; los más básicos se resumen en la Tabla \ref{tab3.4}.
Las primeras dos variables son establecidas por el programa de inicio de sesión, antes de que se inicie el shell. El shell establece el valor de las dos siguientes cada vez que usted cambia de directorio. El valor de la última variable cambia dinámicamente, a medida que transcurre el tiempo. Aunque también puede establecer los valores de cualquiera de estas variables, como cualquier otra, es difícil imaginar una situación en la que desee hacerlo.
\begin{table}[h]
\center
\caption{Variables de estado}
\label{tab3.4}
\begin{tabular}{|m{2cm}|m{13cm}|} \hline
\textbf{Variable} & \textbf{Significado} \\ \hline
\texttt{HOME} & Nombre de tu directorio de inicio (login). Este es el argumento por defecto para el comando \texttt{cd}. \\\hline
\texttt{SHELL} & Nombre de ruta del shell que los programas deben utilizar para ejecutar comandos. \\\hline
\texttt{PWD} & Directorio actual. \\\hline
\texttt{OLDPWD} & Directorio anterior al último comando \texttt{cd}. \\\hline
\texttt{SECONDS} & Número de segundos transcurridos desde que se invocó al shell. \\\hline
\end{tabular}
\end{table}
\section{Personalización y Subprocesos}
Algunas de las variables discutidas anteriormente son utilizadas por los comandos que puede ejecutar, a diferencia del propio shell, para que puedan determinar ciertos aspectos de su entorno. La mayoría, sin embargo, ni siquiera se conocen fuera del caparazón.
Esta dicotomía plantea una pregunta importante: ¿qué <<cosas>> del caparazón se conocen fuera del caparazón y cuáles son solo internas? Esta pregunta está en el centro de muchos malentendidos sobre el shell y la programación del shell. Antes de responder, lo preguntaremos nuevamente de una manera más precisa: ¿qué <<cosas>> de shell son conocidas por los subprocesos? Recuerde que cada vez que ingresa un comando, le está diciendo al shell que ejecute ese comando en un subproceso; además, algunos programas complejos pueden iniciar sus propios subprocesos.
La respuesta es bastante simple. Los subprocesos heredan solo variables de entorno. Están disponibles automáticamente, sin que el subproceso tenga que realizar ninguna acción explícita. Todas las demás <<cosas>> (opciones de shell, alias y funciones) deben estar explícitamente disponibles. El archivo de entorno es cómo se hace esto. Además, solo los shells interactivos procesan el archivo de entorno. Las siguientes dos secciones describen las variables de entorno y el archivo de entorno, respectivamente.
\subsection{Variables de Entorno}
Por defecto, sólo una clase de cosas es conocida por todas las clases de subprocesos: una clase especial de variables del shell llamadas variables de entorno. Algunas de las variables incorporadas que hemos visto son en realidad variables de entorno: HISTFILE, HOME, LOGNAME, PATH, PWD, OLDPWD, SHELL y TERM.
Debería estar claro por qué estas y otras variables necesitan ser conocidas por los subprocesos. Ya hemos visto el ejemplo más obvio: los editores de texto como vi y Emacs necesitan saber qué tipo de terminal estás usando; \texttt{TERM} es su forma de determinarlo. Como otro ejemplo, la mayoría de los programas de correo de Unix le permiten editar un mensaje con su editor de texto favorito. ¿Cómo sabe \emph{mail} qué editor usar? El valor de \texttt{EDITOR} (o a veces \texttt{VISUAL}).
Cualquier variable puede convertirse en una variable de entorno, y se pueden crear nuevas variables que sean variables de entorno. Las variables de entorno se crean con el comando:
\begin{lstlisting}[language=bash]
export varnames
\end{lstlisting}
(\emph{varnames} puede ser una lista de nombres de variables separados por espacios en blanco.) Si los nombres en \emph{varnames} ya existen, entonces esas variables se convierten en variables de entorno. Si no existen, el shell crea nuevas variables que son variables de entorno.
Con \emph{ksh}, puede asignar un valor y exportar la variable en un solo paso:
\begin{lstlisting}[language=bash]
export TMPDIR=/var/tmp
\end{lstlisting}
También puedes definir variables para que estén sólo en el entorno de un subproceso (comando) en particular, precediendo al comando con la asignación de la variable, de la siguiente manera:
\begin{lstlisting}[language=bash]
varname=value command
\end{lstlisting}
Puedes poner tantas asignaciones antes del comando como quieras.\footnote{Existe una oscura opción, \emph{keyword}, que (si está activada) te permite poner este tipo de definición de variable de entorno en cualquier parte de la línea de comandos, no sólo al principio.} Por ejemplo, supongamos que está utilizando el editor Emacs. Está teniendo problemas para que funcione con su terminal, así que está experimentando con diferentes valores de TERM. Usted puede hacer esto más fácilmente mediante la introducción de comandos que se parecen:
\begin{lstlisting}[language=bash]
TERM=trythisone emacs filename
\end{lstlisting}
emacs tiene \emph{trythisone} definido como su valor de \texttt{TERM}, sin embargo la variable de entorno en su shell mantiene cualquier valor (si lo hubiera) que tuviera antes. Esta sintaxis no es muy usada, así que no la veremos muy a menudo en el resto de este libro.
No obstante, las variables de entorno son importantes. La mayoría de los archivos \texttt{.profile} incluyen definiciones de variables de entorno; el \emph{.profile} de ejemplo anterior en este capítulo contenía dos de estas definiciones:
\begin{lstlisting}[language=bash]
EDITOR=/usr/local/bin/emacs
SHELL=/bin/ksh
export EDITOR SHELL
\end{lstlisting}
Por alguna razón, el shell Korn no hace de \texttt{EDITOR} una variable de entorno por defecto. Esto significa, entre otras cosas, que mail no sabrá qué editor usar cuando quieras editar un mensaje.\footnote{En realidad, por defecto será el editor de líneas ed. No quieres eso, ¿verdad?} Por lo tanto, tendría que exportarlo usted mismo utilizando el comando \emph{export} en su \emph{.profile}.
La segunda línea del código anterior está pensada para sistemas que no tienen el shell Korn instalado como shell por defecto, es decir, como \emph{/bin/sh}. Algunos programas ejecutan shells como subprocesos dentro de sí mismos (por ejemplo, muchos programas de correo y el modo shell del editor Emacs); por convención, utilizan la variable \texttt{SHELL} para determinar qué shell utilizar.
Puede averiguar qué variables son de entorno y cuáles son sus valores escribiendo \emph{export} sin argumentos.
\subsection{El Archivo de Entorno}
Aunque las variables de entorno siempre son conocidas por los subprocesos, hay que indicar explícitamente al shell qué otras variables, opciones, alias, etc., deben comunicarse a los subprocesos. La forma de hacerlo es poner todas esas definiciones en un archivo especial llamado \emph{archivo de entorno} en lugar de su \emph{.profile}.
Puede llamar al archivo de entorno como desee, siempre que establezca la variable de entorno \texttt{ENV} con el nombre del archivo. La forma habitual de hacerlo es la siguiente:
\begin{enumerate}
\item{Decide qué definiciones de tu \emph{.profile} quieres propagar a los subprocesos. Elimínelas de \emph{.profile} y colóquelas en un archivo que designe como su archivo de entorno.}
\item{Pon una línea en tu \emph{.profile} que le diga al shell dónde está tu archivo de entorno:
\begin{lstlisting}[language=bash]
ENV=envfilename
export ENV
\end{lstlisting}
Es importante que el valor de \texttt{ENV} sea exportado, para que los subprocesos del shell puedan encontrarlo.}
\item{Para que los cambios surtan efecto inmediatamente, cierre la sesión y vuelva a iniciarla.\footnote{Esto asume que el shell Korn está definido como su shell de inicio de sesión. Si no es así, debe hacer que el administrador del sistema lo instale como shell de inicio de sesión.} (No puede utilizar simplemente \texttt{. \~{}/.profile}; el shell no vuelve a ejecutar el archivo \texttt{\$ENV} cuando cambia el valor de \texttt{ENV}).}
\end{enumerate}
La idea del fichero de entorno viene del fichero \emph{.cshrc} del shell C; por lo tanto, muchos usuarios del shell Korn que vinieron del mundo del shell C llaman a sus ficheros de entorno \emph{.kshrc}. (El sufijo rc para los ficheros de inicialización es prácticamente universal en todo el mundo Unix. Significa <<ejecutar comandos>> y entró en el léxico de Unix a través del Sistema Compatible de Tiempo Compartido (CTSS) del MIT).
Como regla general, debería poner tan pocas definiciones como sea posible en \emph{.profile} y tantas como sea posible en su fichero de entorno. Debido a que las definiciones agregan a un entorno en lugar de quitarlo, hay pocas posibilidades de que causen que algo en un subproceso no funcione correctamente. (Una excepción podrían ser los choques de nombres si te pasas con los alias).
Las únicas cosas que realmente necesitan estar en \emph{.profile} son los comandos que no son definiciones sino que realmente se ejecutan o producen salida cuando te conectas. Las definiciones de opciones y alias deberían ir en el fichero de entorno. De hecho, hay muchos usuarios del shell Korn que tienen archivos \emph{.profile} diminutos, por ejemplo:
\begin{lstlisting}[language=bash]
stty stop ^S intr ^C erase ^?
date
from
export ENV=~/.kshrc
\end{lstlisting}
(El comando from, en algunas versiones de Unix, comprueba si tiene correo e imprime una lista de las cabeceras de los mensajes en caso afirmativo). Aunque este es un pequeño \emph{.profile}, el fichero de entorno de este usuario podría ser enorme.
Hay una diferencia importante entre \emph{ksh88} y \emph{ksh93}. En \emph{ksh88}, el fichero de entorno se ejecuta siempre. En \emph{ksh93}, sólo los shells interactivos (aquellos que no leen desde un script, sino desde un terminal) ejecutan el fichero de entorno. Por lo tanto, es mejor que el archivo de entorno contenga sólo comandos que sean útiles para el uso interactivo, como la configuración de alias y opciones.
Otra diferencia entre las dos versiones del shell es que \emph{ksh88} sólo hace sustitución de variables en el valor de ENV, mientras que \emph{ksh93} hace sustitución de variables, comandos y aritmética en su valor. (La sustitución de comandos se describe en el \hyperref[sec:Chapter4]{Capítulo 4}. La sustitución aritmética se describe en el \hyperref[sec:Chapter5]{Capítulo 5}.) La sustitución aritmética se describe en el \hyperref[sec:Chapter6]{Capítulo 6}).
\section{Sugerencias de Personalización}
No dudes en probar cualquiera de las técnicas presentadas en este capítulo. La mejor estrategia es probar algo escribiéndolo en el intérprete de comandos durante su sesión de inicio de sesión; si decide que quiere convertirlo en una parte permanente de su entorno, añádalo a su \emph{.profile}.
Una forma agradable e indolora de añadir a su \emph{.profile} sin entrar en un editor de texto hace uso del comando \emph{print} y uno de los modos de edición del shell Korn. Si escribe un comando de personalización y más tarde decide añadirlo a su \emph{.profile}, puede recuperarlo mediante CTRL-P o CTRL-R (en modo emacs) o j, -, o / (modo vi). Digamos que la línea es:
\begin{lstlisting}[language=bash]
PS1="($LOGNAME !)->"
\end{lstlisting}
Después de recuperarla, edítala para que esté precedida por un comando de \emph{print}, rodeada por comillas simples, y seguida por una redirección de E/S que (como verás en el \hyperref[sec:Chapter7]{Capítulo 7}) anexa la salida a \emph{\~{}/.profile}:
\begin{lstlisting}[language=bash]
print 'PS1="($LOGNAME !)->"' >> ~/.profile
\end{lstlisting}
Recuerde que las comillas simples son importantes porque evitan que el shell intente interpretar cosas como signos de dólar, comillas dobles y signos de exclamación.
También deberías sentirte libre de husmear en los archivos \emph{.profile} de otras personas para obtener ideas de personalización. Una forma rápida de examinar el \emph{.profile} de todo el mundo es la siguiente: supongamos que todos los directorios de inicio de sesión están en \emph{/home}. Entonces puedes escribir:
\begin{lstlisting}[language=bash]
cat /home/*/.profile > ~/other\.profiles
\end{lstlisting}
y examinar los archivos \emph{.profile} de otras personas con un editor de texto a tu antojo (suponiendo que tengas permiso de lectura sobre ellos). Si otros usuarios tienen archivos de entorno, el archivo que acabas de crear mostrará cuáles son y también podrás examinarlos.
Por último, asegúrate de que nadie más que tú tiene permisos de escritura en tus archivos \emph{.profile} y de entorno.