Aprendiendo Korn Shell: Primera versión

This commit is contained in:
Tuxliban Torvalds 2023-12-27 08:49:42 -06:00
parent f5c57a215a
commit 525a076656
31 changed files with 12610 additions and 0 deletions

BIN
Aprendiendo_Korn_Shell.pdf Normal file

Binary file not shown.

158
Aprendiendo_Korn_Shell.tex Normal file
View File

@ -0,0 +1,158 @@
\documentclass[12pt,a4paper]{report}
\usepackage[
spanish,
mexico,
es-lcroman % Poder usar numeración romana en minúscula
]{babel}
\usepackage[utf8]{luainputenc} % Tildes y caracteres especiales con lualatex
\usepackage{eso-pic} % Insertar imagen de fondo para portada
\usepackage{graphicx}
\graphicspath{{./Imágenes}}
\usepackage{parskip} % Espacio entre párrafos en lugar de sangría
\usepackage{setspace} % Permite definir el interlineado
\usepackage{array} % Ajuste de columnas en tablas
\usepackage{xcolor}
\newcommand{\highlight}[1]{\colorbox{black}{\textcolor{white}{#1}}} % Utilizado para resaltar
\usepackage[hidelinks]{hyperref}
\hypersetup{
pdfauthor={Omar Sánchez Enríquez},
pdftitle={Aprendiendo Korn Shell},
pdfkeywords={korn, shell, built-in, ksh}
pdfproducer={Latex con hyperref en Void Linux},
pdfcreator={Texlive with Lualatex},
colorlinks=true,
filecolor=magenta,
linkcolor=blue,
urlcolor=green
}
\usepackage{listings} % Escribir bloques de código
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\lstdefinestyle{mystyle}{
backgroundcolor=\color{backcolour},
commentstyle=\color{codegreen}, % estilo de comentario
keywordstyle=\color{blue},
stringstyle=\color{codepurple},
basicstyle=\ttfamily\scriptsize, % el tamaño de las fuentes utilizadas para el código
breakatwhitespace=false,
breaklines=true, % establece el corte de línea automático
captionpos=b, % Establece la posición del título en la parte inferior
keepspaces=true,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
keepspaces=true,
tabsize=2
}
\lstset{style=mystyle}
\usepackage{tablefootnote}
\usepackage{multirow}
\usepackage{longtable}
\usepackage{tcolorbox} % Crear cuadros de texto
% Definir un nuevo entorno tcolorbox
\newtcolorbox{mybox}[1][]{colback=blue!5!white, colframe=blue!75!black, fonttitle=\bfseries, title=#1}
\usepackage{amssymb, amsmath}
\urlstyle{same} % Misma fuente para los vínculos
%%%%% Márgenes %%%%%
\usepackage[
top=2cm, % Margen superior
bottom=1.5cm, % Margen inferior
right=2.4cm, % Margen derecho
left=2.4cm % Margen izquierdo
]{geometry}
%%%%% Cambiar de tamaño y centrar capítulos %%%%%
\usepackage[explicit]{titlesec}
% Configuración para los capítulos
\titleformat{\chapter}[display]{\bfseries\centering}{\Large CAPÍTULO \thechapter}{1em}{\large #1}
%%%%% Encabezado y pie de página %%%%%
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhf{} % Borrar los valores que vienen por defecto
\rhead[]{\fontsize{9}{12} \selectfont{\rightmark}}
\renewcommand{\footrulewidth}{0.4pt} % Añadir linea horizontal a pie de página
\lfoot[\thepage]{\fontsize{9}{12} \selectfont{Aprendiendo Korn Shell}}
\rfoot[]{\fontsize{9}{12} \selectfont{\thepage}}
%%%%% Portada %%%%%
\title {
\input{./Secciones/Portada}
}
%%%%% Contenido del documento %%%%%
\begin{document}
\spacing{1.5} % Interlineado 1.5
% % % % % Estructura % % % % %
\input{./Secciones/Portada}
\chapter*{COPYRIGHT}
\thispagestyle{empty}
\input{./Secciones/Copyright}
\chapter*{DEDICATORIA}
\thispagestyle{empty}
\input{./Secciones/Dedicatoria}
\chapter*{PREFACIO}\label{sec:Prefacio}
\thispagestyle{empty}
\input{./Secciones/Prefacio}
% Índices
\pagenumbering{roman} % Iniciar numeración desde 1 en números romanos minúscula
\renewcommand\contentsname{\centering Contenido} % Renombrar y centrar nombre de índices
\setcounter{page}{0}
\tableofcontents
\listoffigures
\listoftables
\chapter{CONCEPTOS BÁSICOS DE KORN SHELL}\label{sec:Chapter1}
\pagenumbering{arabic} % Iniciar numeración desde 1 en números arábigos
\input{./Secciones/Capitulo1}
\chapter{EDICIÓN DESDE LA LÍNEA DE COMANDOS}\label{sec:Chapter2}
\input{./Secciones/Capitulo2}
\chapter{PERSONALIZACIÓN DEL ENTORNO}\label{sec:Chapter3}
\input{./Secciones/Capitulo3}
\chapter{PROGRAMACIÓN BÁSICA DE SHELL}\label{sec:Chapter4}
\input{./Secciones/Capitulo4}
\chapter{CONTROL DE FLUJO}\label{sec:Chapter5}
\input{./Secciones/Capitulo5}
\chapter{OPCIONES DE LA LÍNEA DE COMANDOS Y VARIABLES TIPOGRÁFICAS}\label{sec:Chapter6}
\input{./Secciones/Capitulo6}
\chapter{ENTRADA/SALIDA Y PROCESAMIENTO DESDE LA LÍNEA DE COMANDOS}\label{sec:Chapter7}
\input{./Secciones/Capitulo7}
\chapter{MANEJO DE PROCESOS}\label{sec:Chapter8}
\input{./Secciones/Capitulo8}
\chapter{DEPURACIÓN DE PROGRAMAS DEL SHELL}\label{sec:Chapter9}
\input{./Secciones/Capitulo9}
\chapter{ADMINISTRACIÓN DE KORN SHELL}\label{sec:Chapter10}
\input{./Secciones/Capitulo10}
\appendix
\chapter{SHELLS RELACIONADOS}\label{sec:ApendiceA}
\input{./Secciones/ApendiceA}
\chapter{INFORMACIÓN DE REFERENCIA}\label{sec:ApendiceB}
\input{./Secciones/ApendiceB}
\chapter{CONSTRUCCIÓN DE KSH A PARTIR DEL CÓDIGO FUENTE}
\input{./Secciones/ApendiceC}\label{sec:ApendiceC}
\chapter{ACUERDO DE LICENCIA DE CÓDIGO FUENTE DE AT\&T ast}\label{sec:ApendiceD}
\input{./Secciones/ApendiceD}
\end{document}

BIN
Imagenes/Portada.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
Imagenes/fig_01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
Imagenes/fig_02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
Imagenes/fig_03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
Imagenes/fig_04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
Imagenes/fig_05.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
Imagenes/fig_06.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
Imagenes/fig_07.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
Imagenes/fig_08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
Imagenes/fig_09.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
Imagenes/fig_10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

860
Secciones/ApendiceA.tex Normal file
View File

@ -0,0 +1,860 @@
La fragmentación del mercado de Unix ha tenido sus ventajas y desventajas. Las ventajas vinieron principalmente en los primeros días: la falta de estandarización y la proliferación entre académicos y profesionales técnicamente competentes contribuyeron a un <<mercado libre>> saludable para el software de Unix, en el que varios programas del mismo tipo (por ejemplo, shells, editores de texto, herramientas de administración del sistema) a menudo competían por popularidad. Los mejores programas generalmente se volvían más ampliamente utilizados, mientras que el software inferior tendía a desaparecer.
Pero a menudo no había un único programa <<mejor>> en una categoría dada, por lo que varios prevalecían. Esto llevó a la situación actual, donde la multiplicidad de software similar ha llevado a la confusión, la falta de compatibilidad y, lo más desafortunado de todo, a la incapacidad de Unix para capturar una parte tan grande del mercado como otras plataformas operativas. En particular, Unix se ha relegado a su posición actual como un sistema operativo muy popular para servidores, pero es una rareza en las computadoras de escritorio.
La categoría <<shell>> probablemente ha sufrido de esta manera más que cualquier otro tipo de software. Como dijimos en el \hyperref[sec:Prefacio]{Prefacio} y el \hyperref[sec:Chapter1]{Capítulo 1}, una de las fortalezas de Unix es que el shell es reemplazable y, por lo tanto, actualmente hay varios shells disponibles; las diferencias entre ellos a menudo no son tan grandes. Creemos que el shell Korn es uno de los mejores entre los shells más utilizados, pero otros shells ciertamente tienen sus seguidores leales, por lo que no es probable que caigan en el olvido. De hecho, parece que los shells, compatibles con Bourne o no, continúan proliferando.
Por lo tanto, sentimos que era necesario incluir información sobre shells similares al shell Korn. Este apéndice resume las diferencias entre el shell Korn y los siguientes shells:
\begin{itemize}
\item El Shell de Bourne de System V Release 4, como una especie de referencia.
\item La versión de 1988 del shell Korn.
\item El Estándar del shell IEEE POSIX 1003.2, al que se adhieren el shell Korn y otros shells.
\item El shell Desktop Korn (\emph{dtksh}), un shell Korn con mejoras para programación en el sistema X Window, como parte del Common Desktop Environment (CDE).
\item El shell \emph{tksh}, una interesante combinación de \emph{ksh93} con Tcl/Tk.
\item \emph{pdksh}, una versión de dominio público ampliamente utilizada del shell Korn.
\item El shell \emph{bash}, otro shell mejorado de Bourne con algunas características del shell C y Korn.
\item El shell Z, \emph{zsh}, otro shell mejorado de Bourne con algunas características de los shells C y Korn y muchas, muchas más características propias en plataformas de PC de escritorio.
\item Similares al shell Korn en plataformas de PC de sobremesa
\end{itemize}
\section{El Shell de Bourne}
El shell Korn es casi completamente compatible hacia atrás con el Shell de Bourne. La única característica significativa de este último que el shell Korn no admite es \^{} (acento circunflejo) como sinónimo del carácter pipe (|).\footnote{También hay algunas diferencias en cómo reaccionan los dos shells ante ciertas entradas extremadamente patológicas. Por lo general, el shell Korn procesa correctamente lo que hace que el Shell de Bourne <<se atragante>>.}
Esta es una característica arcaica que el Shell de Bourne incluye por su propia compatibilidad hacia atrás con shells anteriores. Ninguna versión moderna de Unix tiene código de shell que use \^{} como pipe.
Para describir las diferencias entre el Shell de Bourne y el Korn shell, revisaremos cada capítulo de este libro y enumeraremos las características discutidas en el capítulo que el Shell de Bourne no admite.
\begin{enumerate}
\item \hyperref[sec:Chapter1]{Capítulo 1}
\begin{itemize}
\item Las formas \texttt{cd old new} y \texttt{cd -} del comando \emph{cd}.
\item Expansión de tilde (\texttt{\~{}}).
\item El Shell de Bourne siempre sigue el diseño físico de archivos, lo que afecta lo que sucede cuando haces \texttt{cd ..} fuera de algún lugar que era un enlace simbólico.
\item Los comandos incorporados no tienen ayuda en línea.
\item Algunas versiones antiguas del Shell de Bourne no admiten el comando \emph{jobs} y el control de trabajos, o pueden requerir ser invocadas como \emph{jsh} para habilitar las características de control de trabajos.
\end{itemize}
\item \hyperref[sec:Chapter2]{Capítulo 2}
\begin{itemize}
\item Todos (es decir, el Shell de Bourne no admite ninguna de las características de historial y edición discutidas en el \hyperref[sec:Chapter2]{Capítulo 2}).
\end{itemize}
\item \hyperref[sec:Chapter3]{Capítulo 3}
\begin{enumerate}
\item No se admiten alias.
\item Las opciones \texttt{set -o} no funcionan. El Shell de Bourne admite las abreviaturas enumeradas en la tabla de <<Opciones>> en el \hyperref[sec:ApendiceB]{Apéndice B}, excepto \emph{-A, -b, -C, -m, -p} y \emph{-s}.
\item No se admiten archivos de entorno; tampoco el comando \emph{print} (usa echo en su lugar). Las siguientes variables internas no se admiten:
\begin{table}[h]
\center
\begin{tabular}{|m{2.5cm}|m{2.5cm}|m{4cm}|m{2.5cm}|m{2.5cm}|} \\
\hline
.sh.edchar & .sh.version & HISTEDIT & LINENO & PS4 \\\hline
.sh.edcol & COLUMNS & HISTFILE & LINES & PWD \\\hline
.sh.edmode & EDITOR & HISTSIZE & OLDPWD & PWD \\\hline
.sh.edtext & ENV & LANG & OPTARG & RANDOM \\\hline
.sh.match & FCEDIT & LC\_ALL & OPTIND & SECONDS \\\hline
.sh.name & FIGNORE & LC\_COLLATE & PPID & TMOUT \\\hline
.sh.subscript & FPATH & LC\_CTYPE & PS3 & VISUAL \\\hline
.sh.value & HISTCMD & LC\_NUMERIC & & \\\hline
\end{tabular}
\end{table}
\item Algunas de estas variables (por ejemplo, \texttt{EDITOR} y \texttt{VISUAL}) aún son utilizadas por otros programas, como \emph{mail} y noticias.
\end{enumerate}
\item \hyperref[sec:Chapter4]{Capítulo 4}
\begin{itemize}
\item No están disponibles los nombres de variables extendidas (aquellas con un punto en ellas), así como la asignación de variables compuestas y la concatenación de cadenas con el operador +=.
\item No se admiten variables indirectas (referencias de nombres).
\item No está disponible el comando \emph{whence}; usa type en su lugar.
\item Los operadores variables de coincidencia de patrones (\%, \%\%, \#, \#\#, etc.) y los caracteres comodín avanzados (expresión regular) no están disponibles; usa el comando externo \emph{expr} en su lugar.
\item No se admiten funciones cargadas automáticamente, y solo se pueden usar funciones de estilo POSIX (definidas utilizando la sintaxis \emph{name()}).
\item La sintaxis de sustitución de comandos es diferente: utilice el antiguo \texttt{`command`} en lugar de \texttt{\$(command)}. (Algunos proveedores han mejorado sus shells Bourne para que admitan la notación \texttt{\$(comando)}, ya que está definida por el estándar POSIX).
\end{itemize}
\item \hyperref[sec:Chapter5]{Capítulo 5}
\begin{itemize}
\item \texttt{return} solo se puede usar desde dentro de una función.
\item Las pruebas condicionales utilizan la sintaxis antigua: \texttt{[ condición ]} o \texttt{test condición} en lugar de \texttt{[[ condición ]]}. Estas son realmente dos formas del mismo comando (consulta la página de manual de \emph{test(1)}).
\item Los operadores lógicos \&\& y || son \texttt{-a} y \texttt{-o} en su lugar. Los operadores de prueba admitidos difieren de un sistema a otro.\footnote{En la Bourne Shell original de la Versión 7 (y en sistemas Unix de Berkeley hasta 4.3BSD), \emph{test} y \texttt{[ condición ]} eran en realidad comandos externos. (Eran enlaces duros entre sí en \emph{/bin}.) Sin embargo, se integraron en el Shell de Bourne en todos los sistemas desde System III (circa 1981).}
\item La palabra clave ! para invertir el estado de salida de un comando no estaba en el Shell de Bourne de SVR4, aunque puede estar disponible en tu sistema, ya que es requerida por POSIX.
\item No se admite la construcción \texttt{select}. Tampoco el bucle \texttt{for} aritmético, y no hay forma de pasar de un caso a otro dentro de una declaración \texttt{case}.
\item No hay un equivalente para \texttt{TMOUT}.
\end{itemize}
\item \hyperref[sec:Chapter6]{Capítulo 6}
\begin{itemize}
\item El comando getopts del Shell de Bourne de SVR4 es similar, pero no idéntico, al de \emph{ksh}. No permite opciones que comienzan con un signo más, ni ninguna de las características avanzadas descritas en el \hyperref[sec:ApendiceB]{Apéndice B}.
\item No se admite la aritmética: usa el comando externo \emph{expr} en lugar de la sintaxis \texttt{\$((...))}. Para condicionales numéricas, usa la antigua sintaxis de prueba de condición y los operadores relacionales \texttt{-lt, -eq}, etc., en lugar de \texttt{((...))}. No se admite \emph{let}.
\item No se admiten variables de matriz y el comando \emph{typeset}.
\end{itemize}
\item \hyperref[sec:Chapter7]{Capítulo 7}
\begin{itemize}
\item No se admiten los siguientes redireccionadores de E/S\footnote{El operador \texttt{<>} estaba en el Shell de Bourne original de la Versión 7, pero no estaba documentado, ya que no siempre funcionaba correctamente en todos los sistemas Unix. No se debe depender de su disponibilidad para la programación de Shell de Bourne.}:
\begin{table}[h]
\center
\begin{tabular}{|m{1cm}|m{1cm}|m{1cm}|m{1cm}|m{1cm}|m{1cm}|m{1cm}|m{1cm}|} \\\hline
\texttt{>|} & \texttt{<>} & \texttt{<<<} & \texttt{<\&p} & \texttt{>\&p} & \texttt{<\&n-} & \texttt{>\&n-} & \texttt{|\&} \\\hline
\end{tabular}
\end{table}
\item No se admite \emph{print} (usa \emph{echo} en su lugar). \emph{printf} generalmente está disponible como un comando externo.
\item Ninguna de las opciones de \emph{read} es compatible, ni la capacidad de proporcionar un indicador con el nombre de la primera variable.
\item El Shell de Bourne no hace interpretaciones especiales de rutas, como \texttt{/dev/fd/2} o \texttt{/dev/tcp/ftp.oreilly.com/ftp}.
\item Los mecanismos de cotización \texttt{\$"..."} y \texttt{\$'...'} no están disponibles.
\end{itemize}
\item \hyperref[sec:Chapter8]{Capítulo 8}
\begin{itemize}
\item Se admite el control de trabajos, pero solo si el shell se invoca con la opción \texttt{-j} o como \emph{jsh}.
\item La opción \texttt{-} a trap (restablecer trap al valor predeterminado para esa señal) no está disponible. En su lugar, la falta de un trap indica que se deben restablecer las trampas suministradas. \emph{trap} acepta solo números de señales, no nombres lógicos.
\item No se admiten las corutinas.
\item El comando \emph{wait} solo acepta identificadores de procesos.
\end{itemize}
\item \hyperref[sec:Chapter9]{Capítulo 9}
\begin{itemize}
\item Las señales ERR y DEBUG no están disponibles. La señal EXIT es compatible, como la señal 0.
\item \texttt{set -n} tiene efecto incluso en shells interactivos.
\item La salida de la traza con \texttt{set -x} no se puede personalizar (no hay variable \texttt{PS4}).
\item Las funciones de disciplina no están disponibles.
\end{itemize}
\item \hyperref[sec:Chapter10]{Capítulo 10}
\begin{itemize}
\item No se admite el modo privilegiado (algunos shells Bourne lo tienen).
\item Los comandos \emph{ulimit} y \emph{umask} no son tan capaces.
\item No está disponible la trampa \texttt{KEYBD}.
\end{itemize}
\end{enumerate}
\section{El Shell Korn de 1988}
Quizás el shell más evidente con el que comparar \emph{ksh93} sea \emph{ksh88}, la versión de 1988 del shell Korn. Esta sección describe brevemente aquellas características de \emph{ksh93} que son diferentes o inexistentes en \emph{ksh88}. Al igual que con la presentación para el shell Bourne, los temas se cubren en el mismo orden en que se presentan en el resto del libro.
\begin{enumerate}
\item \hyperref[sec:Chapter1]{Capítulo 1}
\begin{itemize}
\item Las facilidades de ayuda integradas (como \emph{-?}, \emph{--man}, y así sucesivamente) no están disponibles.
\item La sustitución de tilde no ocurre durante la expansión de variables (\texttt{\$\{var op word\}}).
\end{itemize}
\item \hyperref[sec:Chapter2]{Capítulo 2}
\begin{itemize}
\item CTRL-T funciona de manera diferente en el modo de edición emacs. En \emph{ksh88}, transpone los dos caracteres a la derecha del punto e mueve el punto hacia adelante en un carácter.
\item En el modo emacs, ESC ESC, ESC *, y ESC = no funcionan en la primera palabra de una línea de comando, es decir, no hay una facilidad de autocompletado de comandos. El comando ESC \# siempre antepone un carácter \#. Nunca los elimina.
\item De manera similar, en el modo \emph{vi}, los comandos *, \textbackslash{} y = no funcionan en la primera palabra de una línea de comando, y el comando \# siempre antepone un carácter \#. Nunca los elimina.
\item Finalmente, no hay autocompletado de variables, el comando \emph{hist} se llama \emph{fc}, \texttt{FCEDIT} se usa en lugar de \texttt{HISTEDIT}, y la variable \texttt{HISTCMD} no está disponible.
\end{itemize}
\item \hyperref[sec:Chapter3]{Capítulo 3}
\begin{itemize}
\item El archivo \texttt{ENV} se carga para todos los shells, y \emph{ksh88} solo realiza la sustitución de variables en el valor de \texttt{\$ENV}.
\item El seguimiento de alias se puede desactivar en \emph{ksh88}.
\item La opción \texttt{-p} para alias no está presente en \emph{ksh88}, al igual que la opción \texttt{-a} para unalias.
\item Las siguientes variables internas no son compatibles:
\begin{table}[h]
\center
\begin{tabular}{|m{2cm}|m{2cm}|m{2cm}|m{2cm}|} \\\hline
.sh.edchar & .sh.match & .sh.version & LANG \\\hline
.sh.edcol & .sh.name & FIGNORE & LC\_ALL \\\hline
.sh.edmode & .sh.subscript & HISTCMD & LC\_COLLATE \\\hline
.sh.edtext & .sh.value & HISTEDIT & LC\_CTYPE \\\hline
\end{tabular}
\end{table}
\end{itemize}
\item \hyperref[sec:Chapter4]{Capítulo 4}
\begin{itemize}
\item En \emph{ksh88}, ambas sintaxis para definir funciones producen funciones con semántica de shell Korn. No puedes aplicar el comando dot a un nombre de función.
\item El orden de búsqueda de comandos en \emph{ksh88} es palabras clave, aliases, todos los comandos internos, funciones, y luego comandos externos y scripts. El orden fue cambiado en \emph{ksh93} para cumplir con POSIX.
\item En \emph{ksh88}, las funciones no definidas (\emph{autoloaded}) se buscan exclusivamente a lo largo de la lista en la variable \texttt{FPATH}, y \texttt{PATH} no está involucrado.
\item La característica del archivo \emph{.paths} no está disponible.
\item Muchas de las características de sustitución de variables descritas en el texto principal son nuevas en \emph{ksh93}. Solo las siguientes están disponibles en \emph{ksh88}: \texttt{\$\{name:-string\}, \$\{name:=string\}, \$\{name:?string\}, \$\{name:+string\}, \$\{name\#pattern\}, \$\{name\#\#pattern\}, \$\{name\%pattern\}, \$\{name\%\%pattern\}, \$\{\#name\}, \$\{\#name[*]\}}, y \texttt{\$\{\#name[@]\}}.
\item Variables compuestas, namerefs y el operador += para agregar a una variable no están en \emph{ksh88}.
\item La notación \textbackslash{}n en patrones no está disponible, al igual que la coincidencia no codiciosa, el uso de escapes de barra invertida en patrones, opciones en subpatrones, ni ninguna de las clases de caracteres [:...:].
\item El array \texttt{.sh.match} no está disponible.
\end{itemize}
\item \hyperref[sec:Chapter5]{Capítulo 5}
\begin{itemize}
\item Los valores de salida en \emph{ksh88} solo llegan hasta 128. Los programas que mueren debido a una señal tienen un estado de salida de 128 más el número de señal.
\item No hay un \emph{comando} built-in. Para reemplazar un built-in, debes usar una combinación incómoda de alias, funciones y citas.
\item La opción \texttt{set -o pipefail} y la palabra clave ! para invertir el sentido de una prueba no están disponibles. En lugar del operador \texttt{==} para \texttt{[[...]]}, \emph{ksh88} usa \texttt{=}.
\item El bucle \texttt{for} aritmético no está en \emph{ksh88}. El uso de \texttt{;\&} para pasar de un caso a otro existía en \emph{ksh88} pero no estaba documentada. La variable \texttt{TMOUT} existía en \emph{ksh88}, pero solo se aplicaba al shell en sí, no al bucle \texttt{select} o al comando \texttt{read}.
\end{itemize}
\item \hyperref[sec:Chapter6]{Capítulo 6}
\begin{itemize}
\item La versión de getopts de \emph{ksh88} no tenía la capacidad de especificar argumentos numéricos para opciones, ni una forma de especificar argumentos opcionales para opciones.
\item La aritmética integrada solo admite enteros, y los operadores ++, --, ?: y coma no están disponibles. Las constantes numéricas de la forma \texttt{base\#número} solo pueden llegar hasta la base 36. No hay funciones aritméticas integradas.
\item Solo existen arrays indexados, y el índice máximo es 1023.
\end{itemize}
\item \hyperref[sec:Chapter7]{Capítulo 7}
\begin{itemize}
\item No se admiten los siguientes redireccionadores de E/S:
\begin{itemize}
\item \texttt{<\&n-}
\item \texttt{>\&n-}
\item \texttt{<<<}
\end{itemize}
\item La red de TCP/IP está disponible a partir de \emph{ksh88e}, pero debes usar direcciones IP.
\item \emph{ksh88} no tiene el comando \emph{printf}, ni las opciones \texttt{-A, -d, -n} y \texttt{-t} para \emph{read}. \texttt{TMOUT} no afecta a \emph{read}.
\item La expansión de llaves y la sustitución de procesos son opciones de tiempo de compilación que generalmente no están disponibles en \emph{ksh88}.
\item La traducción de locales con \texttt{\$"..."} y las cadenas ANSI C con \texttt{\$'...'} no están disponibles.
\end{itemize}
\item \hyperref[sec:Chapter8]{Capítulo 8}
\begin{itemize}
\item El comando \emph{disown} no está disponible, y tampoco las opciones \texttt{-n} y \texttt{-s} para \emph{kill}. \texttt{kill -l} solo se puede usar por sí mismo para enumerar las señales disponibles.
\item \emph{true} y \emph{false} son alias predefinidos en lugar de comandos internos.
\item Las funciones y los alias se exportan a los subprocesos del shell; esto no es cierto en \emph{ksh93}.
\end{itemize}
\item \hyperref[sec:Chapter9]{Capítulo 9}
\begin{itemize}
\item Las trampas para la señal falsa DEBUG se ejecutan \emph{después} de que se ejecuta cada comando, no antes.
\item Las funciones de disciplina no están disponibles.
\end{itemize}
\item \hyperref[sec:Chapter10]{Capítulo 10}
\begin{itemize}
\item El comando \emph{umask} solo funciona con máscaras octales.
\item No puedes personalizar los editores integrados; la señal falsa KEYBD no existe.
\end{itemize}
\end{enumerate}
\section{El Estándar del Shell POSIX IEEE 1003.2}
Ha habido muchos intentos de estandarizar Unix. Los intentos monolíticos de dominación del mercado por parte de empresas de hardware, frágiles coaliciones industriales, fallas de marketing y otros esfuerzos por el estilo son parte de la historia, y también de la frustración.
Solo un esfuerzo de estandarización no ha estado vinculado a intereses comerciales: la Interfaz Portátil del Sistema Operativo, conocida como POSIX. Este esfuerzo comenzó en 1981 con el Comité de Normas de \texttt{/usr/group} (ahora UniForum), que produjo la Norma de \texttt{/usr/group} tres años después. La lista de contribuyentes creció. Con el tiempo, el esfuerzo por crear un estándar formal se trasladó bajo el paraguas del Instituto de Ingenieros Eléctricos y Electrónicos (IEEE) y la Organización Internacional de Normalización (ISO).
El primer estándar POSIX se publicó en 1988 y se revisó en 1996. Este, llamado Estándar IEEE 1003.1, abordaba problemas a nivel de llamadas al sistema. El Estándar IEEE 1003.2, que cubre el shell, programas de utilidad y problemas de interfaz de usuario, fue ratificado en septiembre de 1992 después de seis años de esfuerzo. En septiembre de 2001, se aprobó una revisión conjunta de ambos estándares. El nuevo estándar, que cubre todo el material de los dos documentos anteriores, se conoce como Estándar IEEE 1003.1-2001.
Los estándares POSIX nunca se pensaron como rígidos y absolutos. Los miembros del comité ciertamente no iban a poner armas en la cabeza de los implementadores del sistema operativo y obligarlos a adherirse. En cambio, los estándares están diseñados para ser lo suficientemente flexibles como para permitir tanto la coexistencia de software similar disponible, para que el código existente no esté en peligro de volverse obsoleto, como la adición de nuevas características, para que los proveedores tengan incentivos para innovar. En otras palabras, se supone que son el tipo de estándares de terceros que los proveedores podrían estar interesados en seguir.
Como resultado, la mayoría de los proveedores de Unix cumplen actualmente con ambos estándares. El shell Korn no es una excepción; se pretende que sea 100\% compatible con POSIX. Conviene estar familiarizado con lo que hay en el estándar si quieres escribir código que sea portable en diferentes sistemas.
La parte del shell del estándar describe utilidades que deben estar presentes en todos los sistemas y otras que son opcionales, según la naturaleza del sistema. Una de esas opciones es la opción de Utilidades de Portabilidad del Usuario, que define estándares para el uso interactivo del shell y utilidades interactivas como el editor vi. El estándar, de aproximadamente 2000 páginas, está disponible a través del IEEE; para obtener información, ponte en contacto con el IEEE:
\newpage
\begin{footnotesize}
\singlespace
IEEE Customer Service \\
445 Hoes Lane, PO Box 1331 \\
Piscataway, NJ 08855-1331 \\
(800) 678-IEEE (Estados Unidos y Canadá) \\
(732) 981-0060 (internacional/local) \\
(732) 981-9667 (fax)
customer.service@ieee.org \\
\url{http://www.standards.ieee.org/catalog/ordering.html}
\end{footnotesize}
Los miembros del comité tenían dos factores motivadores que considerar cuando diseñaron el estándar del shell. Por un lado, el diseño debía acomodar, tanto como fuera posible, el código del shell existente escrito bajo varios shells derivados de Bourne (Version 7, System V, BSD y Korn). Estos shells son diferentes de varias maneras extremadamente sutiles, la mayoría de las cuales tienen que ver con la forma en que ciertos elementos sintácticos interactúan entre sí.
Debe haber sido bastante difícil y tedioso especificar estas diferencias, sin mencionar llegar a compromisos entre ellas. Agrega los sesgos de algunos miembros del comité hacia shells particulares, y podrías entender por qué llevó seis años ratificar el primer estándar 1003.2 y otros cinco años para fusionar los dos estándares.
Por otro lado, el diseño del shell debía servir como estándar en el que basar futuras implementaciones de shell. Esto implicaba objetivos de simplicidad, claridad y precisión, objetivos que parecen especialmente esquivos en el contexto de los problemas mencionados anteriormente.
Los diseñadores encontraron una manera de aliviar este dilema: decidieron que el estándar debería incluir no solo las características incluidas en el shell, sino también aquellas explícitamente omitidas y aquellas incluidas pero con funcionalidad no especificada. La última categoría permite que algunas innovaciones de shells existentes <<se cuelen>> sin formar parte del estándar, mientras que listar las características omitidas ayuda a los programadores a determinar qué características en los scripts de shell existentes no serán portables a los shells futuros.
El estándar POSIX se basa principalmente en el shell Bourne de System V. Por lo tanto, debes asumir que las características del shell Korn que no están presentes en el shell Bourne tampoco están incluidas en el estándar POSIX.
Sin embargo, el shell Korn contribuyó con algunas de sus características al estándar POSIX, incluyendo:
\begin{itemize}
\item Sintaxis \texttt{\$((...))} para expresiones aritméticas.
\item Sintaxis \texttt{\$(...)} para sustitución de comandos, excepto que la abreviatura \texttt{\$(< filename)} para \texttt{\$(cat filename)} no está soportada.
\item Expansión de tilde (originalmente derivada del shell C).
\end{itemize}
Las siguientes características del shell Korn se dejan <<no especificadas>> en el estándar, lo que significa que su sintaxis es aceptable pero su funcionalidad no está estandarizada:
\begin{itemize}
\item Sintaxis \texttt{((...))} para condicionales aritméticos. Sin embargo, se incluyen los operadores de prueba aritmética introducidos en el \hyperref[sec:Chapter5]{Capítulo 5} (por ejemplo, \texttt{-eq, -lt}).
\item La sintaxis \texttt{[[...]]} para \emph{pruebas} condicionales. Debe usarse el comando de \emph{prueba} externo o \texttt{[...]} en su lugar. La versión del shell Korn de la prueba cumple con POSIX cuando se usa con no más de tres argumentos. (También cumple con cuatro argumentos, si el primer argumento es !).
\item La sintaxis para definir funciones que usa este libro. La otra sintaxis mostrada en el \hyperref[sec:Chapter4]{Capítulo 4} (\texttt{fname()} en lugar de \texttt{function fname}) se admite, con lo que describimos como <<semántica POSIX>>; ver más abajo.
\item La estructura de control \texttt{select}.
\item Solo se permiten números de señal si los números para ciertas señales clave (INT, TERM y algunas otras) son iguales que en las versiones históricas más importantes de Unix. En general, los scripts de shell deben usar nombres simbólicos para las señales.
\end{itemize}
El estándar POSIX admite funciones, pero la semántica es más débil que la de las funciones de estilo de \texttt{function} del shell Korn: las funciones no tienen trampas u opciones locales, y no es posible definir variables locales. (Por esta razón, \emph{ksh93} tiene dos sintaxis diferentes para definir funciones, con semánticas diferentes).
Se admiten bloques de código (\texttt{\{...;\}}). Para obtener máxima portabilidad, cuando desees llaves literales, debes entrecomillarlas (por razones demasiado complicadas para entrar aquí).
El estándar POSIX introdujo las siguientes características, que difieren del comportamiento tradicional del shell Bourne. \emph{ksh93} las admite todas:
\begin{itemize}
\item Se cambió el orden de búsqueda de comandos para permitir que ciertos comandos integrados sean anulados por funciones, ya que los alias no están incluidos en el estándar. Los comandos integrados se dividen en dos conjuntos según su posición en el orden de búsqueda de comandos: algunos se procesan antes que las funciones, otros después. Específicamente, los comandos integrados \emph{break}, : (no hacer nada), \emph{continue}, . (dot), \emph{eval, exec, exit, export, readonly, return, set, shift, trap,} y \emph{unse}t tienen prioridad sobre las funciones.
\item Un nuevo comando integrado, \emph{command}, te permite usar comandos integrados que no están en la lista anterior incluso si hay funciones con el mismo nombre. \footnote{Pero ten en cuenta que no es un comando integrado especial. Aquí se nota el diseño por comité.}
\item Una nueva palabra clave, !, toma la negación lógica del estado de salida de un comando: si \emph{command} devuelve un estado de salida 0, \texttt{! command} devuelve 1; si \emph{comand} devuelve un valor no nulo, \texttt{! command} devuelve 0. ! se puede usar con \texttt{\&\&}, ||, y paréntesis (para subcadenas anidadas) para crear combinaciones lógicas de estados de salida en condicionales.
\item El comando \texttt{unset -v} se utiliza en lugar de \texttt{unset} (sin una opción) para eliminar la definición de una variable. Esto proporciona una mejor coincidencia sintáctica con \texttt{unset -f}, para deshacer la definición de funciones.
\end{itemize}
Finalmente, debido a que el estándar POSIX está destinado a promover la portabilidad de scripts de shell, evita mencionar explícitamente características que solo se aplican al uso interactivo del shell, incluidos alias, modos de edición, teclas de control, y así sucesivamente. La opción de Utilidades de Portabilidad del Usuario cubre estos. También evita mencionar ciertos problemas clave de implementación: en particular, no hay requisito de que se use la multitarea para trabajos en segundo plano, subcadenas, etc. Esto se hizo para permitir la portabilidad a sistemas no multitarea como MS-DOS, de modo que, por ejemplo, el Toolkit MKS (ver más adelante en este apéndice) pueda ser compatible con POSIX.
\section{dtksh}
El Desk Top Korn Shell (\emph{dtksh}) es una parte estándar del \emph{Common Desktop Environment (CDE)}, disponible en sistemas Unix comerciales como Solaris, HP-UX y AIX. Está basado en una versión algo más antigua de \emph{ksh93}. Evolucionó a partir del programa anterior \emph{wksh}, el Windowing Korn shell, lanzado por Unix System Laboratories a fines de 1992. Es un Korn shell completo, compatible con la versión que describe este libro,\footnote{Las características enumeradas a lo largo del libro como introducidas en versiones <<recientes>> no estarán en \emph{dtksh}.}
y tiene extensiones para la programación de interfaces gráficas de usuario (GUI) en el entorno del sistema X Window. Se encuentra típicamente en \texttt{/usr/dt/bin/dtksh}.
\emph{dtksh} admite el Toolkit gráfico OSF/Motif al poner sus rutinas a disposición como comandos integrados. Esto permite a los programadores combinar la fortaleza del Korn shell como un entorno de programación de sistemas Unix con el poder y la abstracción del Toolkit. El resultado es un entorno unificado para el desarrollo rápido y sencillo de software basado en gráficos.
Existen varias herramientas de desarrollo de GUI que permiten construir interfaces de usuario con un editor basado en gráficos en lugar de con código de lenguaje de programación. Pero tales herramientas suelen ser enormes, costosas y complejas. \emph{dtksh}, por otro lado, es económico e insuperable en cuanto a su integración suave con Unix; ¡es la única herramienta de este tipo que puedes usar como tu shell de inicio de sesión! (Bueno, casi; consulta la siguiente sección). Es una opción definitiva para los programadores de sistemas que utilizan estaciones de trabajo basadas en X y necesitan una herramienta de prototipado rápido.
Para darte una idea del código \emph{dtksh}, aquí tienes un script que implementa el programa canónico <<Hola, mundo>> mostrando una pequeña ventana con un botón <<Aceptar>>. Es del artículo \emph{Graphical Desktop Korn Shell}, en el número de julio de 1998 de Linux Journal, escrito por George Kraft IV. (Ver \url{http://www.linuxjournal.com/article.php?sid=2643}.) Este código no debería tener sorpresas para los programadores de X y Motif:
\begin{lstlisting}[language=bash]
#!/usr/dt/bin/dtksh
XtInitialize TOPLEVEL dtHello DtHello "$@"
XmCreateMessageDialog HELLO $TOPLEVEL hello \
dialogTitle:"DtHello" \
messageString:"$(print "Hola\nMundo")"
XmMessageBoxGetChild HELP $HELLO \
DIALOG_HELP_BUTTON
XtUnmanageChild $HELP
XmMessageBoxGetChild CANCEL $HELLO \
DIALOG_CANCEL_BUTTON
XtUnmanageChild $CANCEL
XtAddCallback $HELLO okCallback exit
XtManageChild $HELLO
XtMainLoop
\end{lstlisting}
\url{http://www.cactus.org/~gk4/kraft/george/papers/dtksh/} es la presentación web del Sr. Kraft sobre \emph{dtksh}.
El siguiente libro está dedicado a \emph{dtksh}: \emph{Desktop KornShell Graphical Programming} de J. Stephen Pendergast, Jr., publicado por Addison-Wesley, 1995 (ISBN: 0-201-63375-2). Los ejemplos del libro están disponibles en línea en \url{ftp://ftp.aw.com/aw.prof.comp.series/pendergrast.examples.tar.Z}. También está disponible \url{ftp://ftp.aw.com/aw.prof.comp.series/pend.dtksh1}, un archivo de texto que proporciona una descripción general del libro. Lamentablemente, al momento de escribir esto, este libro está fuera de impresión.
\section{tksh}
En 1996, mientras era estudiante de posgrado en informática en la Universidad de Princeton, el Dr. Jeffrey L. Korn \footnote{Sí, el hijo de David Korn. Ahora trabaja en el mismo centro de investigación que su padre en AT\&T Laboratories, aunque en un área diferente.}
escribió \emph{tksh}. Esta es una integración de \emph{ksh93} con Tcl/Tk. La siguiente cita (de la página web de investigación del Dr. Korn) lo resume bien:
\emph{Tksh} es un lenguaje gráfico (similar a Visual Basic o Tcl/Tk) que utiliza KornShell (\emph{ksh93}) para la creación de scripts y Tk para la interfaz gráfica de usuario. Tksh está implementado como una extensión de \emph{ksh93} y permite que las bibliotecas de Tcl, como Tk, se ejecuten sobre \emph{ksh93} sin cambios. \emph{ksh93} es adecuado para la creación de scripts gráficos porque es compatible con \emph{sh}, lo que facilita tanto el aprendizaje como la extensión de scripts existentes para proporcionar una interfaz de usuario. Tksh también permite que los scripts de Tcl se ejecuten sin modificaciones, lo que hace posible combinar componentes escritos en Tcl o \emph{ksh93}.
La página de inicio de \emph{tksh} todavía está en Princeton: \url{http://www.cs.princeton.edu/\~{}jlk/tksh/}. Tiene enlaces a documentos y documentación que se pueden descargar e imprimir. Sin embargo, el enlace a los ejecutables de \emph{tksh} está desactualizado. El código fuente de \emph{tksh} está disponible en AT\&T Research como parte del paquete ast-open, que también contiene \emph{ksh93} y reimplementaciones de muchas otras herramientas de Unix. Consulta el \hyperref[sec:ApendiceC]{Apéndice C} para obtener más información.
El siguiente script de ejemplo, del artículo de USENIX sobre \emph{tksh}, se llama \emph{watchdir}:
\begin{lstlisting}[language=bash]
# Demo de Tksh
# Jeff Korn
#
# Este script realiza un seguimiento de los directorios visitados y muestra los archivos
# en el directorio actual. Puedes hacer doble clic en archivos y
# directorios. El script debe usarse de manera interactiva, así que para ejecutar:
# $ tksh
# $ . scripts/watchdir
function winsetup {
pack $(frame .f)
frame .f.dirname -relief raised -bd 1
pack .f.dirname -side top -fill x
pack $(frame .f.ls) $(frame .f.dirs) -side left
label .f.dirname.label -text "Directorio actual: "
label .f.dirname.pwd -textvariable PWD
pack .f.dirname.label .f.dirname.pwd -side left
scrollbar .f.ls.scroll -command ".f.ls.list yview"
listbox .f.ls.list -yscroll ".f.ls.scroll set" -width 20 -setgrid 1
pack $(label .f.ls.label -text "Contenido del directorio") -side top
pack .f.ls.list .f.ls.scroll -side left -fill y -expand 1
scrollbar .f.dirs.scroll -command ".f.dirs.list yview"
listbox .f.dirs.list -yscroll ".f.dirs.scroll set" -width 20 -setgrid 1
pack $(label .f.dirs.label -text "Directorios visitados") -side top
pack .f.dirs.list .f.dirs.scroll -side left -fill y -expand 1
bind .f.dirs.list "" 'cd $(selection get)'
bind .f.ls.list "" 'tkfileselect $(selection get)'
}
function tkfileselect {
[[ -d "$1" ]] && tkcd "$1"
[[ -f "$1" ]] && ${EDITOR-${VISUAL-emacs}} "$1"
}
function tkcd {
cd $1 > /dev/null || return
.f.ls.list delete 0 end
set -o markdirs
.f.ls.list insert end .. *
[[ ${VisitedDir["$PWD"]} == "" ]] && .f.dirs.list insert end "$PWD"
VisitedDir["$PWD"]=1
}
typeset -A VisitedDir
winsetup > /dev/null
alias cd=tkcd
tkcd .
\end{lstlisting}
Lo interesante de \emph{tksh}, además de la interesante combinación de tecnologías complementarias, es que lleva la programación Tk al nivel del shell. La programación gráfica con Tk es mucho más elevada que con el kit de herramientas Motif; por lo tanto, la curva de aprendizaje es más fácil de superar y los scripts son más fáciles de leer y escribir.
\section{pdksh}
Muchos de los sistemas tipo Unix de código abierto, como GNU/Linux, vienen con el Public Domain Korn Shell, \emph{pdksh}. \emph{pdksh} está disponible como código fuente; comienza en su página de inicio: \url{http://www.cs.mun.ca/\~{}michael/pdksh/}. Incluye instrucciones para la construcción e instalación en varias plataformas Unix.
\emph{pdksh} fue originalmente escrito por Eric Gisin, quien lo basó en el clon de dominio público del shell Bourne de la Versión 7 creado por Charles Forsyth. Es en su mayoría compatible con el Korn shell de 1988 y POSIX, con algunas extensiones propias.
Su modo de edición en \emph{emacs} es en realidad más potente que el del Korn shell de 1988. Al igual que el editor Emacs completo, puedes personalizar las teclas que invocan comandos de edición (conocidos como \emph{key bindings} en la terminología de Emacs). Los comandos de edición tienen nombres completos que puedes asociar con teclas mediante el uso del comando \emph{bind}.
Por ejemplo, si quieres configurar CTRL-U para hacer lo mismo que CTRL-P (es decir, retroceder al comando anterior en el archivo de historial), podrías poner este comando en tu \emph{.profile}:
\begin{lstlisting}[language=bash]
bind '^U'=up-history
\end{lstlisting}
Incluso puedes configurar secuencias de escape de dos caracteres, que (por ejemplo) te permiten usar las teclas de flecha ANSI además de caracteres de control, y puedes definir \emph{macros}, es decir, abreviaturas para secuencias de comandos de edición.
Las características adicionales del Public Domain Korn Shell incluyen comodines de \emph{alternancia} (tomados del C shell) y notación de tilde definible por el usuario, en la que puedes configurar \~{} como una abreviatura para cualquier cosa, no solo nombres de usuario. También hay algunas diferencias sutiles en la evaluación de expresiones enteras y en la creación de alias.
\emph{pdksh} carece de las siguientes características de la versión oficial:
\begin{itemize}
\item La variable integrada \texttt{LINES}.
\item La señal falsa \texttt{DEBUG}.
\item Las señales falsas \texttt{ERR} y \texttt{EXIT} dentro de funciones.
\item Las funciones heredan la configuración de traps del script principal.
\item Las clases de caracteres de expansión de archivos POSIX (\texttt{[[:alpha:]]}, etc.) no están disponibles.
\item El comando \emph{read} y el bucle \emph{select} no utilizan los modos de edición de línea de comandos.
\item El último comando de una canalización no se ejecuta en el shell principal. Por lo tanto, \texttt{echo hi | read x; print \$x} no funciona igual que en \emph{ksh}. (La mayoría de los shells de estilo Bourne funcionan de esta manera).
\item La forma de opción \texttt{set -o} de \emph{ksh} es \texttt{set -X option} en \emph{pdksh}.
\end{itemize}
Aunque carece de la mayoría de las extensiones de \emph{ksh93}, \emph{pdksh} es una alternativa valiosa a los shells C y Bourne.
\section{bash}
\emph{bash} es probablemente el shell <<tercerizado>> más popular disponible para Unix y otros sistemas. En particular, es el shell predeterminado en sistemas GNU/Linux (también se incluye en el CD <<freeware>> con Solaris 8). Puedes obtenerlo desde Internet, a través de FTP anónimo en \url{ftp.gnu.org} en el directorio \texttt{/pub/gnu/bash}. También puedes pedirlo a su fabricante en la dirección que se indica aquí.
\begin{footnotesize}
\singlespace
The Free Software Foundation \\
59 Temple Place - Suite 330 \\
Boston, MA 02111-1307 \\
(617) 542-2652 \\
(617) 542-5942 (fax) \\
gnu@gnu.org \\
\url{http://www.gnu.org} \\
\end{footnotesize}
\emph{Bash} fue escrito por Brian Fox y Chet Ramey. Chet Ramey es el actual mantenedor. Su nombre está en línea con la predilección de la FSF por los juegos de palabras: significa Bourne-Again Shell. Aunque \emph{bash} está fácilmente disponible y no tienes que pagar por ello (además del costo de los medios, llamadas telefónicas, etc.), no es realmente un software de dominio público. Mientras que el software de dominio público no tiene restricciones de licencia, el software de la FSF sí las tiene. Pero esas restricciones son diametralmente opuestas a las de una licencia comercial:\footnote{En consecuencia, el documento que detalla estas restricciones se llama \emph{copyleft}.}
¡en lugar de acordar no distribuir el software más, acuerdas no impedir que se distribuya más! En otras palabras, disfrutas del uso ilimitado del software siempre y cuando aceptes no obstaculizar que otros hagan lo mismo. Richard Stallman, el fundador de la FSF, inventó este concepto intrigante y admirable.
Hoy en día, los ideales de los movimientos de Software Libre y Código Abierto, el proyecto GNU y la calidad del software GNU son bien conocidos. El sistema GNU más popular es GNU/Linux, que utiliza el núcleo Linux y las utilidades GNU para crear un entorno de computación completo, totalmente funcional y compatible con Unix y POSIX.
\emph{bash} es totalmente compatible con el estándar POSIX de 1992. Tiene varias de las características más importantes del Korn shell y las características del C shell que el Korn shell ha adoptado, incluyendo alias, funciones, notación de tilde, modos de edición emacs y vi, expresiones aritméticas, control de trabajos, etc.
La superposición de características entre \emph{bash} y el Korn shell ha aumentado en los últimos años. Incluye muchas características del \emph{ksh93}. Pero no es un clon exacto de \emph{ksh}. El FAQ de \emph{bash}, publicado mensualmente por Chet Ramey, enumera las siguientes diferencias entre \emph{bash} y \emph{ksh93}. Los elementos incluidos entre corchetes (\texttt{[...]}) están listados en este libro, pero no en el FAQ.
Lo siguiente, en \emph{ksh93}, no está disponible en \emph{bash} 2.05:
\begin{itemize}
\item Arrays asociativos
\item Aritmética y variables de punto flotante
\item Funciones de la biblioteca matemática \texttt{\$\{!nombre[sub]\}} nombre de subíndice para arreglo asociativo
\item \texttt{"."} está permitido en nombres de variables para crear un espacio de nombres jerárquico
\item Sintaxis de asignación compuesta más extensa
\item Funciones de disciplina
\item Instrucciones integradas \emph{sleep} y \emph{getconf} (\emph{bash} tiene versiones cargables)
\item \texttt{typeset -n} y variables \emph{nameref}
\item La trampa KEYBD
\item Las variables: \texttt{.sh.edchar, .sh.edmode, .sh.edcol, .sh.edtext, .sh.version, .sh.name, .sh.subscript, .sh.value, HISTEDIT} [La variable \texttt{.sh.match} también]
\item Referencias posteriores en la coincidencia de patrones (\textbackslash{}N)
\item El operador \& en listas de patrones para coincidencia
\item \texttt{print -f} (bash usa \emph{printf})
\item \emph{fc} ha sido renombrado a \emph{hist}
\item El comando de punto (.) puede ejecutar funciones de shell
\item Estados de salida entre 0 y 255
\item La opción de asignación de variables +=
\item \texttt{TMOU}T es el tiempo de espera predeterminado para \emph{read} y \emph{select}
\item Redirecciones \texttt{<\&n-} y \texttt{>\&n-} (combinación de duplicar y cerrar) [Aquí-strings con \texttt{<<<}]
\item Mezcla de \texttt{FPATH} y \texttt{PATH}
\item \texttt{getopts -a}
\item La opción de invocación \texttt{-R}
\item La trampa \texttt{DEBUG} ahora se ejecuta antes de cada comando simple, en lugar de después
\item Los modificadores \texttt{\%H, \%P, \%T} de \emph{printf}, y una base de salida para \texttt{\%d} [También \texttt{\%Z}.]
\end{itemize}
Lo siguiente, en \emph{ksh93}, está presente en \emph{bash} 2.05:
\begin{itemize}
\item El comando \texttt{for ((...;...;...)) ; do list; done} para comandos aritméticos
\item Los operadores aritméticos \texttt{?:, ++, --} y coma (\texttt{,})
\item Las expansiones de variables de shell: \texttt{\$\{!param\}, \$\{param:offset[:len]\}, \$\{param/pat[/str]\}, \$\{!param*\}}
\item Asignación compuesta de arreglos
\item La palabra reservada !
\item Instrucciones integradas cargables, pero \emph{ksh} usa \emph{builtin} mientras que \emph{bash} usa \emph{enable}
\emph Las instrucciones integradas \emph{command}, \emph{builtin} y \emph{disown}
\item La citación nueva \emph{\$'...'} y \emph{\$"..."}
\item \texttt{FIGNORE} (pero \emph{bash} usa \texttt{GLOBIGNORE}), \texttt{HISTCMD}
\item \texttt{set -o notify}, \texttt{set -C}
\item Cambios en el comando integrado \emph{kill}
\item \texttt{read -A} (\emph{bash} usa \texttt{read -a})
\item \texttt{read -t/read -d}
\item \texttt{trap -p}
\item \texttt{exec -a/exec -c}
\item El comando de punto (.) restaura los parámetros posicionales cuando se completa
\item El comando test se ajusta a POSIX.
\item \texttt{umask -S}
\item \texttt{unalias -a}
\item Sustitución de comandos y aritmética realizada en \texttt{PS1, PS4} y \texttt{ENV}
\item Completado de nombres de comandos
\item \texttt{ENV} procesado solo para shells interactivos
\end{itemize}
\emph{bash} tiene muchas características propias que lo convierten en un entorno muy potente y flexible. Aquí tienes algunas de las características destacadas:
\begin{itemize}
\item Puedes poner escapes de barra invertida en la cadena de texto del indicador primario (\texttt{PS1}), en la que bash sustituye cosas como la fecha, hora, directorio de trabajo actual, nombre de la máquina, nombre de usuario, shell, etc.
\item Los comandos integrados, \emph{command} y \emph{enable}, te brindan más control sobre los pasos que sigue \emph{bash} para buscar comandos, es decir, el equivalente de \emph{bash} a la lista de pasos de búsqueda de comandos en el \hyperref[sec:Chapter7]{Capítulo 7}.
\item El modo de edición \emph{emacs} es personalizable, incluso más que su equivalente en \emph{pdksh}. Puedes usar el comando \emph{bind} para configurar tus propias preferencias de teclas, y hay varios comandos más disponibles, incluida la capacidad de deshacer tu último comando.
\item También puedes volver a asignar teclas en el modo de edición \emph{vi}.
\item \emph{pushd} y \emph{popd} están integrados, al igual que en el C shell.
\item Los arreglos indexados pueden tener un tamaño ilimitado.
\item Muchas opciones y variables nuevas te permiten personalizar tu entorno con una flexibilidad sin precedentes. Esto incluye \texttt{set -o posix} para una conformidad estricta con POSIX.
\end{itemize}
Nos sentimos obligados a decir que muchos usuarios prefieren \emph{bash} al Korn shell. Con la creciente popularidad de GNU/Linux y varios sistemas derivados de BSD, no está claro qué shell tiene una base de usuarios más grande. En cualquier caso, \emph{bash} es definitivamente una excelente elección. Recomendamos el libro \emph{Learning the bash Shell} de Cameron Newham y Bill Rosenblatt, publicado por O'Reilly \& Associates. (Está basado en la primera edición de este libro).
\section{zsh}
\emph{zsh} es un potente shell interactivo y lenguaje de secuencias de comandos con muchas características que se encuentran en \emph{ksh, bash} y \emph{tcsh}, así como varias características únicas. \emph{zsh} tiene la mayoría de las características de \emph{ksh88} pero pocas de \emph{ksh93}. Está disponible de forma gratuita y debería compilar y ejecutarse en prácticamente cualquier versión moderna de Unix. También hay versiones para otros sistemas operativos. La página principal de \emph{zsh} es \url{http://www.zsh.org}. La versión actual es 4.0.2.
En esta sección cubrimos:
\begin{itemize}
\item Globbing extendido
\item Completado
\item Edición de línea de comandos
\item Indicadores y temas de indicadores
\item Diferencias entre \emph{zsh} y \emph{ksh}
\end{itemize}
\subsection{Globbing extendido}
Una característica muy útil es el operador glob recursivo \texttt{**}\footnote{El \emph{globbing} es un argot técnico para la expansión de comodines.}
. Por ejemplo, es sencillo construir una búsqueda recursiva con \emph{grep}:
\begin{lstlisting}[language=bash]
grep foo **/*.c
\end{lstlisting}
O para encontrar recursivamente todos los archivos o directorios llamados \emph{core}, prueba:
\begin{lstlisting}[language=bash]
print **/core
\end{lstlisting}
Otra característica útil son los \emph{calificadores de glob}. Hay muchos de ellos, por ejemplo, para imprimir solo archivos regulares en el directorio actual:
\begin{lstlisting}[language=bash]
print *(.)
\end{lstlisting}
o solo los directorios:
\begin{lstlisting}[language=bash]
print *(/)
\end{lstlisting}
Combinar estos con el operador glob recursivo puede ser útil. Podemos mejorar el ejemplo anterior de encontrar archivos \emph{core} limitando la búsqueda solo a archivos regulares:
\begin{lstlisting}[language=bash]
print **/core(.)
\end{lstlisting}
Otro calificador es U para objetos del sistema de archivos de los que eres propietario. Lo siguiente imprime todos los archivos que posees en \texttt{/tmp} y sus subdirectorios:
\begin{lstlisting}[language=bash]
print /tmp/**/*(U)
\end{lstlisting}
Los calificadores de glob también se pueden combinar. Por ejemplo, usando la palabra clave del archivo de socket = en combinación con U, es fácil encontrar archivos de socket en \texttt{/tmp} y sus subdirectorios que posees:
\begin{lstlisting}[language=bash]
print /tmp/**/*(U=)
\end{lstlisting}
También están disponibles calificadores de tamaño de archivo. Por ejemplo, para encontrar todos los archivos en tu directorio de inicio que tienen más de 10 megabytes de tamaño:
\begin{lstlisting}[language=bash]
print ~/**/*(Lm+10)
\end{lstlisting}
Y también hay calificadores de permisos de archivo. Por ejemplo, el calificador W selecciona objetos del sistema de archivos que son escribibles por el mundo. Puedes usarlo para encontrar todos los directorios en tu directorio de inicio y sus subdirectorios que te pertenecen y que son escribibles por el mundo:
\begin{lstlisting}[language=bash]
print ~/**/*(UW/)
\end{lstlisting}
Consulta \emph{zshexpn(1)} para obtener más información.
\subsection{Completado}
El sistema de completado de \emph{zsh} es extremadamente sofisticado. La idea principal es que cada vez que estás a punto de escribir algo en la línea de comandos, si presionas TAB, \emph{zsh} intentará completarlo. \emph{zsh} viene con muchos valores predeterminados para la completación y también es completamente personalizable.
Para obtener un conjunto completo de funciones de completado predeterminadas, ejecuta los siguientes comandos (normalmente en tu archivo de inicio \texttt{~/.zshrc}):
\begin{lstlisting}[language=bash]
autoload -U compinit
compinit
\end{lstlisting}
Ahora veamos algunos ejemplos. Representamos la tecla TAB en los ejemplos como \texttt{[TAB]}.
Primero, \emph{zsh} es inteligente al realizar completados. Por ejemplo, \texttt{cd[TAB]} solo expande directorios, eliminando así el ruido de la completación.
¿Alguna vez te has sentido frustrado porque no puedes recordar exactamente el nombre del comando del que deseas obtener más información, y \emph{man -k}\footnote{Esto realiza una búsqueda de palabras clave en una base de datos en línea extraída de las páginas del manual.}
no está configurado en tu sistema? Bueno, \emph{zsh} completará las páginas del manual disponibles para ti:
\begin{lstlisting}[language=bash]
g@sifl:pts/7% man zsh[TAB]
zsh zshcompctl zshcontrib zshmodules zshzftpsys
zshall zshcompsys zshexpn zshoptions zshzle
zshbuiltins zshcompwid zshmisc zshparam
\end{lstlisting}
O tal vez quieras descubrir un nombre de proceso o PID que deseas matar:
\begin{lstlisting}[language=bash]
g@sifl:pts/2% kill [TAB]
9652 pts/2 00:00:00 zsh
9653 pts/2 00:00:00 ps
\end{lstlisting}
Para \emph{finger}, expande usuarios:
\begin{lstlisting}[language=bash]
g@sifl:pts/7% finger o[TAB]
odin omg oof operator orb
\end{lstlisting}
y hosts:
\begin{lstlisting}[language=bash]
g@sifl:pts/7% finger oof@[TAB]
brak localhost sifl zorak
\end{lstlisting}
Usando la función \emph{compdef} distribuida, puedes definir tus propias completaciones, utilizando tus propias funciones personalizadas o las funciones de completado que vienen con \emph{zsh}. Por ejemplo, la distribución define el comando \emph{kill} para usar la función de distribución \texttt{\_pids} para proporcionar identificadores de procesos. También puedes usarlo para definir la completación para otros comandos, como el comando \emph{pstack} de Solaris:
\begin{lstlisting}[language=bash]
compdef _pids pstack
\end{lstlisting}
Una vez hecho esto, puedes aplicar la completación al comando \emph{pstack} de la siguiente manera:
\begin{lstlisting}[language=bash]
g@sifl:pts/7% pstack [TAB]
13606 pts/7 00:00:00 zsh
13607 pts/7 00:00:00 ps
\end{lstlisting}
Otra función de completado de distribución muy útil es \emph{\_gnu\_generic}. Esto se puede aplicar a cualquier comando que utilice las convenciones de opción de línea de comando \texttt{--long-option} de GNU. La distribución de \emph{zsh} especifica muchos comandos de GNU para completar con esta función (como \emph{tar}):
\begin{lstlisting}[language=bash]
g@sifl:pts/7% tar --v[TAB]
--verbose --verify --version --volno-file
\end{lstlisting}
Y esto es solo la punta del proverbial iceberg. Hay mucho más en el sistema de completado de \emph{zsh}; consulta \emph{zshcompsys(1)} para los detalles (\emph{gory}).
\subsection{Editor de línea de comandos}
El editor de línea de comandos de \emph{zsh} es extremadamente potente. Tiene varias características únicas, incluida la edición de varias líneas y una pila de búfer de entrada. El editor de comandos de varias líneas facilita la composición de pequeños scripts en la línea de comandos, ya que no solo tienes una línea para editar.
La pila de búfer de entrada resulta muy útil. Mientras escribes un comando, puedes escribir ESC q y la línea actual se coloca en la pila de búfer. La línea de entrada se borra y puedes escribir otro comando. Cuando se ejecuta ese comando, la línea anterior se saca de la pila y puedes continuar con ese comando. Consulta \emph{zshzle(1)} para obtener más detalles.
\subsection{Indicadores y temas de indicadores}
Aunque la mayoría de las shells modernas tienen indicadores personalizables, \emph{zsh} lo eleva a una forma de arte. Una de las características únicas es un indicador del lado derecho, \texttt{RPROMPT}, que es muy útil para mostrar el directorio actual. Esto a su vez elimina el desorden del indicador del lado izquierdo:
\begin{lstlisting}[language=bash]
g@sifl:pts/2% RPROMPT='%~'
g@sifl:pts/2% ~/src/xemacs-21.1.14
\end{lstlisting}
Además, puedes definir colores y fuentes en negrita, y el indicador puede ocupar más de una línea.
Y dado que la noción de temas\footnote{Algunas GUIs populares, como GNOME, soportan temas. En lugar de tener una apariencia inmutable, pueden cambiarse a diferentes estilos o temas. Las distribuciones de estos GUIs a menudo contienen varios para elegir. Algunas de ellas tienden a emular otras GUI, mientras que otras son nuevas y son más que nada divertidas decoraciones de ventanas.}
se ha vuelto popular en interfaces de usuario gráficas como GNOME, se pueden definir temas de indicadores de \emph{zsh}; la distribución incluye varios entre los que puedes elegir. Para habilitar los temas de indicadores, agrega estas líneas a tu \texttt{\~{}/.zshrc}:
\begin{lstlisting}[language=bash]
autoload -U promptinit
promptinit
\end{lstlisting}
Para ver qué temas están disponibles, ejecuta:
\begin{lstlisting}[language=bash]
g@sifl:pts/2% prompt -l
Currently available prompt themes:
adam1 adam2 bart bigfade clint elite2 elite fade fire off oliver
redhat suse zefram
\end{lstlisting}
Para habilitar un tema, usa la opción \texttt{-s}. Por ejemplo:
\begin{lstlisting}[language=bash]
g@sifl:pts/7% prompt -s bart
sifl [prompt -s bart] ~ 01-10-04 11:58PM
g@sifl:pts/7%
\end{lstlisting}
Puedes ver que \emph{bart} es un indicador de dos líneas con varios componentes como el nombre del host, el comando anterior, el directorio actual y la fecha y hora. Consulta \emph{zshcontrib(1)} para obtener más detalles sobre los temas de indicadores.
\subsection{Diferencias entre \emph{zsh} y \emph{ksh}}
Esta sección se deriva de información en el FAQ de \emph{zsh}.
La mayoría de las características de \emph{ksh88} (y, por lo tanto, también del Shell de Bourne, \emph{sh}) están implementadas en zsh; pueden surgir problemas porque la implementación es ligeramente diferente. También ten en cuenta que no todas las \emph{ksh} son iguales. Esto se basa en la versión 11/16/88f de \emph{ksh}; las diferencias con \emph{ksh93} son más sustanciales.
Como resumen del estado:
\begin{enumerate}
\item Debido a todas las opciones, no es seguro asumir que una ejecución general de \emph{zsh} por un usuario se comportará como \emph{sh} o \emph{ksh} compatible.
\item Invocar \emph{zsh} como \emph{sh} o \emph{ksh} (o si alguno de ellos es un enlace simbólico a \emph{zsh}) establece opciones apropiadas y mejora la compatibilidad (desde dentro de \emph{zsh}, llamar \texttt{ARGV0=sh zsh} también funcionará).
\item Desde la versión 3.0 en adelante, el grado de compatibilidad con \emph{sh} en estas circunstancias es muy alto: \emph{zsh} ahora se puede usar con GNU \emph{configure} o \emph{Configure de perl}, por ejemplo.
\item El grado de compatibilidad con \emph{ksh} también es alto, pero faltan algunas cosas: por ejemplo, las expresiones de coincidencia de patrones más sofisticadas son diferentes para versiones anteriores a 3.1.3; consulta la lista detallada a continuación.
\item También a partir de 3.0, está disponible el comando \emph{emulate}: \texttt{emulate ksh} y \texttt{emulate sh} establecen varias opciones, así como cambian el efecto de las banderas de opciones de un solo carácter, como si el shell se hubiera invocado con el nombre apropiado. Incluir el comando \texttt{emulate sh; setopt localoptions} en una función del shell activa la emulación \emph{sh} solo para esa función. En 4.0 (y en 3.0.6 a 8), esto se puede abreviar como \texttt{emulate -L sh}.
\end{enumerate}
La diferencia clásica es la división de palabras: \emph{zsh} mantiene el resultado de \texttt{\$variable} como una sola palabra, incluso si la variable contiene espacios en blanco. Esto confunde a muchos \emph{usuarios principiantes de zsh}. La respuesta es establecer \texttt{SH\_WORD\_SPLIT} para compatibilidad hacia atrás. La siguiente diferencia más clásica es que los patrones de globs no coincidentes hacen que el comando \emph{abort}; establece \texttt{NO\_NOMATCH} para evitar eso.
\emph{zsh} tiene un conjunto grande de opciones que aumentan la compatibilidad con \emph{ksh}, aunque tal vez disminuyan las habilidades de \emph{zsh}: consulta las entradas del manual para más detalles. Si se invoca como \emph{ksh}, el shell establece las opciones adecuadas.
Aquí hay algunas diferencias con \emph{ksh} que podrían ser significativas para los programadores de \emph{ksh}, algunas de las cuales pueden interpretarse como errores. Ten en cuenta que esta lista está deliberadamente bastante completa y que la mayoría de los elementos son bastante menores. Aquellos marcados con \nmid{} realizan de manera similar a \emph{ksh} si el shell se invoca con el nombre \emph{ksh} o si está en efecto \texttt{emulate ksh}. Las palabras en mayúsculas con guiones bajos en sus nombres se refieren a opciones del shell.
\emph{Sintaxis}
\begin{itemize}
\item \nmid{} División de palabras en el shell: consulta arriba.
\item \nmid{} Los arrays son (por defecto) más parecidos a \emph{csh} que a \emph{ksh}: los subíndices comienzan en 1, no en 0; \texttt{array[0]} se refiere a \texttt{array[1]; \$array} se refiere a todo el array, no a \texttt{\$array[0]}; las llaves no son necesarias: \texttt{\$a[1]} es lo mismo que \texttt{\$\{a[1]\}}, etc. Establece la opción \texttt{KSH\_ARRAYS} para compatibilidad.
\item Los coprocesos se establecen con \texttt{coproc; |\&} se comporta como \emph{csh}.\footnote{En \texttt{csh, |\&} envía tanto la salida estándar como la salida de error por el mismo conducto; es equivalente a ... \texttt{2>\&1 | ....}}
El manejo de los descriptores de archivo de coproceso también es diferente.
\item Para \texttt{cmd1 \&\& cmd2 \&}, solo \texttt{cmd2}, en lugar de toda la expresión, se ejecuta en segundo plano en \emph{zsh}. El manual da a entender que esto es un error. Usa \texttt{\{ cmd1 \&\& cmd2 \} \&} como solución.
\end{itemize}
\emph{Substituciones de línea de comandos, globbing, etc.}
\begin{itemize}
\item \nmid{} La falta de coincidencia con un patrón de globbing provoca un error (usa \texttt{NO\_NOMATCH}).
\item \nmid{} Los resultados de las sustituciones de parámetros se tratan como texto plano: \texttt{foo="*"; print \$foo} imprime todos los archivos en \emph{ksh} pero imprime * en \emph{zsh} (usa \texttt{GLOB\_SUBST}).
\item \nmid{} Las variables de indicador (por ejemplo, \texttt{PS1}) no sufren sustitución de parámetros por defecto (usa \texttt{PROMPT\_SUBST}).
\item \nmid{} El globbing estándar no permite listas de patrones al estilo \emph{ksh}. La Tabla \ref{Tab:A-1} muestra patrones equivalentes.\\
Las formas \^{}, \~{} y \# (pero no |) requieren establecer \texttt{EXTENDED\_GLOB}. A partir de la versión 3.1.3, las formas de \emph{ksh} son completamente compatibles cuando está en efecto la opción \texttt{KSH\_GLOB}; para versiones anteriores debes usar los equivalentes dados en la Tabla \ref{Tab:A-1}.
\item Las asignaciones no entrecomilladas hacen la expansión de archivos después de los dos puntos (destinado para variables de estilo \texttt{PATH}).
\item \emph{integer} no permite \texttt{-i}.
\item \emph{typeset} e integer tienen un comportamiento especial para las asignaciones en ksh, pero no en \emph{zsh}. Por ejemplo, esto no funciona en \emph{zsh}:
\begin{lstlisting}[language=bash]
integer k=$(wc -l ~/.zshrc)
\end{lstlisting}
porque el valor de retorno de \emph{wc} incluye espacios en blanco iniciales, lo que provoca la división de palabras. \emph{ksh} maneja la asignación de manera especial, como una sola palabra.
\end{itemize}
**Tabla A-1. Equivalentes de patrones ksh/zsh**
\begin{table}[h]
\center
\caption{Equivalentes de patrones \emph{ksh/zsh}}
\label{Tab:A-1}
\begin{tabular}{|m{2cm}|m{2cm}|m{11cm}|} \hline
\textbf{ksh} & \textbf{zsh} & \textbf{Significado} \\ \hline
!(foo) & \^{}foo & Cualquier cosa menos \emph{foo} \\\hline
& foo1\~{}foo2 & Cualquier cosa que coincida con foo1 pero no con foo2.\tablefootnote{Ten en cuenta que \~{} es el único operador de globbing que tiene una precedencia inferior a /. Por ejemplo, \texttt{**/foo\~{}*bar*} coincide con cualquier archivo en un subdirectorio llamado \emph{foo}, excepto donde \emph{bar} ocurrió en alguna parte de la ruta (por ejemplo, \texttt{users/barstaff/foo} será excluido por el operador \~{}). Como el operador \texttt{**} no puede agruparse (dentro de paréntesis se trata como *), esta es la manera de excluir algunos subdirectorios de hacer coincidir un \texttt{**}.} \\\hline
@(foo1|foo2|...) & (foo1|foo2|...) & \emph{foo1} o \emph{foo2} o ... \\\hline
?(foo) & (foo|) & Cero o una ocurrencia de \emph{foo} \\\hline
*(foo) & (foo)\# & Cero o más ocurrencias de \emph{foo} \\\hline
+(foo) & (foo)\#\# & Uno o más ocurrencias de \emph{foo} \\\hline
\end{tabular}
\end{table}
\emph{Ejecución de comandos}
\begin{itemize}
\item \nmid{} No hay variable \texttt{ENV} (usa \texttt{/etc/zshrc}, \texttt{\~{}/.zshrc}; también nota \texttt{\$ZDOTDIR}).
\item \texttt{\$PATH} no se busca para comandos especificados en la invocación sin \texttt{-c}.
\end{itemize}
\emph{Alias y funciones}
\begin{itemize}
\item El orden en que se definen los alias y las funciones es significativo: las definiciones de funciones con () expanden alias.
\item Los alias y las funciones no se pueden exportar.
\item No hay alias rastreados: el hashing de comandos reemplaza esto.
\item El uso de alias para la vinculación de teclas se reemplaza por \emph{bindkey}.
\item \nmid{} Las opciones no son locales para las funciones (usa \texttt{LOCAL\_OPTIONS}; nota que esto siempre puede no estar configurado localmente para propagar la configuración de opciones desde una función hasta el nivel de llamada).
\item Las funciones definidas con \texttt{function funcname \{ body ;\}} se comportan de la misma manera que las definidas con \texttt{funcname () \{ body ;\}}. En \emph{ksh93}, solo las primeras se comportan como funciones verdaderas, y las segundas se comportan como si el cuerpo se leyera desde un archivo con el comando punto.
\end{itemize}
\emph{Trampas y señales}
\begin{itemize}
\item \nmid{} Las trampas no son locales para las funciones. La opción \texttt{LOCAL\_TRAPS} está disponible desde 3.1.6.
\item \texttt{TRAPERR} se convierte en \texttt{TRAPZERR} (esto fue forzado por \texttt{UNICOS} que tiene \texttt{SIGERR}).
\end{itemize}
\emph{Edición}
\begin{itemize}
\item Las opciones \emph{emacs, gmacs} y \emph{viraw} no son compatibles. Usa \emph{bindkey} para cambiar el comportamiento de edición: \texttt{set -o emacs} se convierte en \texttt{bindkey -e} y \texttt{set -o vi} se convierte en \texttt{bindkey -v}; para \emph{gmacs}, ve a \emph{emacs-mode} y usa \texttt{bindkey \textbackslash\^{}t gosmacs-transpose-characters}.
\item La opción de \emph{palabra clave} no existe y \texttt{set -k} es en su lugar \emph{interactivecomments}.
\item \nmid{} La gestión de historiales en múltiples shells es diferente: la lista de historiales no se guarda y restaura después de cada comando. La opción \texttt{SHARE\_HISTORY} apareció en 3.1.6 y se establece en el modo de compatibilidad con ksh para remediar esto.
\item \textbackslash{} no escapa caracteres de edición (usa CTRL-V).
\item No se establecen todos los enlaces de \emph{ksh} (por ejemplo, ESC \#; prueba ESC q).
\item \nmid{} \# en una shell interactiva no se trata como un comentario por defecto.
\end{itemize}
\emph{Comandos incorporados}
\begin{itemize}
\item Algunos comandos incorporados (\emph{r, autoload, history, integer,} ...) son alias en \emph{ksh}.
\item No hay un comando incorporado \emph{newgrp}: usa \texttt{alias newgrp="exec newgrp"}.
\item \emph{jobs} no tiene una bandera \texttt{-n}.
\item \emph{read} no tiene una bandera \texttt{-s}.
\end{itemize}
\emph{Otras idiosincrasias}
\begin{itemize}
\item \texttt{select} siempre vuelve a mostrar la lista de selecciones en cada bucle.
\end{itemize}
\section{Sustitutos en Plataformas PC}
La proliferación del shell Korn no se detuvo en los límites de Unix. Muchos programadores que obtuvieron su experiencia inicial en sistemas Unix y posteriormente cruzaron al mundo de PC deseaban un entorno agradable tipo Unix (especialmente cuando se enfrentaban a los horrores de la línea de comandos MS-DOS), así que no sorprende que hayan aparecido varias interfaces tipo shell Unix para sistemas operativos de pequeñas computadoras, entre ellas, emulaciones del shell Korn.
En los últimos años, no solo han aparecido clones de shell, sino entornos Unix <<completo>>. Dos de ellos utilizan shells que ya hemos discutido. Dos proporcionan sus propias re-implementaciones del shell. Proporcionar listas de diferencias mayores y menores es contraproducente. En cambio, esta sección describe cada entorno a su vez (en orden alfabético), junto con información de contacto y descarga en Internet.
\subsection{Cygwin}
Cygnus Consulting (ahora Red Hat) creó el entorno \emph{Cygwin}. Crearon primero \emph{cgywin.dll}, una biblioteca compartida que proporciona emulación de llamadas al sistema Unix, y luego trasladaron un gran número de utilidades GNU a varias versiones de Microsoft Windows. La emulación incluye redes TCP/IP con la API de sockets de Berkeley. La mayor funcionalidad se encuentra en Windows/NT, Windows 2000 y Windows XP, aunque el entorno puede funcionar en Windows 95/98/ME también.
El entorno \emph{Cygwin} utiliza \emph{bash} como su shell, GCC como su compilador C y el resto de las utilidades GNU para su conjunto de herramientas Unix. Un comando de montaje sofisticado proporciona un mapeo de la notación de ruta de Windows \texttt{C:\textbackslash{}path} a nombres de archivos Unix.
El punto de partida para el proyecto \emph{Cygwin} es \url{http://www.cygwin.com}. Lo primero que debes descargar es un programa instalador. Al ejecutarlo, eliges qué paquetes adicionales deseas instalar. La instalación es completamente basada en Internet; no hay CD oficiales de \emph{Cygwin}, al menos no de los mantenedores del proyecto.
\subsection{DJGPP}
El conjunto DJGPP proporciona herramientas GNU de 32 bits para el entorno MS-DOS. Para citar la página web:
\begin{itemize}
\item DJGPP es un sistema de desarrollo C/C++ completo de 32 bits para PC Intel 80386 (y superiores) que ejecutan MS-DOS. Incluye puertos de muchas utilidades de desarrollo GNU. Las herramientas de desarrollo requieren una computadora 80386 o más nueva para ejecutarse, al igual que los programas que producen. En la mayoría de los casos, los programas que produce se pueden vender comercialmente sin licencia ni regalías.
\item El nombre proviene de las iniciales de D.J. Delorie, quien portó el compilador GNU C++, g++, a MS-DOS, y las iniciales de texto de g++, GPP. Se convirtió esencialmente en un entorno Unix completo en la parte superior de MS-DOS, con todas las herramientas GNU y \emph{bash} como su shell. A diferencia de \emph{Cygwin} o UWIN (ver más adelante en este capítulo), no necesitas una versión de Windows, solo un procesador de 32 bits completo y MS-DOS. (Aunque, por supuesto, puedes usar DJGPP desde una ventana MS-DOS de Windows). El sitio web es \url{http://www.delorie.com/djgpp/}.
\end{itemize}
\subsection{MKS Toolkit}
Quizás el entorno Unix más establecido para el mundo de las PC es el MKS Toolkit de Mortice Kern Systems:
\begin{footnotesize}
\singlespace
MKS Canada - Sede Corporativa \\
410 Albert Street \\
Waterloo, ON N2L 3V3 \\
Canadá \\
(519) 884-2251 \\
(519) 884-8861 (fax) \\
(800) 265-2797 (ventas) \\
\url{http://www.mks.com}
\end{footnotesize}
MKS Toolkit viene en varias versiones dependiendo del entorno de desarrollo y la cantidad de desarrolladores que lo utilizarán. Incluye un shell que cumple con POSIX, junto con casi todas las características del shell Korn de 1988, así como más de 300 utilidades como \emph{awk, perl, vi, make,} y demás. Su biblioteca admite más de 1500 API de Unix, haciéndola extremadamente completa y facilitando la portabilidad al entorno Windows. Más información está disponible en \url{http://www.mkssoftware.com/products/tk/ds_tkpdev.asp}.
\subsection{Thompson Automation Software Toolkit}
Thompson Automation Software proporciona el Thompson Toolkit, que incluye un shell y más de 100 utilidades. El kit está disponible para MS-DOS 2.1 y superior, OS/2 1.2 o WARP y para Microsoft Windows 95 y superior. La información de contacto es:
\begin{footnotesize}
\singlespace
Thompson Automation Software \\
5616 SW Jefferson \\
Portland, OR 97221 \\
1-800-944-0139 (EE. UU. y Canadá) \\
1-503-224-1639 (internacional/local) \\
1-503-224-3230 (fax) \\
sales@tasoft.com \\
\url{http://www.tasoft.com/toolkit.html/}
\end{footnotesize}
El software de Thompson es conocido por su implementación de \emph{awk}, que es rápida y confiable, con muchas extensiones poderosas al lenguaje awk. El shell del toolkit es compatible con POSIX y la versión 1988 del shell Korn.
\subsection{AT\&T UWIN}
El paquete UWIN es un proyecto de David Korn y sus colegas para poner un entorno Unix disponible bajo Microsoft Windows. Es similar en estructura a \emph{Cygwin}, discutido anteriormente. Una biblioteca compartida, \emph{posix.dll}, proporciona emulación de las APIs de llamadas al sistema Unix. La emulación de llamadas al sistema es bastante completa. Un giro interesante es que el registro de Windows se puede acceder como un sistema de archivos bajo \texttt{/reg}. Sobre la emulación de la API de Unix, se han compilado y ejecutado \emph{ksh93} y más de 200 utilidades Unix (o más bien, re-implementaciones). El entorno UWIN depende del compilador nativo de Microsoft Visual C/C++, aunque las herramientas de desarrollo GNU están disponibles para descargar y usar con UWIN.
\url{http://www.research.att.com/sw/tools/uwin/} es la página web del proyecto. Describe lo que está disponible, con enlaces para descargar binarios, así como información sobre la licencia comercial del paquete UWIN. También se incluyen enlaces a varios documentos sobre UWIN, software adicional útil y enlaces a otros paquetes similares.
La ventaja más notable del paquete UWIN es que su shell es el auténtico \emph{ksh93}. Por lo tanto, la compatibilidad con la versión Unix de \emph{ksh93} no es un problema.

813
Secciones/ApendiceB.tex Normal file
View File

@ -0,0 +1,813 @@
Este apéndice contiene listas de referencia para opciones de invocación, comandos incorporados y palabras clave, alias predefinidos, variables de shell incorporadas, operadores de \emph{prueba}, opciones de shell, opciones de \emph{typeset}, aritmética, comandos en modo \emph{emacs} y comandos de control en modo \emph{vi}. Además, describe cómo utilizar todas las facilidades del comando incorporado \emph{getopts}.
\section{Opciones de invocación}
Esta es una lista de las opciones que puede usar cuando invoque al shell Korn. Además de éstas, se puede utilizar cualquier opción establecida en la línea de órdenes; consulte la tabla de opciones más adelante en este apéndice. Los intérpretes de comandos de inicio de sesión suelen invocarse con las opciones \texttt{-i} (interactivo), \texttt{-s} (leer de la entrada estándar) y \texttt{-m} (habilitar el control de trabajos).
\begin{table}[h]
\center
\begin{tabular}{|m{2cm}|m{13cm}|} \hline
\textbf{Opción} & \textbf{Significado} \\ \hline
-c \emph{string} & Ejecuta \emph{string} y después sale \\\hline
-D & Imprime todas las cadenas \texttt{\$"..."} del script. Esto se utiliza para crear una base de datos de traducciones específicas de cada idioma de las cadenas de un script. \\\hline
-i & Shell interactivo. Ignora las señales TERM, INTR y QUIT. \\\hline
-r & Shell restringido. Ver \hyperref[sec:Chapter10]{Capítulo 10} \\\hline
-R \emph{filename} & Crea una base de datos de referencias cruzadas para definiciones de variables y comandos en \emph{filename}. No se puede compilar. \\\hline
-s & Leer comandos de la entrada estándar. Si se proporciona un argumento, esta bandera tiene prioridad (es decir, el argumento no se tratará como un nombre de script y se leerá la entrada estándar). \\\hline
\end{tabular}
\end{table}
\section{Comandos integrados y palabras clave}
He aquí un resumen de todos los comandos y palabras clave incorporados.
\begin{longtable}[h]{|p{2cm}|p{3.5cm}|p{2cm}|p{7.5cm}|} \hline
\textbf{Nombre} & \small{\textbf{Comando / Palabra Clave}} & \textbf{Capítulo} & \textbf{Resumen} \\\hline
\endfirsthead
\hline
\textbf{Nombre} & \small{\textbf{Comando / Palabra Clave}} & \textbf{Capítulo} & \textbf{Resumen} \\\hline
\endhead
! & Palabra clave & 5 & Invierte el resultado verdadero/falso de la siguiente tubería. \\\hline
: & Comando & 7 & No hacer nada (sólo hacer expansiones de argumentos). \\\hline
. & Comando & 4 & Leer archivo y ejecutar su contenido en el shell actual. \\\hline
alias & Comando & 3 & Establecer la abreviatura de comando o línea de comandos. \\\hline
bg & Comando & 8 & Poner trabajo en segundo plano. \\\hline
builtin & Comando & & Añade o elimina comandos incorporados; imprime información sobre ellos. \\\hline
break & Comando & 5 & Salir del bucle \texttt{for, select, while} o \texttt{until} circundante. \\\hline
case & Comando & 5 & Construcción condicional multidireccional. \\\hline
cd & Comando & 1 & Cambiar el directorio de trabajo. \\\hline
command & Comando & 5 & Localizar comandos integrados y externos; encontrar un comando integrado en lugar de una función con el mismo nombre. \\\hline
continue & Comando & 4 & Salta a la siguiente iteración del bucle \texttt{for, select, while} o \texttt{until}. \\\hline
disown & Comando & 8 & Desasociar un trabajo en segundo plano del shell actual. El efecto es que el trabajo no recibe la señal HUP cuando el shell sale. \\\hline
echo & Comando & 4 & Expandir e imprimir argumentos (obsoleto). \\\hline
exec & Comando & 9 & Sustituye el shell por el programa dado. \\\hline
exit & Comando & 5 & Salida del shell. \\\hline
export & Comando & 3 & Crear variables de entorno. \\\hline
eval & Comando & 7 & Procesa los argumentos como una línea de comandos. \\\hline
false & Comando & 8 & No hacer nada y salir sin éxito. Útil para hacer bucles infinitos. \\\hline
fg & Comando & 8 & Poner el trabajo de fondo en primer plano. \\\hline
for & Palabra clave & 5 & Construcción en bucle. \\\hline
function & Palabra clave & 4 & Definir función \\\hline
getconf & Comando & & Obtener información específica del sistema. Los parámetros están definidos por POSIX. \\\hline
getopts & Comando & 6 & Opciones de la línea de comandos del proceso. \\\hline
hist & Palabra clave & 2 & Trabajar con el historial de comandos. \\\hline
if & Comando & 1 & Construcción condicional. \\\hline
jobs & Comando & 1 & Enumera los trabajos de fondo. \\\hline
kill & Comando & 8 & Enviar señal al proceso. \\\hline
let & Comando & 6 & Asignación aritmética de variables. \\\hline
newgrp & Comando & & Iniciar nuevo shell con nuevo ID de grupo (obsoleto). \\\hline
print & Comando & 1 & Expande e imprime los argumentos en la salida estándar. \\\hline
printf & Comando & 7 & Expande e imprime los argumentos en la salida estándar, utilizando los especificadores de formato \emph{printf(3)} de ANSI C. \\\hline
pwd & Comando & 1 & Imprimir directorio de trabajo. \\\hline
read & Comando & 7 & Leer una línea de la entrada estándar. \\\hline
readonly & Comando & 5 & Hacer que las variables sean de sólo lectura (no asignables). \\\hline
return & Comando & 5 & Retorno desde función o script circundante. \\\hline
select & Palabra clave & 5 & Constructo de generación de menús. \\\hline
set & Comando & 3 & Configura las opciones. \\\hline
shift & Comando & 6 & Cambia los argumentos de la línea de comandos. \\\hline
sleep & Comando & 8 & Suspende la ejecución durante el número de segundos indicado. \\\hline
test & Comando & 5 & Versión antigua del programa de prueba condicional. Utilice \texttt{[[...]]} en su lugar. \\\hline
time & Palabra clave & & Ejecuta el comando e imprime los tiempos de ejecución. Por sí mismo, imprime los tiempos acumulados para el shell y todos los hijos. \\\hline
trap & Comando & 8 & Establezca una rutina de captación de señales. \\\hline
true & Comando & 8 & No hacer nada y salir con éxito. Útil para hacer bucles infinitos. \\\hline
typeset & Comando & 6 & Establecer características especiales de variables y funciones. \\\hline
ulimit & Comando & 10 & Establecer/mostrar los límites de recursos del proceso. \\\hline
umask & Comando & 10 & Establecer/mostrar máscara de permisos de archivo. \\\hline
unalias & Comando & 3 & Eliminar definiciones de alias. \\\hline
unset & Comando & 3 & Eliminar definiciones de variables o funciones. \\\hline
until & Palabra clave & 5 & Construcción en bucle. \\\hline
wait & Comando & 8 & Espere a que termine(n) la(s) tarea(s) en segundo plano. \\\hline
whence & Comando & 3 & Identificar la fuente de mando. \\\hline
while & Palabra clave & 5 & Construcción en bucle. \\\hline
\end{longtable}
Las asignaciones para los comandos \emph{alias, export, readonly} y \emph{typeset} se procesan como asignaciones de variables, en el sentido de que la expansión de tilde se realiza después del carácter =, y la división de campos no se realiza en ninguna sustitución de variable en el valor que se asigna.
\section{Alias predefinidos}
Hay una serie de alias predefinidos, es decir, incorporados automáticamente a \emph{ksh} en tiempo de compilación. Están listados en la siguiente tabla. Tenga en cuenta que algunos de ellos se definen con un carácter de espacio al final. Esto permite la expansión del alias en la palabra que sigue al alias en la línea de comandos.
\begin{table}[h]
\center
\begin{tabular}{|m{2cm}|m{2cm}|m{11cm}|} \hline
\textbf{Nombre} & \textbf{Capítulo} & \textbf{Valor completo} \\ \hline
autoload & 4, 6 & \texttt{alias autoload='typeset -fu'} \\\hline
command & 7 & \texttt{alias command='command '} \\\hline
fc & 2 & \texttt{alias fc=hist} \\\hline
float & 6 & \texttt{alias float='typeset -E'} \\\hline
functions & 6 & \texttt{alias functions='typeset -f'} \\\hline
hash & 3 & \texttt{alias hash='alias -t --'} \\\hline
history & 2 & \texttt{alias history='hist -l'} \\\hline
integer & 6 & \texttt{alias integer='typeset -i'} \\\hline
nameref & 4 & \texttt{alias nameref='typeset -n'} \\\hline
nohup & 3,8 & \texttt{alias nohup='nohup '} \\\hline
r & 2 & \texttt{alias r='hist -s'} \\\hline
redirect & 9 & \texttt{alias redirect='command exec'} \\\hline
stop & 8 & \texttt{alias stop='kill -s STOP'} \\\hline
times & & \texttt{alias time='\{ \{time;\} 2>\&1;\}'} \\\hline
type & 4 & \texttt{alias type='whence -v'} \\\hline
\end{tabular}
\end{table}
\section{Variables de Shell incorporadas}
He aquí un resumen de todas las variables de shell incorporadas:
\begin{longtable}[h]{|p{3cm}|p{2cm}|p{10cm}|} \hline
\textbf{Variable} & \textbf{Capítulo} & \textbf{Significado}\\ \hline
\endfirsthead
\hline
\textbf{Variable} & \textbf{Capítulo} & \textbf{Significado}\\ \hline
\endhead
\# & 4 & Número de argumentos dados al proceso actual. \\\hline
0 & 4 & Argumentos de línea de comandos para el proceso actual. Dentro de comillas dobles, se expande a argumentos individuales. \\\hline
* & 4 & Argumentos de línea de comandos para el proceso actual. Dentro de comillas dobles, se expande a un único argumento. \\\hline
- (guión) & & Opciones dadas al shell en la invocación. \\\hline
? & 5 & Estado de salida del comando anterior. \\\hline
\$ & 8 & ID de proceso del proceso shell. \\\hline
\_ (guón bajo) & 3 & Dentro de \texttt{\$MAILPATH:} el nombre de archivo que activó el mensaje <<tienes correo>>. En la línea de comandos: último argumento del comando anterior. Dentro de un script: la ruta completa utilizada para encontrar y ejecutar el script. \\\hline
! & 8 & ID de proceso del último comando en segundo plano. \\\hline
.sh.edchar & 10 & Caracteres introducidos al procesar una trampa KEYBD. \\\hline
.sh.edcol & 10 & Posición del cursor en la trampa KEYBD más reciente. \\\hline
.sh.edmode & 10 & Igual a ESC en modo \emph{vi}, vacío en caso contrario. \\\hline
.sh.edtext & 10 & Caracteres en el búfer de entrada durante una trampa KEYBD. \\\hline
.sh.match & 4 & Variable de matriz con texto que coincide con el patrón en la sustitución de variables. (A partir de \emph{ksh93l}.) \\\hline
.sh.name & 9 & Nombre de una variable que ejecuta una función de disciplina. \\\hline
.sh.subscript & 9 & Subíndice de una variable de matriz que ejecuta una función de disciplina. \\\hline
.sh.value & 9 & Valor de la variable que ejecuta una función de disciplina. \\\hline
.sh.version & 4, 9 & Versión de \emph{ksh}. \\\hline
CDPATH & 3 & Lista de directorios en los que debe buscar el comando \emph{cd}. \\\hline
COLUMNS & 3 & Anchura de visualización en columnas (para los modos de edición y \texttt{select}). \\\hline
EDITOR & 2 & Se utiliza para establecer el modo de edición; también lo utilizan \emph{mail} y otros programas. Anulado por \texttt{VISUAL}, si está configurado. \\\hline
EMPV & 3 & Nombre del archivo que se ejecutará como archivo de entorno cuando se invoque al shell. \\\hline
FCEDIT & 2 & Editor por defecto obsoleto para el comando \emph{hist}. \\\hline
FIGNORE & 1 & Patrón para los archivos a ignorar durante la expansión del patrón. \\\hline
FPATH & 4 & Ruta de búsqueda de funciones autocargadas. \\\hline
HISTCMD & 2 & Número del comando actual en el historial de comandos. \\\hline
HISTEDIT & 2 & Editor por defecto para el comando \emph{hist} \\\hline
HISTFILE & 2 & Nombre del archivo de historial de comandos. \\\hline
HISTSIZE & 2 & Número de líneas guardadas en el archivo histórico. \\\hline
HOME & 3 & Directorio de inicio (login). \\\hline
IFS & 7 & Separador de campo interno: lista de caracteres que actúan como separadores de palabras. Normalmente se establece en espacio, TAB y nueva línea. \\\hline
LANG & & Nombre por defecto de la configuración regional actual; reemplazado por otras variables LC\_*. \\\hline
LC\_ALL & & Nombre de la configuración regional actual; sustituye a LANG y a las demás variables LC\_*. \\\hline
LC\_COLLATE & & Nombre de la configuración regional actual para la clasificación de caracteres. \\\hline
LC\_CTYPE & & Nombre de la configuración regional actual para determinar la clase de caracteres durante la comparación de patrones; véase el \hyperref[sec:Chapter4]{Capítulo 4}. \\\hline
LC\_NUMERIC & & Nombre de la configuración regional actual para el formato de los números (punto decimal, separador de miles). \\\hline
LINEO & 9 & Número de línea del script o función que se acaba de ejecutar. \\\hline
LINES & 3 & Altura de la pantalla en líneas (para el comando \texttt{select}). \\\hline
MAIL & 3 & Nombre del archivo para comprobar si hay correo nuevo. \\\hline
MAILCHECK & 3 & Frecuencia (en segundos) con la que se comprueba si hay correo nuevo. \\\hline
MAILPATH & 3 & Lista de nombres de archivo para comprobar si hay correo nuevo, si MAIL no está configurado. \\\hline
OLDPWD & 3 & Directorio de trabajo anterior. \\\hline
OPTARG & 6 & Argumento de la opción procesada por \emph{getopts}. \\\hline
OPTIND & 6 & Número del primer argumento después de las opciones. \\\hline
PATH & 3 & Ruta de búsqueda de comandos. \\\hline
PPID & 8 & ID del proceso padre. \\\hline
PS1 & 3 & String del símbolo del sistema principal. \\\hline
PS2 & 3 & String de solicitud de continuación de línea. \\\hline
PS3 & 5 & String de consulta para el comando \texttt{select}. \\\hline
PS4 & 9 & String de consulta para la opción \texttt{xtrace}. \\\hline
PWD & 3 & Directorio de trabajo actual. \\\hline
RANDOM & 9 & Número aleatorio entre 0 y 32767 ($2^15-1$). \\\hline
REPLY & 5, 7 & Respuesta del usuario al comando \texttt{select}; resultado del comando \emph{read} si no se dan nombres de variables. \\\hline
SECONDS & 3 & Número de segundos transcurridos desde que se invocó al shell. \\\hline
SHELL & 3 & Ruta completa de los programas shell que se deben utilizar para ejecutar comandos. \\\hline
TMOUT & 5, 7, 10 & Si se establece en un número entero positivo, número de segundos entre comandos tras los cuales el shell termina automáticamente. También se aplica a la lectura de respuestas a \texttt{select} y \emph{read}. \\\hline
VISUAL & 2 & Permite establecer el modo de edición. \\\hline
\end{longtable}
\section{Operadores de prueba}
Son los operadores que se utilizan con la construcción \texttt{[[...]]}. Pueden combinarse lógicamente con \texttt{\&\&} (\texttt{"}y\texttt{"}) y \texttt{||} (\texttt{"}o\texttt{"}) y agruparse con paréntesis. Cuando se utilizan con nombres de archivo de la forma \texttt{/dev/fd/N}, comprueban el atributo correspondiente del descriptor de archivo abierto \emph{N}.
\begin{longtable}{|p{4cm}|p{11cm}|} \hline
\textbf{Operador} & \textbf{Verdadero si} \\ \hline
\endfirsthead
\hline
\textbf{Operador} & \textbf{Verdadero si} \\ \hline
\endhead
-a file & \emph{file} existe (Obsoleto. \texttt{-e} es preferido) \\\hline
-b file & \emph{file} es un dispositivo de bloque \\\hline
-c file & \emph{file} es un dispositivo de caracteres \\\hline
-d file & \emph{file} es un directorio \\\hline
-e file & \emph{file} existe \\\hline
-f file & \emph{file} es un archivo regular \\\hline
-g file & \emph{file} tiene activado el bit de \emph{setgid} \\\hline
-G file & El ID de grupo de \emph{file} es el mismo que el ID de grupo efectivo del intérprete de comandos \\\hline
-h file & \emph{file} es un enlace simbólico \\\hline
-k file & \emph{file} tiene su bit \emph{sticky} activado \\\hline
-l file & \emph{file} es un enlace simbólico. (Solo funciona en sistemas en los que \texttt{/bin/test -l} comprueba si hay enlaces simbólicos \\\hline
-L file & \emph{file} es un enlace simbólico \\\hline
-n string & strin no es nulo \\\hline
-o option & Option está activado \\\hline
-O file & \emph{file} es propiedad del ID de usuario efectivo del intérprete de comandos \\\hline
-p file & \emph{file} es una tubería o una tubería con nombre ( archivo FIFO) \\\hline
-r file & \emph{file} tiene permisos de lectura \\\hline
-s file & \emph{file} no está vacío \\\hline
-S file & \emph{file} es un socket \\\hline
-t file & El descriptor de fichero \emph{N} apunta a una terminal \\\hline
-u file & \emph{file} tiene activado el bit \emph{setuid} \\\hline
-w file & \emph{file} tiene permisos de escritura \\\hline
-x file & \emph{file} tiene permisos de ejecución, o el archivo es un directorio en el que se puede buscar \\\hline
-z string & \emph{string} es nulo \\\hline
fileA -nt fileB & fileA es más reciente que file B, o fileB no existe \\\hline
fileA -ot fileB & fileA es más antiguo que fileB, o fileB no existe \\\hline
fileA -ef fileB & fileA y fileB apuntan al mismo archivo \\\hline
string = pattern & \emph{string} coincide con el patrón (que puede contener comodines). Obsoleto; se prefiere == \\\hline
string == pattern & \emph{String} coincide con el patrón (que puede contener comodines) \\\hline
string != pattern & String no coincide con el patrón \\\hline
stringA < stringB & \emph{StringA} está antes que \emph{StringB} en el orden del diccionario \\\hline
stringA > stringB & \emph{StringA} va después que \emph{StringB} en el orden del diccionario \\\hline
exprA -eq expB & Las expresiones aritméticas \emph{exprA} y \emph{exprB} son iguales \\\hline
exprA -ne exprB & Las expresiones aritméticas \emph{exprA} y \emph{exprB} no son iguales \\\hline
exprA -lt exprB & \emph{exprA} es menor que \emph{exprB} \\\hline
exprA -gt exprB & \emph{exprA} es mayor que \emph{exprB} \\\hline
exprA -le exprB & \emph{exprA} es menor o igual que \emph{exprB} \\\hline
exprA -ge exprB & \emph{exprA} es mayor o igual que \emph{exprB} \\\hline
\end{longtable}
Los operadores \emph{-eq, -ne, -lt, -le, -gt} y \emph{-ge} se consideran obsoletos en \emph{ksh93}; en su lugar debe utilizarse el comando \emph{let} o \texttt{((...))}.
Para =, == y !=, cite el patrón para realizar comparaciones literales de strings.
\section{Opciones}
Son opciones que pueden activarse con el comando \texttt{set -o}. Todas están inicialmente desactivadas excepto donde se indique lo contrario. Las abreviaturas, cuando aparecen, son opciones de set que pueden utilizarse en lugar de la orden \texttt{set -o} completa (por ejemplo, \texttt{set -a} es una abreviatura de \texttt{set -o allexport}). En la mayoría de los casos, las abreviaturas son opciones del shell Bourne compatibles con versiones anteriores. Para desactivar una opción, utilice \texttt{set +o nombre largo} o \texttt{set +X}, donde \emph{nombre largo} y \emph{X} son la <<forma larga>> o la forma de <<un solo carácter>> de la opción, respectivamente.
\begin{longtable}[h]{|p{2cm}|p{2.5cm}|p{11.5cm}|} \hline
\textbf{Opción} & \small{\textbf{Abreviatura}} & \textbf{Significado} \\ \hline
\endfirsthead
\hline
\textbf{Opción} & \small{\textbf{Abreviatura}} & \textbf{Significado} \\ \hline
\endhead
allexport & -a & Exporta todas las variables definidas posteriormente. \\\hline
bgnice & & Ejecuta todos los trabajos en segundo plano con prioridad reducida (activada por defecto). \\\hline
emacs & & Utilice la edición de línea de comandos estilo Emacs. \\\hline
errexit & -e & Salir del shell cuando un comando sale con un estado distinto de cero. \\\hline
gmacs & & Utiliza la edición de línea de comandos al estilo de Emacs, pero con un significado ligeramente diferente para CTRL-T (véase el capítulo 2). \\\hline
ignoreeof & & No permitir CTRL-D para salir del shell. \\\hline
keyword & -k & Ejecutar asignaciones en medio de líneas de comando. (Muy obsoleto.) \\\hline
markdirs & & Añade \texttt{/} a todos los nombres de directorio generados a partir de la expansión de comodines. \\\hline
monitor & -m & Activar el control de trabajos (activado por defecto). \\\hline
noclobber & -c & No permitir a > la redirección a archivos existentes. \\\hline
noexec & -n & Leer los comandos y comprobar si hay errores de sintaxis, pero no los ejecuta. \\\hline
noglob & -f & Desactivar la expansión de comodines. \\\hline
nolog & & Desactivar el historial de comandos para las definiciones de funciones. \\\hline
notify & -b & Imprime mensajes de finalización de trabajo inmediatamente, en lugar de esperar a la siguiente solicitud. \\\hline
nounset & -u & Tratar las variables indefinidas como errores, no como nulos. \\\hline
pipefail & & Espera a que se completen todos los trabajos de una cadena. El estado de salida es el del último comando que falló, o cero en caso contrario. (\emph{ksh93g} y posteriores). \\\hline
privileged & -p & El script se ejecuta en modo \emph{suid}. \\\hline
trackall & -h & Crea un alias para cada ruta completa encontrada en una búsqueda de comandos. (\emph{ksh93} ignora esta opción; el comportamiento es siempre activado, incluso si esta opción está desactivada). \\\hline
verbose & -v & Imprime los comandos (textualmente) antes de ejecutarlos. \\\hline
vi & & Utilice la edición de línea de comandos estilo \emph{vi}. \\\hline
viraw & & Utilice el modo \emph{vi} y haga que cada pulsación de tecla tenga efecto inmediatamente. (Esto es necesario en algunos sistemas muy antiguos para que el modo \emph{vi} funcione, y es necesario en todos los sistemas para poder usar TAB para completar. A partir de \emph{ksh93n}, se habilita automáticamente cuando se usa \emph{vi-mode}). \\\hline
xtrace & -x & Imprime los comandos (después de las expansiones) antes de ejecutarlos. \\\hline
\end{longtable}
El comando set tiene algunas opciones adicionales que no tienen sus correspondientes versiones set -o, como se indica a continuación:
\begin{table}[h]
\center
\begin{tabular}{|m{2cm}|m{13cm}|} \hline
\textbf{Opción} & \textbf{Significado} \\ \hline
set -A ... & Asignación de matrices indexadas. \\\hline
set -s & Ordena los parámetros posicionales. \\\hline
set -t & Leer y ejecutar un comando, y luego salir. (Obsoleto.) \\\hline
\end{tabular}
\end{table}
\section{Opciones tipográficas}
Estas son las opciones del comando \emph{typeset}. Utilice \texttt{+option} para desactivar una opción, por ejemplo, \texttt{typeset +x foo} para dejar de exportar la variable foo.
\begin{longtable}[h]{|p{2.5cm}|p{12.5cm}|} \hline
\textbf{Opción} & \textbf{Significado} \\ \hline
\endfirsthead
\hline
\textbf{Opción} & \textbf{Significado} \\ \hline
\endhead
& Sin opción, crear variable local dentro de la función. \\\hline
-A & Declarar variable como array asociativo. \\\hline
-E[\emph{n}] & Declara la variable como un número de punto flotante. Opcional \emph{n} es el número de cifras significativas. \\\hline
-F[\emph{n}] & Declara la variable como un número de punto flotante. Opcional \emph{n} es el número de dígitos significativos. \\\hline
-f & Sin argumentos, imprime todas las definiciones de funciones. \\\hline
-f \emph{fname} & Imprime la definición de la función \emph{fname}. \\\hline
+f & Imprime todos los nombres de las funciones. \\\hline
-ft & Activar el modo de rastreo para la(s) función(es) nombrada(s). \\\hline
+ft & Desactiva el modo de rastreo para la(s) función(es) nombrada(s). \\\hline
-fu & Definir nombre(s) dado(s) como función(es) \emph{autoloaded}. \\\hline
-fx & Obsoleto; no hace nada en \emph{ksh93}. \\\hline
-H & Asignación de nombres de archivo de Unix a host para sistemas no Unix. \\\hline
-i[\emph{n}] & Declara la variable como un entero. Opcional \emph{n} es la base de salida. \\\hline
-l & Convierte todas las letras a minúsculas. \\\hline
-L & Justificar a la izquierda y eliminar los espacios a la izquierda. \\\hline
-n & Declarar variable como \emph{nameref}. \\\hline
-p & Imprime comandos \emph{tipográficos} (\emph{typeset}) para volver a crear variables con los mismos atributos. \\\hline
-r & Hacer que la variable sea de sólo lectura. \\\hline
-R & Justificar a la derecha y eliminar los espacios finales. \\\hline
-t & Etiquetar la variable. (Obsoleto.) \\\hline
-u & Convierte todas las letras a mayúsculas. \\\hline
-ui[\emph{n}] & Declara la variable como un entero sin signo. Opcional \emph{n} es la base de salida. (\emph{ksh93m} y posteriores). \\\hline
-x & Variable de exportación, es decir, poner en el entorno para que se pase a los subprocesos. \\\hline
-Z[\emph{n}] & Justificar a la derecha y rellenar con ceros a la izquierda. \emph{n} es la anchura, o la anchura se establece a partir del valor utilizado en la primera asignación. \\\hline
\end{longtable}
\section{Aritmética}
A partir de \emph{ksh93m}, la instalación de aritmética incorporada comprende un gran porcentaje de las expresiones del lenguaje C. Esto hace que el shell sea más atractivo como un lenguaje de programación completo. Las siguientes características están disponibles:
\emph{Sufijos de tipo al final}\\
Las constantes enteras pueden tener un sufijo \texttt{U} o \texttt{L} al final para indicar que son sin signo o largas, respectivamente. Aunque las versiones en minúsculas también se pueden usar, esto no se recomienda, ya que es fácil confundir una \texttt{l} (ele minúscula) con un 1 (uno).
\emph{Constantes de caracteres en C} \\
Se reconocen las constantes de caracteres individuales en C, entre comillas simples. Al igual que en C, actúan como constantes enteras. Por ejemplo:
\begin{lstlisting}[language=bash]
$ typeset -i c
$ for ((c = 'a'; c <= 'z'; c++))
> do print $c
> done
97
98
99
100
...
\end{lstlisting}
\emph{Constantes octales y hexadecimales} \\
Puedes utilizar el formato C para constantes octales (base 8) y hexadecimales (base 16). Las constantes octales comienzan con un 0 líder, y las constantes hexadecimales comienzan con un \texttt{0x} o \texttt{0X} líder. Por ejemplo:
\begin{lstlisting}[language=bash]
$ print $((010 + 1)) # Octal 10 es decimal 8
9
$ print $((0x10 + 1)) # Hexadecimal 10 es decimal 16
17
\end{lstlisting}
\emph{Aritmética con enteros sin signo} \\
Al usar \texttt{typeset -ui}, puedes crear enteros sin signo. Los enteros regulares representan números positivos y negativos. Los enteros sin signo comienzan en 0, llegan hasta algún valor dependiente de la implementación y luego <<se envuelven>> nuevamente a 0. Del mismo modo, restar 1 de 0 se envuelve en la otra dirección, dando como resultado el número sin signo más grande:
\begin{lstlisting}[language=bash]
$ typeset -ui u=0
$ let u--
$ print $u
4294967295
\end{lstlisting}
\emph{Operadores y precedencia en C} \\
\emph{ksh} admite el conjunto completo de operadores en C, con la misma precedencia y asociatividad. Los operadores se presentaron en detalle en el \hyperref[sec:Chapter6]{Capítulo 6} y se resumen nuevamente a continuación.
\begin{longtable}[h]{|p{5cm}|p{6cm}|p{4cm}|} \hline
\textbf{Operador} & \textbf{Significado} & \textbf{Asociatividad} \\\hline
\endfirsthead
\hline
\textbf{Operador} & \textbf{Significado} & \textbf{Asociatividad} \\\hline
\endhead
\texttt{++ --} & Incremento y decremento, prefijo y postfijo & De izquierda a derecha \\\hline
\texttt{+ - ! \~{}} & Más y menos unarios; negación lógica y por bits & De derecha a izquierda \\\hline
\texttt{**} & Exponenciación \tablefootnote{\emph{ksh93m} y versiones más recientes. El operador ** no está en el lenguaje C.} & De derecha a izquierda \\\hline
\texttt{* ? / \%} & Multiplicación, división y resta & De izquierda a derecha \\\hline
\texttt{+ -} & Suma y resta & De izquierda a derecha \\\hline
\texttt{<< >>} & Desplazamiento de bits a izquierda y derecha & De izquierda a derecha \\\hline
\texttt{< <= > >=} & Comparaciones & De izquierda a derecha \\\hline
\texttt{== !=} & Iguales y no iguales & De izquierda a derecha \\\hline
\texttt{\&} & Bit a bit <<y>> & De izquierda a derecha \\\hline
\texttt{\^{}} & <<O>> exlusivo bit a bit & de Izquierda a derecha \\\hline
\texttt{|} & Bit a bit <<o>> & De izquierda a derecha \\\hline
\texttt{\&\&} & AND lógico & De izquierda a derecha \\\hline
\texttt{||} & OR lógico & De izquierda a derecha \\\hline
\texttt{?:} & Expresión condicional & De derecha a izquierda \\\hline
\texttt{= += -= *= /= \%= \&= \^{}= <<= >>=} & Operadores de asignación & De derecha a izquierda \\\hline
\texttt{,} & Evaluación secuencial & De izquierda a derecha \\\hline
\end{longtable}
\section{Comandos del modo Emacs}
Aquí tienes una lista completa de todos los comandos del modo de edición de emacs. Algunos de estos, como \texttt{ESC [ A,} representan secuencias de teclas de flecha de terminal estándar ANSI; se agregaron para \emph{ksh93h}.
\begin{longtable}[h]{|p{3cm}|p{12cm}|} \hline
\textbf{Comando} & \textbf{Significado} \\\hline
\endfirsthead
\hline
\textbf{Comando} & \textbf{Significado} \\\hline
\endhead
CTRL-A & Mover al principio de la línea \\\hline
CTRL-B & Retroceder un carácter sin borrar \\\hline
CTRL-C & Capitalizar el carácter después del punto \\\hline
CTRL-D & Borrar un carácter hacia adelante \\\hline
CTRL-E & Mover al final de la línea \\\hline
CTRL-F & Avanzar un carácter \\\hline
CTRL-I (TAB) & Completar el nombre de archivo en la palabra actual (a partir de \emph{ksh93h}) \\\hline
CTRL-J & Igual que ENTER. \\\hline
CTRL-K & Borrar (<<kill>>) hacia adelante hasta el final de la línea \\\hline
CTRL-L & Limpiar pantalla \\\hline
CTRL-M & Igual que ENTER \\\hline
CTRL-N & Línea siguiente \\\hline
CTRL-O & Igual que ENTER, luego muestra la línea siguiente en el archivo de historial \\\hline
CTRL-P & Línea anterior \\\hline
CTRL-R & Buscar hacia atrás \\\hline
CTRL-T & Intercambiar los dos caracteres a cada lado del punto \\\hline
CTRL-U & Repetir el siguiente comando cuatro veces \\\hline
CTRL-V & Imprimir la versión del shell Korn \\\hline
CTRL-W & Borrar (<<wipe>>) todos los caracteres entre el punto y la marca \\\hline
CTRL-X CTRL-E & Invocar el programa emacs en el comando actual \\\hline
CTRL-X CTRL-X & Intercambiar el punto y la marca \\\hline
CTRL-Y & Recuperar (<<yank>>) el último elemento eliminado \\\hline
CTRL-] x & Buscar hacia adelante x, donde x es cualquier carácter \\\hline
CTRL-@ & Establecer la marca en el punto \\\hline
DEL & Borrar un carácter hacia atrás \\\hline
CTRL-[ & Igual que ESC (en la mayoría de los teclados) \\\hline
ESC b & Mover una palabra hacia atrás \\\hline
ESC c & Cambiar la palabra después del punto a mayúsculas \\\hline
ESC d & Borrar una palabra hacia adelante \\\hline
ESC f & Mover una palabra hacia adelante \\\hline
ESC h & Borrar una palabra hacia atrás \\\hline
ESC l & Cambiar la palabra después del punto a minúsculas \\\hline
ESC p & Guardar caracteres entre el punto y la marca como si estuvieran borrados \\\hline
ESC CTRL-H & Borrar una palabra hacia atrás \\\hline
ESC CTRL-] x & Buscar hacia atrás x, donde x es cualquier carácter \\\hline
ESC ESPACIO & Establecer la marca en el punto \\\hline
ESC \# & Insertar línea en el archivo de historial para edición futura \\\hline
ESC DEL & Borrar una palabra hacia atrás \\\hline
ESC < & Mover al principio del archivo de historial \\\hline
ESC > & Mover al final del archivo de historial \\\hline
ESC . & Insertar última palabra en la línea de comando anterior después del punto \\\hline
ESC \_ & Igual que arriba \\\hline
ESC ESC & Hacer la completación de nombre de archivo/comando/variable en la palabra actual \\\hline
ESC * & Hacer la expansión de nombre de archivo/comando/variable en la palabra actual \\\hline
ESC = & Hacer la lista de nombre de archivo/comando/variable en la palabra actual \\\hline
ESC [ A & Línea anterior (\emph{ksh93h} y más reciente) \\\hline
ESC [ B & Línea siguiente (\emph{ksh93h} y más reciente) \\\hline
ESC [ C & Mover un carácter hacia adelante (\emph{ksh93h} y más reciente) \\\hline
ESC [ D & Mover un carácter hacia atrás (sin borrar) (\emph{ksh93h} y más reciente) \\\hline
ESC [ H & Mover al principio de la línea (\emph{ksh93h} y más reciente) \\\hline
ESC [ Y & Mover al final de la línea (\emph{ksh93h} y más reciente) \\\hline
Kill & El carácter de eliminación \emph{stty(1)}, a menudo CTRL-U o @ o CTRL-X. Esto borra todo en la línea. Escribirlo dos veces activa el modo <<line feed>>, que emite un carácter de avance de línea para empezar en una nueva línea. Esto es apropiado para terminales solo de papel. \\\hline
\end{longtable}
\section{Comandos del modo de control vi}
Aquí tienes una lista completa de todos los comandos de control en el modo \emph{vi}. Al igual que con los comandos del modo \emph{emacs}, las secuencias como \texttt{[ A} son para las teclas de flecha estándar ANSI y se agregaron para \emph{ksh93h}.
\begin{longtable}[h]{|p{3cm}|p{12cm}|} \hline
\textbf{Comando} & \textbf{Significado} \\\hline
\endfirsthead
\hline
\textbf{Comando} & \textbf{Significado} \\\hline
\endhead
h & Mover a la izquierda un carácter \\\hline
[ & D Mover a la izquierda un carácter (\emph{ksh93h} y más reciente) \\\hline
l & Mover a la derecha un carácter \\\hline
space & Mover a la derecha un carácter \\\hline
[ C & Mover a la derecha un carácter (\emph{ksh93h} y más reciente) \\\hline
w & Mover a la derecha una palabra \\\hline
b & Mover a la izquierda una palabra \\\hline
W & Mover al principio de la siguiente palabra no en blanco \\\hline
B & Mover al principio de la palabra no en blanco precedente \\\hline
e & Mover al final de la palabra actual \\\hline
E & Mover al final de la palabra actual no en blanco \\\hline
0 & Mover al principio de la línea \\\hline
[ H & Mover al principio de la línea (\emph{ksh93h} y más reciente) \\\hline
\^{} & Mover al primer carácter no en blanco en la línea \\\hline
\$ & Mover al final de la línea \\\hline
[ Y & Mover al final de la línea (\emph{ksh93h} y más reciente) \\\hline
i & Insertar texto antes del carácter actual \\\hline
a & Insertar texto después del carácter actual \\\hline
I & Insertar texto al principio de la línea \\\hline
A & Insertar texto al final de la línea \\\hline
r & Reemplazar un carácter (no entra en modo de entrada) \\\hline
R & Sobrescribir el texto existente \\\hline
dh & Eliminar un carácter hacia atrás \\\hline
dl & Eliminar un carácter hacia adelante \\\hline
db & Eliminar una palabra hacia atrás \\\hline
dw & Eliminar una palabra hacia adelante \\\hline
dB & Eliminar una palabra no en blanco hacia atrás \\\hline
dW & Eliminar una palabra no en blanco hacia adelante \\\hline
d\$ & Eliminar hasta el final de la línea \\\hline
d0 & Eliminar hasta el principio de la línea \\\hline
D & Equivalente a d\$ (eliminar hasta el final de la línea) \\\hline
dd & Equivalente a 0d\$ (eliminar toda la línea) \\\hline
C & Equivalente a c\$ (eliminar hasta el final de la línea, entrar en modo de entrada) \\\hline
cc & Equivalente a 0\$ (eliminar toda la línea, entrar en modo de entrada) \\\hline
s & Equivalente a xi (eliminar el carácter actual, entrar en modo de entrada) \\\hline
S & Equivalente a cc (eliminar toda la línea, entrar en modo de entrada) \\\hline
x & Equivalente a dl (eliminar carácter hacia atrás) \\\hline
X & Equivalente a dh (eliminar carácter hacia adelante) \\\hline
k o - & Mover hacia atrás una línea [ A Mover hacia atrás una línea (\emph{ksh93h} y más reciente) \\\hline
j o + & Mover hacia adelante una línea \\\hline
[ B & Mover hacia adelante una línea (\emph{ksh93h} y más reciente) \\\hline
G & Mover a la línea dada por el recuento de repeticiones \\\hline
/string & Buscar hacia adelante la cadena \\\hline
?string & Buscar hacia atrás la cadena \\\hline
n & Repetir la búsqueda hacia adelante \\\hline
N & Repetir la búsqueda hacia atrás \\\hline
f x & Mover a la siguiente ocurrencia de x hacia la derecha \\\hline
F x & Mover a la ocurrencia anterior de x hacia la izquierda \\\hline
t x & Mover a la siguiente ocurrencia de x hacia la derecha, luego hacia atrás un espacio \\\hline
T x & Mover a la ocurrencia anterior de x hacia la izquierda, luego hacia adelante un espacio \\\hline
yh & Yankar un carácter hacia atrás \\\hline
yl & Yankar un carácter hacia adelante \\\hline
yb & Yankar una palabra hacia atrás \\\hline
yw & Yankar una palabra hacia adelante \\\hline
yB & Yankar una palabra no en blanco hacia atrás \\\hline
yW & Yankar una palabra no en blanco hacia adelante \\\hline
y\$ & Yankar hasta el final de la línea \\\hline
y0 & Yankar hasta el principio de la línea \\\hline
Y & Equivalente a y\$ (yankar hasta el final de la línea) \\\hline
yy & Equivalente a 0y\$ (yankar toda la línea) \\\hline
u & Deshacer el último cambio de edición \\\hline
U & Deshacer todos los cambios de edición realizados en la línea \\\hline
. (punto) & Repetir el último comando de edición \\\hline
| & Mover a la posición absoluta de la columna \\\hline
; & Rehacer el último comando de búsqueda de caracteres \\\hline
, & Rehacer el último comando de búsqueda de caracteres, pero en dirección opuesta \\\hline
\% & Mover a la coincidencia de (, ), \{, \}, [, o ] \\\hline
\textbackslash{} & Realizar la completación de nombre de archivo/comando/variable \\\hline
CTRL-I (TAB) & Realizar la completación de nombre de archivo/comando/variable (solo para \texttt{set -o viraw}) (a partir de \emph{ksh93h}) \\\hline
* & Realizar la expansión de nombre de archivo/comando/variable (en la línea de comandos) \\\hline
= & Realizar la expansión de nombre de archivo/comando/variable (como lista impresa) \\\hline
\~{} & Invertir (<<twiddle>>) mayúsculas y minúsculas del carácter actual \\\hline
\_ & Anexar la última palabra del comando anterior, entrar en modo de entrada \\\hline
v & Ejecutar el comando hist en la línea actual (en realidad, ejecutar el comando \texttt{hist -e \$\{VISUAL:-\$\{EDITOR:-vi\}\}}); generalmente esto significa ejecutar \emph{vi} completo en la línea actual \\\hline
CTRL-J & Igual que ENTER \\\hline
CTRL-L & Iniciar una nueva línea y volver a dibujar la línea actual en ella \\\hline
CTRL-M & Igual que ENTER \\\hline
CTRL-V & Imprimir la versión del shell Korn \\\hline
\# & Agregar \# (carácter de comentario) a la línea y enviarlo. Si la línea comienza con \#, quitar el \# inicial y todos los \# iniciales después de cualquier salto de línea incrustado \\\hline
@ x & Insertar expansión de alias \_x como entrada de modo de comando \\\hline
\end{longtable}
\section{Uso de getopts}
El comando \emph{getopts} es extremadamente capaz. Con él, puedes hacer que tus scripts de shell acepten opciones largas, especificar que los argumentos son opcionales o numéricos, y proporcionar descripciones de los argumentos y valores para que las opciones \texttt{-?, --man, --html} y \texttt{--nroff} funcionen de la misma manera para tu programa que para los comandos internos de \emph{ksh93}.
El precio por este poder es la complejidad del <<lenguaje>> de descripción de opciones. Basándonos en una descripción proporcionada por el Dr. Glenn Fowler de AT\&T Research, describimos cómo evolucionaron las facilidades, cómo funcionan y resumimos cómo usarlas en tus propios programas. Usamos el comando getopts extendido en la solución para la \hyperref[box:B-1]{Tarea B-1}.
\begin{mybox}[Tarea B-1]\label{box:B-1}
Diseña el programa \emph{phaser4}, que combina las características de los programas \emph{phaser3} y \emph{tricorder}. Asegúrate de que sea autosuficiente.\footnote{No, las paredes de mi habitación no están cubiertas de pósters de Star Trek. Superé eso hace mucho tiempo, y además, mi esposa no me dejaría de todos modos. ADR.}
\end{mybox}
El primer paso es describir las opciones. Esto se hace con un comentario en la parte superior del script:
\begin{lstlisting}[language=bash]
# uso: phaser4 [ opciones ] archivos
# -k, --kill usar configuración de eliminación (predeterminado)
# -l n, --level n establecer el nivel del phaser (predeterminado = 2)
# -s, --stun usar configuración solo de aturdimiento
# -t [lf], --tricorder [lf] modo tricorder, escaneo opcional para forma de vida lf
\end{lstlisting}
Ahora comienza la diversión. Este esquema de capacidades sigue el orden en que se agregaron funciones a \emph{getopts}.
\begin{enumerate}
\item Comienza con el comando \emph{getopts} como se describe en el \hyperref[sec:Chapter6]{Capítulo 6}. Esto produce una cadena de opciones simple que solo permite opciones de una sola letra:
\begin{lstlisting}[language=bash]
USAGE="kl#st:"
while getopts "$USAGE" optchar ...
\end{lstlisting}
\item Añade una descripción textual para el argumento de opción. Esto se hace encerrando texto arbitrario entre [ y ]:
\begin{lstlisting}[language=bash]
USAGE="kl#[level]st:[life_form]"
while getopts "$USAGE" optchar ...
\end{lstlisting}
\item Permite un valor predeterminado para el argumento de una opción. Esto se hace especificando \texttt{:= value} dentro de la descripción entre corchetes:
\begin{lstlisting}[language=bash]
USAGE="kl#[level:=2]st:[life_form]"
while getopts "$USAGE" optchar ...
\end{lstlisting}
\item Añade ? después de : para indicar un argumento opcional:
\begin{lstlisting}[language=bash]
USAGE="kl#[level:=2]st:?[life_form]"
while getopts "$USAGE" optchar ...
\end{lstlisting}
\item Permite opciones largas que comienzan con \texttt{--}. Esto se hace usando \texttt{[let:long]} en lugar de la única letra de opción:
\begin{lstlisting}[language=bash]
USAGE="[k:kill]"
USAGE+="[l:level]#[level:=2]"
USAGE+="[s:stun]"
USAGE+="[t:tricorder]:?[life_form]"
while getopts "$USAGE" optchar ...
\end{lstlisting}
Aquí, hemos dividido cada opción en su propia línea para facilitar el seguimiento y las hemos concatenado usando el operador de asignación +=. Ten en cuenta que no hay saltos de línea en la cadena.
\item Dentro de los corchetes cuadrados de una letra de opción, permite que siga un texto descriptivo después de un signo de interrogación. Este texto se ignora, al igual que cualquier carácter de espacio en blanco, incluidos los saltos de línea:
\begin{lstlisting}[language=bash]
USAGE="[k:kill?Usar configuración de eliminación (predeterminado).]"
USAGE+="[l:level]#[level:=2?Establecer el nivel del phaser.]"
USAGE+="[s:stun?¡Solo aturdimiento!]"
USAGE+="[t:tricorder?Modo tricorder.]:?[life_form]"
while getopts "$USAGE" optchar ...
\end{lstlisting}
\item Ahora se pone interesante. Los encabezados de sección al estilo de la página man de Unix vienen \emph{antes} de la descripción de la opción. Se distinguen de las descripciones de las opciones al comenzar con un + dentro de corchetes cuadrados:
\begin{lstlisting}[language=bash]
USAGE="[+NOMBRE?phaser4 --- phaser y tricorder combinados]"
USAGE+="[+DESCRIPCIÓN?El programa phaser4 combina la operación "
USAGE+="de los programas phaser3 y tricorder en una herramienta práctica.]"
USAGE+="[k:kill?Usar configuración de eliminación (predeterminado).]"
USAGE+="[l:level]#[level:=2?Establecer el nivel del phaser.]"
USAGE+="[s:stun?¡Solo aturdimiento!]"
USAGE+="[t:tricorder?Modo tricorder.]:?[life_form]"
while getopts "$USAGE" optchar ...
\end{lstlisting}
Observa que \emph{getopts} comprende automáticamente que la descripción real de las opciones viene después de los encabezados de la página man; no hay un explícito \texttt{[+OPTIONS?...]} en el texto del string.
\item Se puede proporcionar texto adicional descriptivo para el resumen de uso corto después de la descripción de las opciones, separado por dos saltos de línea:
\begin{lstlisting}[language=bash]
USAGE="[+NOMBRE?phaser4 --- phaser y tricorder combinados]"
USAGE+="[+DESCRIPCIÓN?El programa phaser4 combina la operación "
USAGE+="de los programas phaser3 y tricorder en una herramienta práctica.]"
USAGE+="[k:kill?Usar configuración de eliminación (predeterminado).]"
USAGE+="[l:level]#[level:=2?Establecer el nivel del phaser.]"
USAGE+="[s:stun?¡Solo aturdimiento!]"
USAGE+="[t:tricorder?Modo tricorder.]:?[life_form]"
USAGE+=$'\n\narchivo ...\n\n' # Usa cadena ANSI C para el carácter \n
USAGE+="[+VER TAMBIÉN?phaser3(1), tricorder(1)]"
while getopts "$USAGE" optchar ...
\end{lstlisting}
\item Para indicar texto que debe cursivarse, enciérralo entre pares de caracteres \textbackslash{}a. Para indicar texto que debe ponerse en negrita, enciérralo entre pares de caracteres \textbackslash{}b:
\begin{lstlisting}[language=bash]
USAGE="[+NOMBRE?phaser4 --- phaser y tricorder combinados]"
USAGE+="[+DESCRIPCIÓN?El programa \aphaser4\a combina la operación "
USAGE+="de los programas \aphaser3\a y \atricorder\a en una herramienta práctica.]"
USAGE+="[k:kill?Usar configuración de eliminación (predeterminado).]"
USAGE+="[l:level]#[level:=2?Establecer el nivel del phaser.]"
USAGE+="[s:stun?¡Solo aturdimiento!]"
USAGE+="[t:tricorder?Modo tricorder.]:?[life_form]"
USAGE+=$'\n\narchivo ...\n\n' # Usa cadena ANSI C para el carácter \n
USAGE+=$'[+VER TAMBIÉN?\aphaser3\a(1), \atricorder\a(1)]'
while getopts "$USAGE" optchar ...
\end{lstlisting}
\item Es posible un control dinámico de la salida descriptiva. Para hacer esto, escribe una función que imprima lo que desees y luego encierra el nombre de la función entre un par de caracteres \textbackslash{}f: \textbackslash{}fname\textbackslash{}f (esto no es necesario para phaser4).
\item Si una opción (o cualquier otra cosa) necesita una descripción detallada, encerrar el texto entre { y } crea una lista sangrada. Esto es particularmente útil para describir diferentes valores de opción:
\begin{lstlisting}[language=bash]
USAGE="[+NOMBRE?phaser4 --- phaser y tricorder combinados]"
USAGE+="[+DESCRIPCIÓN?El programa \aphaser4\a combina la operación "
USAGE+="de los programas \aphaser3\a y \atricorder\a en una herramienta práctica.]"
USAGE+="[k:kill?Usar configuración de eliminación (predeterminado).]"
USAGE+="[l:level]#[level:=2?Establecer el nivel del phaser.]{ Añadir descripciones de valores
[level=0-2?configuraciones no letales]
[level=3-10?letales, usar con precaución]
}"
USAGE+="[s:stun?¡Solo aturdimiento!]"
USAGE+="[t:tricorder?Modo tricorder.]:?[life_form]"
USAGE+=$'\n\narchivo ...\n\n' # Usa cadena ANSI C para el carácter \n
USAGE+=$'[+VER TAMBIÉN?\aphaser3\a(1), \atricorder\a(1)]'
while getopts "$USAGE" optchar ...
\end{lstlisting}
\item Casi terminado. El texto entre corchetes cuadrados que comienza con un signo menos proporciona información de versión e identificación. Tal texto va al principio. El ítem vacío indica una versión y puede contener tanto cadenas de SCCS como de RCS, como se muestra aquí:
\begin{lstlisting}[language=bash]
USAGE=$'[-?\n@(#)$Id: phaser4 (Investigación y Desarrollo de la Flota Estelar)'
USAGE+=$' Stardate 57234.22 $\n]'
USAGE+="[-autor?J. Programador ]"
USAGE+="[-derechos?Copyright (c) Stardate 57000 Flota Estelar.]"
USAGE+="[-licencia?http://www.starfleet.mil.fed/weapons-license.xml23]"
USAGE+="[+NOMBRE?phaser4 --- phaser y tricorder combinados]"
USAGE+="[+DESCRIPCIÓN?El programa \aphaser4\a combina la operación "
USAGE+="de los programas \aphaser3\a y \atricorder\a en una herramienta práctica.]"
USAGE+="[k:kill?Usar configuración de eliminación (predeterminado).]"
USAGE+="[l:level]#[level:=2?Establecer el nivel del phaser.]{
[level=0-2?configuraciones no letales]
[level=3-10?letales, usar con precaución]
}"
USAGE+="[s:stun?¡Solo aturdimiento!]"
USAGE+="[t:tricorder?Modo tricorder.]:?[life_form]"
USAGE+=$'\n\narchivo ...\n\n' # Usa cadena ANSI C para el carácter \n
USAGE+=$'[+VER TAMBIÉN?\aphaser3\a(1), \atricorder\a(1)]'
while getopts "$USAGE" optchar ...
\end{lstlisting}
\item Finalmente, permite escapes dentro de las cadenas. \texttt{]]} representa un corchete de cierre literal cuando \emph{getopts} de otra manera podría interpretarlo como un corchete de cierre. De manera similar, ?? representa un signo de interrogación literal que de otra manera podría iniciar una descripción.
\end{enumerate}
Uff, ¡eso es mucha información! Sin embargo, verlo en el orden en que se agregó ayuda a que tenga sentido. Aquí tienes un resumen de los elementos que van en la cadena de uso, en el orden que requiere `getopts`:
\begin{enumerate}
\item Las cadenas de identificación para la versión, autor, licencia, etc., son la primera parte. Están encerradas en corchetes y comienzan con un signo menos. El nombre del ítem, como <<author>>, sigue el signo menos y termina en un signo de interrogación. Después del signo de interrogación viene la información asociada.
El ítem vacío indica información de versión y debe tener la forma mostrada anteriormente; \emph{getopts} elimina los caracteres de identificación especiales de SCCS y RCS.
\item Las secciones y el texto de estilo de la página de manual de Unix vienen a continuación. Están encerrados en corchetes y comienzan con un signo más. El nombre de la sección termina en el carácter ?, y el texto descriptivo sigue.
El texto separado por dos nuevas líneas desde la descripción de las opciones se añade al mensaje breve de uso.
\item Las descripciones de las opciones forman la tercera sección. Todavía se permite la forma corta original, como se describió en el \hyperref[sec:Chapter6]{Capítulo 6}:
\begin{itemize}
\item Usa : para opciones que requieren argumentos.
\item Usa \# para opciones que requieren argumentos numéricos.
\item Usa :? y \#? para opciones que permiten argumentos pero no los requieren.
\end{itemize}
\item Sigue las opciones con un texto descriptivo entre [ y ]. Usa := dentro del texto descriptivo para especificar un valor predeterminado para un argumento de opción.
\item Las opciones largas se emparejan con una letra de opción corta encerrándolas entre corchetes y separándolas por dos puntos. Esto reemplaza la forma de una sola letra.
\item Encierra entre dos caracteres \texttt{\textbackslash{}a} los elementos que se deben cursivar. Encierra entre dos caracteres \texttt{\textbackslash{}\b} los elementos que se deben poner en negrita. Encierra el nombre de una función de personalización a llamar entre dos caracteres \texttt{\textbackslash{}f}.
\item Usa \{ y \} para encerrar descripciones de opciones anidadas e indentadas.
\item Sigue la sección de opciones con dos nuevas líneas y texto adicional para el resumen de las opciones cortas.
\item Usa \texttt{]]} para representar un corchete de cierre literal y \texttt{??} para representar un signo de interrogación literal.
\end{enumerate}
Aquí está la versión esquelética de \emph{phaser4}:
\begin{lstlisting}[language=bash]
#! /bin/ksh
# uso: phaser4 [ opciones ] archivos
# -k, --kill usa la configuración de matar (predeterminado)
# -l n, --level n establece el nivel del phaser (predeterminado = 2)
# -s, --stun usa la configuración de aturdir
# -t [lf], --tricorder [lf] modo tricorder, escaneo opcional para la forma de vida lf
USAGE=$'[-?\n@(#)$Id: phaser4 (Investigación y Desarrollo de la Flota Estelar)'
USAGE+=$' Fecha estelar 57234.22 $\n]'
USAGE+="[-autor?J. Programador <J.Prog@r-d.starfleet.mil.fed>]"
USAGE+="[-copyright=Derechos de autor (c) Fecha estelar 57000 Flota Estelar.]"
USAGE+="[-licencia?http://www.flotaestelar.mil.fed/licencia-armas.xml23]"
USAGE+="[+NOMBRE?phaser4 --- combinación de phaser y tricorder]"
USAGE+="[+DESCRIPCIÓN?El programa \aphaser4\a combina la operación "
USAGE+="de los programas \aphaser3\a y \atricorder\a en una herramienta práctica.]"
USAGE+="[k:kill?Usar configuración de matar (predeterminado).]"
USAGE+="[l:level]#[level:=2?Establecer el nivel del phaser.]{
[0-2?configuraciones no letales]
[3-10?letales, usar con precaución]
}"
USAGE+="[s:stun?Solo aturdir.]"
USAGE+="[t:tricorder?Modo tricorder.]:?[life_form]"
USAGE+=$'\n\narchivo ...\n\n'
USAGE+=$'[+VER TAMBIÉN?\aphaser3\a(1), \atricorder\a(1)]'
kill=1 aturdir=0 nivel=2 # valores predeterminados
tricorder=0 phaser=1
life_form=
while getopts "$USAGE" optchar ; do
case $optchar in
k) kill=1 aturdir=0 ;;
s) kill=0 aturdir=1 ;;
l) level=$OPTARG
if ((level < 0)) ; then
level=0 ;
fi
if ((level > 10)) ; then
level=10 ;
fi
;;
t) phaser=0 tricorder=1
life_form=${OPTARG:-"desconocido_general"}
;;
esac
done
print kill=$kill
print aturdir=$aturdir
print level=$level
print phaser=$phaser
print tricorder=$tricorder
print life_form=$life_form
\end{lstlisting}
Aquí está la salida de \texttt{phaser4 --man}:
\begin{lstlisting}[language=bash]
NAME
phaser4 --- combinación de phaser y tricorder
SYNOPSIS
phaser4 [ opciones ] archivo ...
DESCRIPTION
El programa phaser4 combina la operación de los programas phaser3 y tricorder
en una herramienta práctica.
OPTIONS
-k, --kill Usar configuración de kill (predeterminado).
-l, --level=level Establecer el level del phaser.
level=0-2
configuraciones no letales
level=3-10
letales, usar con precaución
El valor predeterminado es 2.
-s, --stun Solo aturdir.
-t, --tricorder[=life_form]
Modo tricorder. El valor de la opción puede omitirse.
SEE ALSO
phaser3(1), tricorder(1)
IMPLEMENTATION
version phaser4 (Investigación y Desarrollo de la Flota Estelar) Fecha estelar
57234.22
autor J. Programador
copyright Copyright (c) Fecha estelar 57000 Flota Estelar.
license http://www.flotaestelar.mil.fed/licencia-armas.xml23
\end{lstlisting}

123
Secciones/ApendiceC.tex Normal file
View File

@ -0,0 +1,123 @@
Este apéndice describe cómo descargar binarios para \emph{ksh93}, así como cómo descargar el código fuente para \emph{ksh93} y construir una versión funcional. Deberías hacer esto si tu sistema no tiene \emph{ksh93} en absoluto o si necesitas alguna de las características que solo están disponibles en las versiones más recientes.
\section{Sitios web de Korn Shell}
El punto de partida para todo lo relacionado con el shell Korn es \url{http://www.kornshell.com}, mantenido por David Korn, con enlaces agrupados en las siguientes categorías:
\begin{description}
\item[Información] Al hacer clic en este enlace, se accede a una descripción general de una página sobre el shell Korn.
\item[Software] Al hacer clic en este enlace, se encuentran enlaces al sitio de descargas de AT\&T (ver la siguiente sección), algún código de ejemplo, un artículo en línea para \emph{dtksh} y un enlace para \emph{tksh}. Estos dos últimos se describen en el Apéndice A.
\item[Documentación] Este enlace lleva a una página con enlaces a información en línea, incluida información general, páginas de manual tanto para \emph{ksh88} como para \emph{ksh93}, libros y referencias sobre el shell Korn, y documentos sobre el shell Korn de diversas conferencias.
\item[Recursos] Una lista de enlaces a otros recursos en la web para \emph{ksh} y muchos de los otros shells descritos en el Apéndice A, como \emph{bash} y \emph{dtksh}.
\item[Diversión] David G. Korn, el programador, se encuentra con KoRN, el grupo de rock. Dicho y hecho.
\end{description}
\section{Lo que puedes descargar}
\url{http://www.research.att.com/sw/download} es el punto de partida para descargar el software de \emph{ksh}. El software está cubierto por una licencia de estilo de código abierto. La versión actual de la licencia se encuentra en \url{http://www.research.att.com/sw/license/ast-open.html}. Esta licencia se reproduce en el Apéndice D. Deberías leerla y entenderla primero; si sus términos no son aceptables para ti, no deberías descargar el código fuente o binarios del software desde el sitio web de AT\&T.
El software en el sitio web de AT\&T está disponible en diferentes <<paquetes>>, la mayoría de los cuales tienen nombres con el prefijo <<ast>>, que significa \emph{Advanced Software Tools} (Herramientas de Software Avanzado). Los paquetes de origen vienen como archivos tar comprimidos con \emph{gzip}, usando el sufijo de nombre de archivo \emph{.tgz}. Elige uno o más de los siguientes paquetes para descargar:
\begin{description}
\item[ratz] Un programa ejecutable independiente para leer archivos tar comprimidos con \emph{gzip}. Úsalo si no tienes \emph{gzip} en tu sistema y no quieres molestarte en descargar y compilar \emph{gzip} primero. Puedes descargar el código fuente para este paquete o un ejecutable binario para cualquiera de las arquitecturas enumeradas en la Tabla \ref{Tab:C-1}.
\item[ksh] Esta es la forma más rápida de obtener un ejecutable de \emph{ksh93}. Hay versiones disponibles para las arquitecturas enumeradas en la Tabla \ref{Tab:C-1}.
\item[INIT] Este paquete debe descargarse al construir cualquiera de los siguientes paquetes fuente. Contiene los archivos y estructuras de directorio en los que dependen las herramientas y el sistema de construcción de AST.
\item[ast-ksh] Este paquete construye solo la infraestructura de soporte (bibliotecas, programas de prueba de entorno, etc.) para \emph{ksh} y el ejecutable \emph{ksh}. Es lo más sencillo de construir.
\item[ast-base] Este paquete construye todo en el paquete \emph{ast-ksh} y algunas herramientas AST básicas adicionales. En particular, incluye \emph{pax}, una herramienta de archivado que combina funciones de \emph{tar(1)} y \emph{cpio(1)}, y \emph{nmake}, una versión significativamente mejorada del programa estándar \emph{make(1)} de Unix. También incluye las bibliotecas \emph{sfio} (Safe Fast I/O) y \emph{ast}, que puedes usar para tus propios programas.
\item[ast-open] Este paquete construye todo en el paquete ast-base y muchas herramientas adicionales. Ten en cuenta que \emph{tksh} (ver Apéndice A) está incluido en este paquete.
\end{description}
Cada uno de los paquetes (excepto \emph{INIT}) también está disponible como binarios precompilados. La Tabla C-1 enumera las arquitecturas disponibles para estos paquetes. También hay traducciones de locales para algunos locales y algunos programas.
\begin{table}[h]
\center
\caption{Arquitecturas admitidas para programas AST}
\label{Tab:C-1}
\begin{tabular}{|m{3cm}|m{12cm}|} \hline
\textbf{Nombre} & \textbf{SO/Arquitectura} \\ \hline
darwin.ppc & Apple's MacOS X (también conocido como Darwin) para Motorola Power PC \\\hline
hp.pa & Hewlett-Packard HP-UX para HP Precision Architecture \\\hline
ibm.risc & AIX de IBM para RS/6000 \\\hline
linux.i386 & GNU/Linux en Intel 80386 y superior \\\hline
linux.s390 & GNU/Linux en el mainframe IBM S/390 \\\hline
mvs.390 & MVS de IBM en el mainframe IBM S/390 \\\hline
netbsd.i386 & NetBSD en Intel 80386 y superior (ver \url{http://www.netbsd.org}) \\\hline
openbsd.i386 & OpenBSD en Intel 80386 y superior (ver \url{http://www.openbsd.org}) \\\hline
osf.alpha & OSF/1 en el procesador Compaq (anteriormente Digital) Alpha \\\hline
sgi.mips3 & Silicon Graphics (SGI) Irix en el procesador MIPS \\\hline
sol.sun4 & Solaris 5.4 en la arquitectura Sun SPARC \\\hline
sol6.sun4 & Solaris 5.6 en la arquitectura Sun SPARC \\\hline
sol7.i386 & Solaris 7 en Intel 80386 y superior \\\hline
sol7.sun4 & Solaris 7 en la arquitectura Sun SPARC \\\hline
sol8.sun4 & Solaris 8 en la arquitectura Sun SPARC \\\hline
sun4 & SunOS 4.x en la arquitectura Sun SPARC \\\hline
unixware.i386 & UnixWare (la última versión oficial de System V) en Intel 80386 y superior \\\hline
\end{tabular}
\end{table}
\section{Construcción de ksh (Shell Korn)}
Construir cualquiera de los paquetes a partir del código fuente es bastante sencillo. Todos los detalles, junto con preguntas frecuentes y notas, se encuentran en el sitio web de AT\&T. Aquí tienes una guía paso a paso de los pasos. Mostramos los pasos para el paquete \emph{ast-open}, pero son idénticos para los demás paquetes de código fuente.
\begin{enumerate}
\item Asegúrate de tener un compilador de C para tu sistema. Se prefiere un compilador de C ANSI/ISO, pero un compilador K\&R también funcionará. Obtener un compilador de C si no tienes uno está más allá del alcance de este libro; contacta a tu administrador de sistema local.
\item Descarga el/los paquete(s) que deseas construir en un directorio vacío. Aquí, construimos el paquete ast-open del 31 de octubre de 2001:
\begin{lstlisting}[language=bash]
$ ls
INIT.2001-10-31.tgz ast-open.2001-10-31.tgz
\end{lstlisting}
\item Crea el directorio \texttt{lib/package/tgz} y mueve los archivos allí:
\begin{lstlisting}[language=bash]
$ mkdir lib lib/package lib/package/tgz
$ mv *.tgz lib/package/tgz
\end{lstlisting}
\item Extrae el paquete \emph{INIT} manualmente:
\begin{lstlisting}[language=bash]
$ gzip -d < lib/package/tgz/INIT.2001-10-31.tgz | tar -xvpf -
\r\v\vNOTICE -- LICENSED SOFTWARE -- SEE README FOR DETAILS\r\v\v
README
src/Makefile
src/cmd/Makefile
src/lib/Makefile
...
\end{lstlisting}
Si no tienes \emph{gzip}, usa el programa \emph{ratz}, como se describió anteriormente.
\item Inicializa la lista de paquetes disponibles:
\begin{lstlisting}[language=bash]
$ bin/package read
\r\v\vNOTICE -- LICENSED SOFTWARE -- SEE README FOR DETAILS\r\v\v
README
src/Makefile
src/cmd/Makefile
src/lib/Makefile
src/Mamfile
...
\end{lstlisting}
\item Inicia la compilación. Este paso es bastante detallado y llevará un tiempo. La duración exacta depende de la velocidad de tu sistema y compilador, y del paquete que estás construyendo:
\begin{lstlisting}[language=bash]
$ bin/package make
package: initialize the /home/arnold/ast-open/arch/linux.i386 view
package: update /home/arnold/ast-open/arch/linux.i386/bin/proto
package: update /home/arnold/ast-open/arch/linux.i386/bin/mamake
package: update /home/arnold/ast-open/arch/linux.i386/bin/ratz
package: update /home/arnold/ast-open/arch/linux.i386/bin/release
...
\end{lstlisting}
\item Instala los archivos creados. Esto se puede hacer con el comando \texttt{bin/package install directory}, donde \emph{directory} es la ubicación para colocar las cosas.
Alternativamente, si todo lo que te interesa es el binario de \emph{ksh}, simplemente puedes copiarlo. El binario compilado estará en un directorio llamado \texttt{arch/ARCH/bin}, donde \emph{ARCH} representa tu arquitectura, como \emph{linux.i386}:
\begin{lstlisting}[language=bash]
cp arch/linux.i386/bin/ksh $HOME/bin/ksh93
\end{lstlisting}
\item{¡Disfruta!}
\end{enumerate}

99
Secciones/ApendiceD.tex Normal file
View File

@ -0,0 +1,99 @@
\begin{textbf}
ACUERDO DE CÓDIGO FUENTE \\
Versión 1.2D
\end{textbf}
POR FAVOR, LEA ESTE ACUERDO DETENIDAMENTE. Al acceder y utilizar el \textbf{Código Fuente}, acepta este Acuerdo en su totalidad y se compromete a utilizar el \textbf{Código Fuente} únicamente de acuerdo con los siguientes términos y condiciones. Si no desea quedar vinculado por estos términos y condiciones, no acceda ni utilice el \textbf{Código Fuente}.
\begin{enumerate}
\item TUS REPRESENTACIONES
\begin{itemize}
\item Declara y garantiza que:
\begin{enumerate}
\item Si eres una entidad, o un individuo que no es la persona que acepta este Acuerdo, la persona que acepta este Acuerdo en tu nombre es tu representante legalmente autorizado, debidamente autorizado para aceptar acuerdos de este tipo en tu nombre y obligarte a cumplir con sus disposiciones.
\item Has leído y comprendido completamente este Acuerdo en su totalidad.
\item Tus \textbf{Materiales de Construcción} son originales o no incluyen ningún \textbf{Software} obtenido bajo una licencia que entre en conflicto con las obligaciones contenidas en este Acuerdo.
\item Según tu leal saber y entender, tus \textbf{Materiales de Construcción} no infringen ni apropian indebidamente los derechos de ninguna persona o entidad.
Monitorizarás regularmente el Sitio web en busca de cualquier aviso.
\end{enumerate}
\end{itemize}
\item DEFINICIONES E INTERPRETACIÓN
\begin{enumerate}
\item A los efectos de este Acuerdo, ciertos términos se han definido a continuación y en otros lugares de este Acuerdo para abarcar significados que pueden diferir o agregarse a la connotación normal de la palabra definida.
\begin{enumerate}
\item \textbf{<<Código Adicional>>} significa \textbf{Software} en forma de código fuente que no contiene ningún
\begin{itemize}
\item \textbf{Código Fuente}, o
\item trabajo derivado (este término tiene el mismo significado en este Acuerdo que en la Ley de Derechos de Autor de EE. UU.) del \textbf{Código Fuente}.
\end{itemize}
\item \textbf{<<Reclamaciones de Patente de AT\&T} significa aquellas reclamaciones de patentes (i) propiedad de AT\&T y (ii) licenciables sin restricción u obligación, que, sin una licencia, son necesaria e inevitablemente infringidas por el uso de la funcionalidad del \textbf{Código Fuente}.
\item \textbf{<<Materiales de Construcción>>} significa, en referencia a un \textbf{Producto Derivado}, el \textbf{Parche} y el \textbf{Código Adicional}, si los hay, utilizados en la preparación de dicho \textbf{Producto Derivado}, junto con instrucciones escritas que describan, de manera razonable detallada, dicha preparación.
\item \textbf{<<Cápsula>>} significa un archivo informático que contiene exactamente el mismo contenido que un archivo informático descargado del \textbf{Sitio web}.
\item \textbf{<<Producto Derivado>>} significa un \textbf{Producto de Software} que es una obra derivada del \textbf{Código Fuente}.
\item \textbf{<<IPR>>} significa todos los derechos protegibles bajo la ley de propiedad intelectual en cualquier lugar del mundo, incluidos los derechos protegibles bajo las leyes de patentes, derechos de autor y secretos comerciales, pero no los derechos de marca.
\item \textbf{<<Parche>>} significa \textbf{Software} para cambiar toda o cualquier parte del \textbf{Código Fuente}.
\item \textbf{<<Aviso Propietario>>} significa la siguiente declaración:
<<Este producto contiene ciertos códigos de software u otra información (<<Software de AT\&T>>) propietarios de AT\&T Corp. (<<AT\&T>>). El Software de AT\&T se proporciona tal cual. USTED ACEPTA TODA LA RESPONSABILIDAD Y RIESGO POR EL USO DEL SOFTWARE DE AT\&T. AT\&T NO REALIZA, Y RENUNCIA EXPRESAMENTE, CUALQUIER GARANTÍA EXPRESA O IMPLÍCITA DE NINGÚN TIPO, INCLUIDAS, SIN LIMITACIÓN, LAS GARANTÍAS IMPLÍCITAS DE COMERCIABILIDAD O ADECUACIÓN PARA UN PROPÓSITO PARTICULAR, LAS GARANTÍAS DE TÍTULO O NO INFRACCIÓN DE NINGÚN DERECHO DE PROPIEDAD INTELECTUAL, CUALQUIER GARANTÍA QUE SURJA POR EL USO DEL COMERCIO, CURSO DE NEGOCIACIÓN O DESEMPEÑO, O CUALQUIER GARANTÍA DE QUE EL SOFTWARE DE AT\&T ES <<LIBRE DE ERRORES>> O CUMPLIRÁ CON SUS REQUISITOS.
A menos que acepte una licencia para utilizar el Software de AT\&T, no debe descompilar, desensamblar o realizar ingeniería inversa de este producto para averiguar el código fuente de ningún Software de AT\&T.
© AT\&T Corp. Todos los derechos reservados. AT\&T es una marca registrada de AT\&T Corp.>>
\item \textbf{<<Software>>} significa, según lo que pueda requerir el contexto, instrucciones de código fuente u objeto para controlar el funcionamiento de una unidad central de procesamiento o computadora, y archivos informáticos que contienen datos o texto.
\item \textbf{<<Producto de Software>>} significa una colección de archivos informáticos que contienen \textbf{Software} solo en forma de código objeto, que, tomados en conjunto, comprenden razonablemente un producto, independientemente de si dicho producto está destinado para uso interno o explotación comercial. Un solo archivo informático puede constituir un \textbf{Producto de Software}.
\item \textbf{<<Código Fuente>>} significa el \textbf{Software} contenido en forma comprimida en la \textbf{Cápsula}.
\item \textbf{<<Sitio web>>} significa el sitio web de Internet con la URL \url{http://www.research.att.com/sw/download/}. AT\&T puede \textbf{cambiar} el contenido o la URL del \textbf{Sitio web}, o eliminarlo por completo de Internet.
\end{enumerate}
\item A modo de aclaración solamente, los términos \textbf{Cápsula, Aviso Propietario} y \textbf{Código Fuente}, cuando se utilizan en este Acuerdo, significarán los materiales e información definidos por dichos términos sin ningún cambio, mejora, enmienda, alteración o modificación (colectivamente, \textbf{<<cambio>>}).
\end{enumerate}
\item CONCESIÓN DE DERECHOS
\begin{enumerate}
\item Sujeto a reclamaciones de propiedad intelectual de terceros, si las hay, y a los términos y condiciones de este Acuerdo, AT\&T te otorga bajo:
\begin{enumerate}
\item Las \textbf{Reclamaciones de Patente} de AT\&T y los derechos de autor de AT\&T en el \textbf{Código Fuente}, una licencia no exclusiva y totalmente pagada para:
\begin{enumerate}
\item Reproducir y distribuir la \textbf{Cápsula}.
\item Mostrar, ejecutar, utilizar y compilar el \textbf{Código Fuente} y ejecutar el \textbf{Software} binario resultante en una computadora.
\item Preparar un \textbf{Producto Derivado} únicamente compilando \textbf{Código Adicional}, si lo hay, junto con el código resultante de operar un \textbf{Parche} en el \textbf{Código Fuente}.
\item Ejecutar en una computadora y distribuir a otros \textbf{Productos Derivados}, excepto que, con respecto a las \textbf{Reclamaciones de Patente de AT\&T}, los derechos de licencia otorgados en los incisos (iii) y (iv) anteriores solo se extenderán y limitarán a esa parte de un \textbf{Producto Derivado} que es un \textbf{Software} compilado a partir de alguna parte del \textbf{Código Fuente}; y,
\end{enumerate}
\item Los derechos de autor de AT\&T en el \textbf{Código Fuente}, una licencia no exclusiva y totalmente pagada para preparar y distribuir \textbf{Parches} para el \textbf{Código Fuente}.
\end{enumerate}
\item Sujeto a los términos y condiciones de este Acuerdo, puedes crear un hipervínculo entre un sitio web de Internet de tu propiedad y controlado por ti y el \textbf{Sitio web}, que describa de manera justa y de buena fe dónde se pueden obtener la \textbf{Cápsula} y el \textbf{Código Fuente}, siempre que no enmarques el \textbf{Sitio web} ni des la falsa impresión de que AT\&T está de alguna manera asociado, respalda o patrocina tu sitio web. Cualquier buena voluntad asociada con dicho hipervínculo será para el único beneficio de AT\&T. Además de la creación de dicho hipervínculo, nada en este Acuerdo se interpretará como que te confiere el derecho de usar cualquier referencia a AT\&T, sus nombres comerciales, marcas comerciales, marcas de servicio o cualquier otro indicio de origen propiedad de AT\&T, o indicar que tus productos o servicios están de alguna manera patrocinados, aprobados o respaldados por, o afiliados con, AT\&T.
\item Excepto según se establece expresamente en la Sección 3.1 anterior, no se otorgan ni otros derechos ni licencias bajo ninguna \textbf{IPR} de AT\&T ni, por implicación, impedimento o de otra manera, se confieren. A modo de ejemplo únicamente, no se otorgan ni se confieren derechos ni licencias bajo ninguna de las patentes de AT\&T ni, por implicación, impedimento o de otra manera, con respecto a ninguna parte de un \textbf{Producto Derivado} que no sea un \textbf{Software} compilado a partir de alguna parte, sin \textbf{cambios}, del \textbf{Código Fuente}.
\end{enumerate}
\item TUS OBLIGACIONES
\begin{enumerate}
\item Si distribuyes \textbf{Materiales de Construcción} (incluido si estás obligado a hacerlo en virtud de este Acuerdo), debes asegurarte de que el destinatario celebre y acepte debidamente un acuerdo contigo que incluya los términos mínimos establecidos en el Apéndice A (\url{http://www.research.att.com/sw/license/ast-terms.html}) (completado para indicar que eres el LICENCIANTE) y ninguna otra disposición que, en opinión de AT\&T, entre en conflicto con tus obligaciones según, o el propósito de, este Acuerdo. El acuerdo requerido en virtud de esta Sección 4.1 puede estar en forma electrónica y puede distribuirse con los \textbf{Materiales de Construcción} de una manera tal que el destinatario acepte el acuerdo mediante el uso o la instalación de los \textbf{Materiales de Construcción}. Si algún \textbf{Código Adicional} contenido en tus \textbf{Materiales de Construcción} incluye \textbf{Software} que obtuviste bajo licencia, el acuerdo también incluirá detalles completos sobre la licencia y cualquier restricción u obligación asociada con dicho \textbf{Software}.
\item Si preparas un Parche que distribuyes a cualquier otra persona, debes:
\begin{enumerate}
\item Ponerte en contacto con AT\&T, según se pueda proporcionar en el \textbf{Sitio web} o en un archivo de texto incluido con el \textbf{Código Fuente}, y describir para AT\&T dicho \textbf{Parche} y proporcionar a AT\&T una copia de dicho \textbf{Parche} según lo indique AT\&T; o,
\item Donde hagas tu \textbf{Parche} generalmente disponible en tu sitio web de Internet, debes proporcionar a AT\&T la URL de tu sitio web y, por la presente, otorgar a AT\&T un derecho no exclusivo y totalmente pagado para crear un hipervínculo entre tu sitio web y una página asociada al \textbf{Sitio web}.
\end{enumerate}
\item Si preparas un \textbf{Producto Derivado}, dicho producto debe mostrar de manera conspicua a los usuarios, y cualquier documentación y acuerdo de licencia correspondiente debe incluir como disposición el \textbf{Aviso Propietario}.
\end{enumerate}
\item TU CONCESIÓN DE DERECHOS A AT\&T
\begin{enumerate}
\item Concedes a AT\&T, bajo cualquier \textbf{propiedad intelectual de tu propiedad} o con licencia tuya que de alguna manera esté relacionada con tus \textbf{Parches}, una licencia no exclusiva, perpetua, mundial, totalmente pagada, sin restricciones, irrevocable, junto con el derecho de sublicenciar a otros, para (a) fabricar, hacer fabricar, usar, ofrecer en venta, vender e importar cualquier producto, servicio o cualquier combinación de productos o servicios, y (b) reproducir, distribuir, preparar trabajos derivados basados en, realizar, mostrar y transmitir tus Parches en cualquier medio, ya sea conocido ahora o desarrollado en el futuro.
\end{enumerate}
\item CLAUSULA <<TAL COMO ESTÁ>> / LIMITACIÓN DE RESPONSABILIDAD
\begin{enumerate}
\item El \textbf{Código Fuente} y la \textbf{Cápsula} se proporcionan <<TAL COMO ESTÁ>>. ASUMES LA RESPONSABILIDAD Y RIESGO TOTAL POR SU USO, INCLUYENDO EL RIESGO DE DEFECTOS O INEXACTITUDES. AT\&T NO REALIZA, Y RENUNCIA EXPRESAMENTE A, CUALQUIER GARANTÍA EXPRESA O IMPLÍCITA DE NINGÚN TIPO, INCLUYENDO, SIN LIMITACIÓN, LAS GARANTÍAS IMPLÍCITAS DE COMERCIABILIDAD O ADECUACIÓN PARA UN PROPÓSITO PARTICULAR, GARANTÍAS DE TÍTULO O NO INFRACCIÓN DE CUALQUIER DERECHO DE PROPIEDAD INTELECTUAL O MARCA, CUALQUIER GARANTÍA QUE SURJA DEL USO DEL COMERCIO, CURSO DE TRATAMIENTO O DESEMPEÑO, O CUALQUIER GARANTÍA DE QUE EL CÓDIGO FUENTE O LA CÁPSULA SON <<LIBRES DE ERRORES>> O CUMPLIRÁN CON TUS REQUISITOS.
\item EN NINGÚN CASO AT\&T SERÁ RESPONSABLE DE (a) DAÑOS INCIDENTALES, CONSECUENTES O INDIRECTOS (INCLUYENDO, SIN LIMITACIÓN, DAÑOS POR PÉRDIDA DE BENEFICIOS, INTERRUPCIÓN DEL NEGOCIO, PÉRDIDA DE PROGRAMAS O INFORMACIÓN, Y SIMILARES) DERIVADOS DEL USO O LA INCAPACIDAD DE USAR EL \textbf{CÓDIGO FUENTE} O \textbf{LA CÁPSULA}, INCLUSO SI SE HA INFORMADO A AT\&T O A CUALQUIERA DE SUS REPRESENTANTES AUTORIZADOS DE LA POSIBILIDAD DE TALES DAÑOS, (b) CUALQUIER RECLAMO ATRIBUIBLE A ERRORES, OMISIONES U OTRAS INEXACTITUDES EN EL \textbf{CÓDIGO FUENTE} O \textbf{LA CÁPSULA}, O (c) CUALQUIER RECLAMO DE TERCEROS.
\item DEBIDO A QUE ALGUNOS ESTADOS NO PERMITEN LA EXCLUSIÓN O LIMITACIÓN DE RESPONSABILIDAD POR DAÑOS INCIDENTALES O CONSECUENTES, ES POSIBLE QUE LAS LIMITACIONES ANTERIORES NO SE APLIQUEN A TI. EN CASO DE QUE LA LEGISLACIÓN APLICABLE NO PERMITA LA EXCLUSIÓN O LIMITACIÓN TOTAL DE RESPONSABILIDAD DE RECLAMACIONES Y DAÑOS SEGÚN LO ESTABLECIDO EN ESTE ACUERDO, LA RESPONSABILIDAD DE AT\&T SE LIMITA EN LA MAYOR MEDIDA PERMITIDA POR LA LEY.
\end{enumerate}
\item INDEMNIZACIÓN
\begin{enumerate}
\item Indemnizarás y eximirás de responsabilidad a ATi\&T, sus afiliados y representantes autorizados contra cualquier reclamo, demanda o procedimiento presentado o iniciado por cualquier tercero y derivado de, o relacionado con, tu uso del \textbf{Código Fuente}. Esta obligación incluirá indemnizar contra todos los daños, pérdidas, costos y gastos (incluidos los honorarios de abogados) incurridos por AT\&T, sus afiliados y representantes autorizados como resultado de tales reclamaciones, demandas o procedimientos, incluidos los costos o gastos incurridos en la defensa contra tales reclamaciones, demandas o procedimientos.
\end{enumerate}
\item GENERAL
\begin{enumerate}
\item No presentarás contra AT\&T, sus afiliados o representantes autorizados ningún reclamo por infracción o apropiación indebida de cualquier \textbf{propiedad intelectual} o derechos de marca de alguna manera relacionados con el \textbf{Código Fuente}, incluidos aquellos relacionados con cualquier \textbf{Parche}.
\item En caso de que alguna disposición de este Acuerdo se considere ilegal o inaplicable, AT\&T puede, pero no está obligada a, publicar en el \textbf{sitio web} una nueva versión de este Acuerdo que, en opinión de AT\&T, preserve razonablemente la intención de este Acuerdo.
\item Tus derechos y licencia (pero no tus obligaciones) bajo este Acuerdo se terminarán automáticamente en caso de que (a) se publique en el \textbf{sitio web} un aviso de una reclamación no frívola de un tercero relacionada con el \textbf{Código Fuente} o la \textbf{Cápsula}, (b) tengas conocimiento de dicha reclamación, (c) alguna de tus representaciones o garantías en el Artículo 1.0 o la Sección 8.4 sean falsas o inexactas, (d) excedas los derechos y la licencia otorgados o (e) no cumplas completamente con cualquier disposición de este Acuerdo. Nada en esta disposición se interpretará para restringirte, a tu opción y sujeto a la ley aplicable, de reemplazar la parte del \textbf{Código Fuente} que sea objeto de una reclamación de un tercero con código no infractor o de negociar de manera independiente los derechos necesarios con el tercero.
\item Reconoces que el \textbf{Código Fuente} y la \textbf{Cápsula} pueden estar sujetos a las leyes y regulaciones de exportación de EE. UU. y, en consecuencia, garantizas a AT\&T que no violarás, directa o indirectamente, ninguna ley o regulación de exportación de los EE. UU. aplicable.
\item Sin limitar ninguno de los derechos de AT\&T en virtud de este Acuerdo o por ley o en equidad, ni ampliar de otra manera el alcance de la licencia y los derechos otorgados en virtud del presente, si no cumples con alguna de tus obligaciones bajo este Acuerdo con respecto a cualquiera de tus \textbf{Parches} o \textbf{Productos Derivados}, o si realizas cualquier acto que exceda el alcance de la licencia y los derechos otorgados en este documento, entonces dichos \textbf{Parches, Productos Derivados} y actos no están licenciados ni autorizados en virtud de este Acuerdo y dicho incumplimiento también se considerará una violación de este Acuerdo. Además de cualquier otro remedio disponible por cualquier violación de tus obligaciones bajo este Acuerdo, AT\&T tendrá derecho a una orden judicial que te obligue a cumplir con dichas obligaciones.
\item Este Acuerdo se regirá e interpretará de acuerdo con las leyes del Estado de Nueva York, EE. UU., sin tener en cuenta sus reglas de conflictos de leyes. Este Acuerdo será interpretado de manera justa de acuerdo con sus términos y sin ninguna interpretación estricta a favor o en contra de AT\&T o de ti. Cualquier demanda o procedimiento que presentes en relación con este Acuerdo deberá presentarse y tramitarse únicamente en Nueva York, Nueva York, EE. UU.
\end{enumerate}
\end{enumerate}

782
Secciones/Capitulo1.tex Normal file
View File

@ -0,0 +1,782 @@
Usted ha utilizado su ordenador para realizar tareas sencillas, como invocar sus programas de aplicación favoritos, leer su correo electrónico y quizás examinar e imprimir archivos. Sabe que su máquina ejecuta el sistema operativo Unix, o quizá lo conozca bajo algún otro nombre, como Solaris, HP-UX, AIX o SunOS. (O puede que esté utilizando un sistema como GNU/Linux o uno de los sistemas derivados de 4.4-BSD que no está basado en el código fuente original de Unix). Pero aparte de eso, puede que no hayas pensado demasiado en lo que ocurre dentro de la máquina cuando escribes un comando y pulsas ENTER.
Es cierto que varias capas de eventos tienen lugar cada vez que introduces un comando, pero vamos a considerar sólo la capa superior, conocida como shell. En términos generales, un shell es cualquier interfaz de usuario para el sistema operativo Unix, es decir, cualquier programa que toma la entrada del usuario, la traduce en instrucciones que el sistema operativo puede entender, y transmite la salida del sistema operativo de vuelta al usuario.
Hay varios tipos de interfaz de usuario. El shell Korn pertenece a la categoría más común, conocida como interfaces de usuario basadas en caracteres. Estas interfaces aceptan líneas de comandos textuales que el usuario escribe; normalmente producen una salida basada en texto. Otros tipos de interfaz incluyen las ahora comunes interfaces gráficas de usuario (GUI), que añaden la capacidad de mostrar gráficos arbitrarios (no sólo caracteres de máquina de escribir) y de aceptar entradas de ratones y otros dispositivos señaladores, interfaces de pantalla táctil (como las que se ven en algunos cajeros automáticos), etc.
\section{¿Qué es un Shell?}
El trabajo del shell, entonces, es traducir las líneas de comando del usuario en instrucciones del sistema operativo. Por ejemplo, considere esta línea de comando:
\begin{lstlisting}[language=bash]
sort -n phonelist > phonelist.sorted
\end{lstlisting}
Esto significa: <<Ordena las líneas del archivo \emph{phonelist} en orden numérico, y pon el resultado en el archivo \emph{phonelist.sorted}>> Esto es lo que hace el shell con este comando:
\begin{enumerate}
\item Divide la línea en los trozos \texttt{sort, -n, phonelist, >,} y \texttt{phonelist.sorted}. Estos trozos se llaman \emph{palabras}.
\item Determina el propósito de las palabras: \texttt{sort} es un comando; \texttt{-n} y \texttt{phonelist} son argumentos; \texttt{>} y \texttt{phonelist.sorted}, en conjunto, son instrucciones de E/S.
\item Establece la E/S según \texttt{> phonelist.sorted} (salida al archivo \emph{phonelist.sorted}) y algunas instrucciones estándar implícitas.
\item Busca el comando \emph{sort} en un archivo y lo ejecuta con la opción \emph{-n} (orden numérico) y el argumento \emph{phonelist} (nombre de archivo de entrada).
\end{enumerate}
Por supuesto, cada paso implica realmente varios subpasos, y cada subpaso incluye una instrucción particular al sistema operativo subyacente.
Recuerde que el shell en sí mismo no es Unix - sólo la interfaz de usuario para él. Esto se ilustra en la Figura \ref{fig1}. Unix es uno de los primeros sistemas operativos en hacer la interfaz de usuario independiente del sistema operativo.
\begin{figure}[h!]
\centering
\includegraphics[scale=1]{fig_01}
\caption{\small{El shell es una capa alrededor del sistema operativo Unix}}
\label{fig1}
\end{figure}
\section{Alcance de Este Libro}
En este libro, aprenderá sobre el shell Korn, que es el más reciente y potente de los shells distribuidos con los sistemas Unix comerciales. Hay dos formas de utilizar el shell Korn: como interfaz de usuario y como entorno de programación.
Este capítulo y el siguiente cubren el uso interactivo. Estos dos capítulos deberían darle suficiente información para usar el shell con confianza y de forma productiva para la mayoría de sus tareas diarias.
Después de haber utilizado el shell durante un tiempo, sin duda encontrará ciertas características de su entorno (la <<apariencia>> del shell) que le gustaría cambiar y tareas que le gustaría automatizar. El \hyperref[sec:Chapter3]{Capítulo 3} muestra varias formas de hacerlo.
El \hyperref[sec:Chapter3]{Capítulo 3} también le prepara para la programación del shell, la cual se cubre en su mayor parte en los Capítulos \hyperref[sec:Chapter4]{4} a \hyperref[sec:Chapter6]{6}. No necesita tener ninguna experiencia en programación para entender estos capítulos y aprender la programación del shell. El \hyperref[sec:Chapter7]{Capítulo 7} y el \hyperref[sec:Chapter8]{Capítulo 8} dan descripciones más completas de las capacidades de E/S y de manejo de procesos del shell, y el \hyperref[sec:Chapter9]{Capítulo 9} discute varias técnicas para encontrar y eliminar problemas en sus programas del shell.
Aprenderá mucho sobre el shell Korn en este libro; también aprenderá sobre las utilidades de Unix y el funcionamiento del sistema operativo Unix en general. Es posible convertirse en un virtuoso programador de shell sin ninguna experiencia previa en programación. Al mismo tiempo, hemos evitado cuidadosamente bajar más allá de un cierto nivel de detalle sobre los aspectos internos de Unix. Mantenemos que no deberías ser un experto en el funcionamiento interno para usar y programar el shell de forma efectiva, y no nos detendremos en las pocas características del shell que están pensadas específicamente para los programadores de sistemas de bajo nivel.
\section{Historia de los Shells de Unix}
La independencia del shell del sistema operativo Unix en sí mismo ha llevado al desarrollo de docenas de shells a lo largo de la historia de Unix, aunque sólo unos pocos han logrado un uso generalizado.
El primer intérprete de comandos importante fue el intérprete de comandos Bourne (llamado así por su inventor, Stephen Bourne); se incluyó en la primera versión ampliamente popular de Unix, la versión 7, a partir de 1979. El shell Bourne se conoce en el sistema como sh. Aunque Unix ha pasado por muchos, muchos cambios, el shell Bourne sigue siendo popular y esencialmente sin cambios. Varias utilidades y funciones de administración de Unix dependen de él.
El primer shell alternativo ampliamente utilizado fue el shell C, o csh. Fue escrito por Bill Joy en la Universidad de California en Berkeley como parte de la versión de Unix de la Berkeley Software Distribution (BSD) que salió un par de años después de la versión 7. Está incluido en casi todas las versiones recientes de Unix. (Una variante popular es el llamado Twenex csh, tcsh).
El shell C recibe su nombre del parecido de sus comandos con las declaraciones del lenguaje de programación C, lo que hace que el shell sea más fácil de aprender para los programadores de los sistemas Unix. Soporta una serie de características del sistema operativo (por ejemplo, el control de trabajos; véase el \hyperref[sec:Chapter8]{Capítulo 8}) que antes eran exclusivas de BSD Unix pero que ahora han migrado a casi todas las demás versiones modernas. También tiene algunas características importantes (por ejemplo, los alias; véase el \hyperref[sec:Chapter3]{Capítulo 3}) que facilitan su uso en general.
\subsection{El Shell de Korn}
El shell Korn, o \emph{ksh}, fue inventado por David Korn de AT\&T Bell Laboratories a mediados de la década de 1980. Es casi totalmente compatible con el shell Bourne,\footnote{Con algunas excepciones extremadamente pequeñas. Vea el \hyperref[sec:Chapter10]{Capítulo 10} para la única importante.}
lo que significa que los usuarios del shell Bourne pueden utilizarlo inmediatamente, y todas las utilidades del sistema que utilizan el shell Bourne pueden utilizar el shell Korn en su lugar. De hecho, algunos sistemas tienen el shell Korn instalado como si fuera el shell Bourne.
El intérprete de comandos Korn comenzó su vida pública en 1986 como parte del <<Toolchest Experimental>> de AT\&T, lo que significa que su código fuente estaba disponible a muy bajo costo para cualquiera que estuviera dispuesto a usarlo sin soporte técnico y con el conocimiento de que todavía podría tener algunos errores. Finalmente, los Laboratorios de Sistemas Unix (USL) de AT\&T decidieron darle soporte completo como utilidad Unix. A partir de la versión de Unix de USL llamada System V Release 4 (SVR4 para abreviar, 1989), se distribuyó con todos los sistemas Unix de USL, todas las versiones de Unix de terceros derivadas de SVR4 y muchas otras versiones.
A finales de 1993, David Korn publicó una versión más reciente, conocida popularmente como ksh93. Esta versión se distribuye con muchos sistemas Unix comerciales como parte del Entorno Común de Escritorio (CDE), normalmente como el <<shell Korn de escritorio>>, \emph{/usr/dt/bin/dtksh}.
Aunque el propio Unix ha cambiado de propietario varias veces desde entonces, David Korn permaneció en los Laboratorios Bell hasta 1996, cuando AT\&T (voluntariamente, esta vez) se dividió en AT\&T Corporation, Lucent Technologies y NCR. En ese momento, se trasladó a AT\&T Research Laboratories desde Bell Labs (que siguió formando parte de Lucent). Aunque tanto Lucent como AT\&T conservaron todos los derechos del shell de Korn, todas las mejoras y cambios provienen ahora de David Korn en AT\&T.
El 1 de marzo de 2000, AT\&T liberó el código fuente de ksh93 bajo una licencia de tipo Open Source. La obtención del código fuente se discute en el \hyperref[sec:ApendiceC]{Apéndice C}, y la licencia se presenta en el \hyperref[sec:ApendiceD]{Apéndice D}.
Este libro se centra en la versión 2001 de \emph{ksh93}. Ocasionalmente, el libro señala una diferencia significativa entre las versiones de 1993 y 1988. Cuando es necesario, las distinguimos como \emph{ksh93} y \emph{ksh88}, respectivamente. En el \hyperref[sec:ApendiceA]{Apéndice A} se describen las diferencias entre las versiones de 1988 y 1993 de forma ordenada, y en ese apéndice también se resumen brevemente otras versiones del shell.
\subsection{Características del Shell Korn}
Aunque el shell Bourne sigue siendo conocido como el shell <<estándar>>, el shell Korn también es popular. Además de su compatibilidad con el shell Bourne, incluye las mejores características del shell C, así como varias ventajas propias. También se ejecuta de forma más eficiente que cualquier shell anterior.
Los modos de edición de la línea de comandos del shell Korn son las características que suelen atraer a la gente al principio. Con la edición de la línea de comandos, es mucho más fácil volver atrás y corregir errores que con el mecanismo de historial del shell C -- y el shell Bourne no permite hacer esto en absoluto.
La otra característica principal del shell Korn que está pensada principalmente para los usuarios interactivos es el control de trabajos. Como se explica en el \hyperref[sec:Chapter8]{Capítulo 8}, el control de trabajos le da la capacidad de parar, iniciar y pausar cualquier número de comandos al mismo tiempo. Esta característica fue tomada casi literalmente del shell C.
El resto de las ventajas importantes del shell Korn están destinadas principalmente a los personalizadores y programadores del shell. Tiene muchas opciones y variables nuevas para la personalización, y sus características de programación se han ampliado significativamente para incluir la definición de funciones, más estructuras de control, expresiones regulares y aritmética incorporadas, matrices asociativas, variables estructuradas, control avanzado de E/S, y más.
\section{Cómo Obtener el Shell Korn de 1993}
Este libro cubre la versión de 1993 del shell Korn. Gran parte de lo que se cubre es exclusivo de ese shell; un subconjunto de lo que es exclusivo se aplica sólo a las versiones recientes disponibles directamente de AT\&T. Para hacer un mejor uso de este libro, debería estar usando el shell Korn de 1993. Utilice la siguiente secuencia de instrucciones para determinar qué shell tiene actualmente y si el shell Korn de 1993 existe en su sistema, y para hacer que el shell Korn de 1993 sea su shell de inicio de sesión.
\begin{enumerate}
\item{Determine qué shell está utilizando. La variable SHELL indica su shell de inicio de sesión. Entre en su sistema y escriba \emph{echo \$SHELL} en el prompt. Verá una respuesta que contiene sh, csh, o ksh; estos denotan los shells Bourne, C, y Korn, respectivamente. (También es muy probable que esté utilizando un shell de terceros como bash o tcsh). Si la respuesta es ksh, vaya al paso 3. De lo contrario, continúe con el paso 2.}
\item{Vea si existe alguna versión de ksh en su sistema en un directorio estándar. Escriba ksh. Si eso funciona (imprime un prompt \$), tiene una versión del shell Korn; continúe con el paso 3. De lo contrario, continúe con el paso 5.}
\item{Compruebe la versión. Escriba \emph{echo \${.sh.version}}. Si eso imprime una versión, está todo listo; omita el resto de estas instrucciones. De lo contrario, continúe con el paso 4.}
\item{No tiene la versión 1993 del shell Korn. Para saber qué versión tienes, escribe el comando \emph{set -o emacs}, y luego presiona CTRL-V. Esto te dirá si tienes la versión de 1988 o el shell Korn de dominio público. En cualquier caso, continúe con el paso 5.}
\item{Escribe el comando \texttt{/usr/dt/bin/dtksh}. Si esto le da un prompt \emph{\$}, usted tiene el Desktop Korn Shell, que está basado en una versión temprana de ksh93. Puede utilizar esta versión; casi todo lo que se dice en este libro funcionará. Vaya al paso 7.}
\item{Necesita descargar una versión ejecutable de \emph{ksh93} o descargar el código fuente y construir un ejecutable a partir de él. Estas tareas se describen en el \hyperref[sec:ApendiceC]{Apéndice C}. Lo mejor sería solicitar la ayuda de su administrador de sistemas para este paso. Una vez que tenga un \emph{ksh93} que funcione, continúe con el paso 7.}
\item{Instale \emph{ksh93} como su shell de inicio de sesión. Hay dos situaciones; elija la que le convenga:}
\begin{description}
\item[Sistema monousuario] En un sistema monousuario, en el que usted es el administrador, probablemente tendrá que añadir la ruta completa a \emph{ksh93} al archivo \emph{/etc/shells} como primer paso. Luego, debería poder cambiar su shell de inicio de sesión escribiendo \emph{chsh nombre-k}, donde \emph{nombre-k} es la ruta completa al ejecutable \emph{ksh93}. Si esto funciona, se te pedirá tu contraseña. Escriba su contraseña, salga y vuelva a entrar para empezar a usar el shell Korn. \\
Si \emph{chsh} no existe o no funciona, consulte la página man de \emph{passwd(1)}. Busque las opciones \emph{-e} o \emph{-s} para actualizar la información de su archivo de contraseñas. Utilice lo que sea apropiado para su sistema para cambiar su shell de inicio de sesión. \\
Si nada de lo anterior funciona, puede recurrir a editar el archivo \emph{/etc/passwd} mientras está conectado como root. Si tienes el comando vipw(8), deberías usarlo para editar tu archivo de contraseñas. Si no, edita el archivo manualmente con tu editor de texto favorito.
\item[Sistema multiusuario grande] Esta situación es aún más compleja que el caso de un solo usuario. Lo mejor es dejar que el administrador del sistema se encargue de cambiar el shell por ti. La mayoría de las instalaciones grandes tienen un <<servicio de ayuda>> (accesible por correo electrónico o por teléfono, o ambos) para introducir dichas solicitudes.
\end{description}
\end{enumerate}
\section{Uso Interactivo del Shell}
Cuando usas el shell de forma interactiva, entras en una sesión de inicio de sesión que comienza cuando te conectas y termina cuando sales o presionas CTRL-D.\footnote{Puedes configurar tu shell para que no acepte CTRL-D, es decir, que requiera que escribas exit para terminar tu sesión. Recomendamos esto, porque CTRL-D es demasiado fácil de escribir por accidente; vea la sección de opciones en el \hyperref[sec:Chapter3]{Capítulo 3}.}
Durante una sesión de inicio de sesión, escribes líneas de comando en el shell; estas son líneas de texto que terminan en ENTER y que escribes en tu terminal o estación de trabajo.\footnote{Aunque asumimos que hay pocas personas que todavía usan terminales seriales reales, los sistemas modernos de ventanas proporcionan acceso al shell a través de un emulador de terminal. Por lo tanto, al menos cuando se trata del uso interactivo del shell, el término <<terminal>> se aplica igualmente a un entorno de ventanas.}
Por defecto, el shell te pide cada comando con un signo de dólar, aunque, como verás en el Capítulo 3, el prompt puede ser cambiado.
\subsection{Comandos, Argumentos y Opciones}
Las líneas de comandos del shell consisten en una o más palabras, que se separan en una línea de comandos por espacios o TABs. La primera palabra de la línea es el comando. El resto (si lo hay) son argumentos (también llamados parámetros) del comando, que son nombres de cosas sobre las que actuará el comando.
Por ejemplo, la línea de comandos \texttt{lpr myfile} está formada por el comando \emph{lpr} (imprimir un archivo) y el único argumento \emph{myfile}. \emph{lpr} trata \emph{myfile} como el nombre de un archivo a imprimir. Los argumentos suelen ser nombres de archivos, pero no necesariamente: en la línea de comandos \texttt{mail billr}, el programa de correo trata a \emph{billr} como el nombre del usuario al que se enviará un mensaje.
Una opción es un tipo especial de argumento que da al comando información específica sobre lo que debe hacer. Las opciones suelen consistir en un guión seguido de una letra; decimos <<suelen>> porque se trata de una convención más que de una regla rígida. El comando \texttt{lpr -h miarchivo} contiene la opción \emph{-h}, que le dice a \emph{lpr} que no imprima la <<página de bandera>> antes de imprimir el archivo.
A veces las opciones tienen sus propios argumentos. Por ejemplo, \texttt{lpr -P hp3si -h miarchivo} tiene dos opciones y un argumento. La primera opción es \emph{-P hp3si} , que significa <<Enviar la salida a la impresora llamada hp3si>>. La segunda opción y el argumento son como los anteriores.
\subsection{Ayuda Incorporada (Built-in)}
Casi todos los comandos incorporados en ksh tienen ayuda <<en línea>> tanto mínima como más extensa. Si le das a un comando la opción -?, imprime un breve resumen de uso:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (*\textbf{cd -?}*)
Usage: cd [-LP] [directory]
Or: cd [ options ] old new
\end{lstlisting}
(Es posible que desee citar la \texttt{?}, ya que, como veremos más adelante, es especial para el shell). También puede dar la opción \emph{--man} para imprimir la ayuda en la forma de la tradicional página man de Unix\footnote{A partir de \emph{ksh93i}.}
La salida usa secuencias de escape estándar ANSI para producir un cambio visible en la pantalla, representado aquí usando una fuente en negrita:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (*\textbf{cd --man}*)
NAME
cd - change working directory
SYNOPSIS
(*\textbf{cd} [ \textbf{options}*) ] [directory]
(*\textbf{cd} [ \textbf{options}*) ] old new
DESCRIPTION
(*\textbf{cd}*) changes the current working directory of the current shell
environment.
In the first form with one operand, if (*\textbf{directory}*) begins with /,
or if the first component is . or .., the directory will be
changed to this directory...
\end{lstlisting}
Del mismo modo, la opción \emph{--html} produce la salida en formato HTML para su posterior renderización con un navegador web.
Por último, la opción \emph{--nroff} le permite producir la ayuda de cada comando en forma de entrada \emph{nroff} -man\footnote{Todas las opciones de ayuda envían su salida a error estándar (que se describe más adelante en este capítulo). Esto significa que tiene que usar las facilidades del shell que no cubriremos hasta el \hyperref[sec:Chapter7]{Capítulo 7} para capturar su salida. Por ejemplo, cd --man 2>\&1 | more ejecuta la ayuda en línea a través del programa paginador more.},
lo que resulta práctico para dar formato a la ayuda para la salida impresa.
Para cumplir con POSIX, algunos comandos no aceptan estas opciones: \emph{echo, false, jobs, login, newgrp, true} y :. Para \emph{test}, tienes que escribir \texttt{test - -man - -} para obtener la ayuda en línea.
\section{Ficheros}
Aunque los argumentos de los comandos no siempre son ficheros, los ficheros son los tipos de <<cosas>> más importantes en cualquier sistema Unix. Un archivo puede contener cualquier tipo de información, y hay diferentes tipos de archivos. Cuatro tipos son, con mucho, los más importantes:
\begin{description}
\item[Archivos normales] También llamados \emph{archivos de texto}; contienen caracteres legibles. Por ejemplo, este libro se creó a partir de varios archivos regulares que contienen el texto del libro más instrucciones de formato DocBook XML legibles por humanos.
\item[Archivos ejecutables] También llamados programas; se invocan como comandos. Algunos no pueden ser leídos por humanos; otros - los scripts del shell que examinaremos en este libro - son sólo archivos de texto especiales. El propio shell es un archivo ejecutable (no legible por humanos) llamado ksh.
\item[Directorios] Como carpetas que contienen otros archivos - posiblemente otros directorios (llamados subdirectorios).
\item[Enlaces simbólicos] Una especie de <<atajo>> de un lugar a otro en la jerarquía de directorios del sistema. Veremos más adelante en este capítulo cómo los enlaces simbólicos pueden afectar al uso interactivo del shell Korn.
\end{description}
\subsection{Directorios}
Repasemos los conceptos más importantes sobre los directorios. El hecho de que los directorios puedan contener otros directorios da lugar a una estructura jerárquica, más popularmente conocida como árbol, para todos los ficheros de un sistema Unix. La Figura \ref{fig2} muestra parte de un árbol de directorios típico; los óvalos son ficheros normales y los rectángulos son directorios.
\begin{figure}[h!]
\centering
\includegraphics[scale=1]{fig_02}
\caption{\small{Árbol de directorios y archivos}}
\label{fig2}
\end{figure}
La parte superior del árbol es un directorio llamado <<raíz>> o <<root>> que no tiene nombre en el sistema\footnote{La mayoría de los tutoriales introductorios de Unix dicen que root tiene el nombre /. Nos quedamos con esta explicación alternativa porque es más consistente lógicamente.}.
Todos los archivos pueden nombrarse expresando su ubicación en el sistema en relación con la raíz; dichos nombres se construyen enumerando todos los nombres de directorio (en orden desde la raíz), separados por barras (/), seguidos del nombre del archivo. Esta forma de nombrar los archivos se denomina \emph{ruta completa (o absoluta)}.
Por ejemplo, supongamos que hay un archivo llamado \emph{memo} en el directorio \emph{fred}, que está en el directorio \emph{home}, que está en el directorio raíz. La ruta completa de este fichero es \texttt{/home/fred/memo}.
\subsubsection{El Directorio de Trabajo}
Por supuesto, es molesto tener que utilizar nombres de ruta completos cada vez que necesitas especificar un archivo, por lo que también existe el concepto de \emph{directorio de trabajo} (a veces llamado \emph{directorio actual}), que es el directorio en el que te encuentras en un momento dado. Si se da un nombre de ruta sin barra inicial, la ubicación del archivo se calcula en relación con el directorio de trabajo. Este tipo de nombres de ruta se denominan nombres de ruta relativos; los utilizarás con mucha más frecuencia que los nombres de ruta completos.
Cuando te conectas al sistema, tu directorio de trabajo se establece inicialmente en un directorio especial llamado tu directorio personal (o \emph{de inicio de sesión}). Los administradores de sistemas a menudo configuran el sistema para que el nombre del directorio personal de cada usuario sea el mismo que su nombre de inicio de sesión, y todos los directorios personales están contenidos en un directorio común bajo root. En la actualidad, es habitual utilizar \emph{/home} como directorio principal para los directorios personales.
Por ejemplo, \emph{/home/billr} es un directorio personal típico. Si este es tu directorio de trabajo y das el comando \texttt{lp memo}, el sistema busca el archivo memo en \emph{/home/billr}. Si tienes un directorio llamado bob en tu directorio personal, y contiene el archivo statrpt, puedes imprimir \emph{statrpt} con el comando \texttt{lp bob/statrpt}.
\subsubsection{Notación con tilde}
Como puedes imaginar, los directorios personales aparecen a menudo en los nombres de ruta. Aunque muchos sistemas están organizados de forma que todos los directorios personales tienen un directorio principal común (como \emph{/home}), no debería depender de que esto sea así, ni siquiera debería saber cuál es el nombre absoluto del directorio personal de alguien.
Por lo tanto, el shell Korn tiene una forma de abreviar los directorios home: basta con preceder el nombre del usuario con una tilde (\textasciitilde). Por ejemplo, puedes referirte al archivo memo en el directorio personal del usuario fred como \emph{\textasciitilde{} fred/memo}. Se trata de una ruta absoluta, por lo que no importa cuál sea tu directorio de trabajo cuando la utilices. Si el directorio personal de fred tiene un subdirectorio llamado bob y el archivo está allí, puedes usar \emph{\textasciitilde{} fred/bob/memo} como nombre.
Aún más conveniente, una tilde por sí misma se refiere a su propio directorio personal. Puede referirse a un archivo llamado \emph{notas} en su directorio personal como \emph{\textasciitilde{} /notas} (observe la diferencia entre eso y \emph{\textasciitilde{} notas}, que el shell trataría de interpretar como el directorio personal del usuario notas). Si \emph{notes} está en tu subdirectorio bob, puedes llamarlo \emph{\textasciitilde{} /bob/notes}. Esta notación es más útil cuando su directorio de trabajo no está en su árbol de directorios personales, por ejemplo, cuando es algún directorio del <<sistema>> como \emph{/tmp}.
\subsubsection{Cambiar los Directorios de Trabajo}
Si desea cambiar su directorio de trabajo, utilice el comando \emph{cd}. Si no recuerda su directorio de trabajo, el comando \emph{pwd} indica al shell que lo imprima.
\emph{cd} toma como argumento el nombre del directorio que desea que se convierta en su directorio de trabajo. Puede ser relativo a su directorio actual, puede contener una tilde, o puede ser absoluto (comenzando con una barra). Si omite el argumento, \emph{cd} cambia a su directorio personal (es decir, es lo mismo que \texttt{cd \~ }).
La Tabla \ref{tab1} muestra algunos ejemplos de comandos \emph{cd}. Cada comando asume que tu directorio de trabajo es \emph{/home/billr} justo antes de que se ejecute el comando, y que tu estructura de directorios se parece a la Figura \ref{fig2}.
\begin{table}[h]
\center
\caption{Ejemplos de comandos cd}
\label{tab1}
\begin{tabular}{m{4cm}|m{4cm}} \hline
\textbf{Comando} & \textbf{Nuevo directorio de trabajo} \\ \hline
\texttt{cd bob} & \emph{/home/billr/bob} \\
\texttt{cd bob/dave} & \emph{/home/billr/bob/dave} \\
\texttt{cd \textasciitilde{}/bob/dave} & \emph{/home/billr/bob/dave} \\
\texttt{cd /usr/lib} & \emph{/usr/lib} \\
\texttt{cd ..} & \emph{/home} \\
\texttt{cd ../pete} & \emph{/home/pete} \\
\texttt{cd \textasciitilde{}pete} & \emph{/home/pete} \\
\texttt{cd billr pete} & \emph{/home/pete} \\
\texttt{cd illr arry} & \emph{/home/barry} \\
\end{tabular}
\end{table}
Los cuatro primeros son sencillos. Los dos siguientes utilizan un directorio especial llamado .. (dos puntos, pronunciado <<dot dot>>), que significa <<padre de este directorio>>. Todos los directorios tienen uno de estos; es una forma universal de llegar al directorio situado por encima del actual en la jerarquía, que se denomina directorio padre.
Cada directorio también tiene el directorio especial . (punto simple), que sólo significa <<este directorio>>. Por lo tanto, \texttt{cd .} efectivamente no hace nada. Tanto . como .. son en realidad ficheros especiales ocultos en cada directorio que apuntan al propio directorio y a su directorio padre, respectivamente. El directorio raíz es su propio directorio padre.
Los dos últimos ejemplos de la tabla utilizan una nueva forma del comando \emph{cd}, que no está incluida en la mayoría de los shells Bourne. La forma es \texttt{cd viejo nuevo}. Toma la ruta completa del directorio de trabajo actual e intenta encontrar la cadena \emph{old} en ella. Si encuentra la cadena, la sustituye por \emph{new} y cambia al directorio resultante.
En el primero de los dos ejemplos, el shell sustituye \emph{pete} por \emph{billr} en el nombre del directorio actual y convierte el resultado en el nuevo directorio actual. El último ejemplo muestra que la sustitución no necesita ser un nombre de archivo completo: al sustituir \emph{arry} por \emph{illr} en \emph{/home/billr} se obtiene \emph{/home/barry}. (Si no se puede encontrar la cadena antigua en el nombre del directorio actual, el intérprete de comandos imprime un mensaje de error).
Otra característica del comando cd del shell Korn es la forma \texttt{cd -}, que cambia al directorio en el que se encontraba antes del actual. Por ejemplo, si empiezas en \emph{/usr/lib}, escribes \texttt{cd} sin un argumento para ir a tu directorio personal, y luego escribes \texttt{cd -}, estarás de vuelta en \emph{/usr/lib}.
\subsubsection{Enlaces Simbólicos a Directorios}
Los sistemas Unix modernos ofrecen \emph{enlaces simbólicos}. Los enlaces simbólicos (a veces llamados \emph{enlaces blandos}) proporcionan una especie de <<acceso directo>> a los archivos en una parte diferente de la jerarquía de archivos del sistema. Se puede crear un enlace simbólico a un fichero o a un directorio, utilizando nombres de ruta completos o relativos. Cuando se accede a un fichero o directorio mediante un enlace simbólico, Unix <<sigue el enlace>> hasta el fichero o directorio real.
Los enlaces simbólicos a directorios pueden generar comportamientos sorprendentes. Para explicar por qué, vamos a empezar asumiendo que estás usando el shell Bourne normal, sh.\footnote{Si tienes un sistema donde el shell Korn está instalado como /bin/sh, este ejemplo no funcionará}
Ahora, supongamos que nosotros y el usuario fred estamos trabajando juntos en un proyecto, y el directorio primario para el proyecto está bajo su directorio personal, digamos \texttt{/home/fred/projects/} \texttt{important/wonderprog}. Es un nombre de ruta bastante largo, incluso usando la notación con tilde (que no podemos usar en el shell Bourne, pero eso es otra historia). Para hacer la vida más fácil, vamos a crear un enlace simbólico al directorio \emph{wonderprog} en nuestro directorio personal:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (*\textbf{sh} \emph{Utilizar el shell Bourne}*)
$ (*\textbf{cd} \emph{Asegurarse de que se estamos en nuestro directorio de inicio}*)
$ (*\textbf{pwd} \emph{Mostrar el directorio en el que estamos}*)
/home/billr
(*\emph{Crear el enlace suave}*)
$ (*\textbf{ln -s /home/fred/projects/important/wonderprog wonderprog}*)
\end{lstlisting}
Ahora, cuando escribimos \texttt{cd wonderprog}, terminamos en \texttt{/home/fred/projects/important} \texttt{/wonderprog}:
\begin{lstlisting}[language=bash]
$ cd wonderprog
$ pwd
/home/fred/projects/important/wonderprog
\end{lstlisting}
Después de trabajar un rato añadiendo nuevas e importantes características\footnote{Las <<importantes>> son las que desea el departamento de marketing, las necesiten o no los clientes.} a \emph{wonderprog}, recordamos que necesitamos actualizar el archivo \emph{.profile} en nuestro directorio home. No hay problema: simplemente \emph{cd} de nuevo allí y empezar a trabajar en el archivo, mirándolo primero con \emph{more}.
\begin{lstlisting}[language=bash]
$ cd ..
Retrocedemos un nivel
$ more .profile
Observamos .profile
.profile: No such file or directory
\end{lstlisting}
¿Qué ha pasado? El \texttt{cd ..} no nos llevó de vuelta por donde vinimos. En su lugar, subió un nivel en la jerarquía \emph{física} del sistema de archivos:
\begin{lstlisting}[language=bash]
$ pwd
/home/fred/projects/important
\end{lstlisting}
Este es el <<problema>> de los enlaces simbólicos: la visión lógica de la jerarquía del sistema de ficheros que presenta un enlace simbólico a un directorio se rompe con la realidad física subyacente cuando se accede al directorio padre.
El shell Korn funciona de forma diferente. Entiende los enlaces simbólicos y, por defecto, siempre te presenta una vista lógica del sistema de ficheros. No sólo \emph{cd} está integrado en el shell, sino también \emph{pwd}. Ambos comandos aceptan las mismas dos opciones: \emph{-L}, para realizar operaciones lógicas (por defecto), y \emph{-P}, para realizar las operaciones en los directorios reales. Empecemos de nuevo en el shell Korn:
\begin{lstlisting}[language=bash]
$ cd wonderprog ; pwd
# cd a traves del enlace simbolico
/home/billr/wonderprog # La respuesta es la ubicacion logica
$ pwd -P
# Cual es la ubicacion fisica
/home/fred/projects/important/wonderprog # La respuesta es la ubicacion fisica
$ cd .. ; pwd
# Sube un nivel
/home/billr # El trayecto fue de nuevo logico
$ cd -P wonderprog; pwd
# Hacer un cd fisico
/home/fred/projects/important/wonderprog # logico es ahora igual a fisico
cd .. ; pwd
# Vuelve a subir un nivel
/home/fred/projects/important # logico sigue siendo igual a fisico
\end{lstlisting}
Como se muestra, la opción \emph{-P} de \emph{cd} y \emph{pwd} le permite <<evitar>> el uso por defecto del posicionamiento lógico del shell Korn. La mayoría de las veces, sin embargo, el posicionamiento lógico es exactamente lo que quieres.
\textbf{NOTA}: El intérprete de comandos establece las variables PWD y OLDPWD de forma correspondiente cada vez que se realiza un \emph{cd}; los resultados de escribir \emph{pwd} y \emph{print \$PWD} deberían ser siempre los mismos.
Como nota no relacionada que redondea la discusión, los sistemas Unix también proporcionan <<enlaces duros>> (o simplemente \emph{enlaces}) a los archivos. Cada nombre para un fichero se llama enlace; todos los enlaces duros se refieren a los mismos datos en el disco, y si el fichero cambia de nombre, ese cambio se ve al mirarlo desde un nombre diferente. Los enlaces duros tienen ciertas restricciones, que los enlaces simbólicos superan. (Véase \emph{ln(1)} para más información). Sin embargo, no puedes hacer enlaces duros a directorios, así que los enlaces simbólicos son todo lo que importa para \emph{cd} y \emph{pwd}.
\subsection{Nombres de Archivo y Comodines}
A veces es necesario ejecutar un comando en más de un archivo a la vez. El ejemplo más común de este tipo de órdenes es \emph{ls}, que muestra información sobre los ficheros. En su forma más simple, sin opciones ni argumentos, enumera los nombres de todos los archivos del directorio de trabajo excepto los archivos ocultos especiales, cuyos nombres empiezan por un punto (.).
Si le das a \emph{ls} argumentos de nombre de archivo, listará esos archivos, lo cual es un poco tonto: si tu directorio actual tiene los archivos bob y fred, y escribes \texttt{ls bob fred}, el sistema simplemente te devolverá los nombres de los archivos.
En realidad, \texttt{ls} se usa más a menudo con opciones que le dicen que liste información sobre los ficheros, como la opción \emph{-l} (long), que le dice a \emph{ls} que liste el propietario del fichero, el grupo, el tamaño, la hora de la última modificación y otra información, o \emph{-a} (all), que también lista los ficheros ocultos descritos anteriormente. Pero a veces quieres verificar la existencia de un determinado grupo de archivos sin tener que saber todos sus nombres; por ejemplo, si diseñas páginas web, puede que quieras ver qué archivos de tu directorio actual tienen nombres que terminan en \emph{.html}.
Los nombres de fichero son tan importantes en Unix que el shell proporciona una forma integrada de especificar el patrón de un conjunto de nombres de fichero sin tener que conocer todos los nombres en sí. Puede utilizar caracteres especiales, llamados \emph{comodines}, en los nombres de archivo para convertirlos en patrones. Mostraremos los tres tipos básicos de comodines que soportan los principales shells de Unix, y dejaremos el conjunto de operadores comodín avanzados del shell Korn para el \hyperref[sec:Chapter4]{Capítulo 4}. La Tabla \ref{tab2} lista los comodines básicos.
\begin{table}[h]
\center
\caption{Comodines básicos}
\label{tab2}
\begin{tabular}{m{2cm}|m{8cm}} \hline
\textbf{Comodín} & \textbf{Coincidencia} \\ \hline
? & Cualquier carácter \\
\texttt{*} & Cualquier cadena de caracteres \\
\texttt{[set]} & Cualquier caracter del conjunto \\
\texttt{[!set]} & Cualquier carácter no incluido en el conjunto \\
\end{tabular}
\end{table}
El comodín \texttt{?} coincide con cualquier carácter, de modo que si el directorio contiene los archivos \emph{program.c, program.log} y \emph{program.o}, la expresión \texttt{program.?} coincide con \emph{program.c} y \emph{program.o}, pero no con \emph{program.log}.
El asterisco (*) es más potente y se utiliza mucho más; coincide con cualquier cadena de caracteres. La expresión \texttt{program.*} coincidirá con los tres archivos del párrafo anterior; los diseñadores web pueden utilizar la expresión \texttt{*.html} para coincidir con sus archivos de entrada\footnote{Los usuarios de MS-DOS, Windows y OpenVMS deben tener en cuenta que el punto (.) no tiene nada de especial en los nombres de archivo Unix (aparte del punto inicial, que <<oculta>> el archivo); es simplemente otro carácter. Por ejemplo, ls * lista todos los ficheros del directorio actual; no se necesita *.* como en otros sistemas.}.
La Tabla \ref{tab3} debería darte una mejor idea de cómo funciona el asterisco. Suponga que tiene los archivos \emph{bob, darlene, dave, ed, frank} y \emph{fred} en su directorio de trabajo.
Observe que * puede no significar nada: tanto \texttt{*ed} como \texttt{*e*} coinciden con \emph{ed}. Observe también que el último ejemplo muestra lo que hace el intérprete de comandos si no puede encontrar ninguna coincidencia: simplemente deja la cadena con el comodín intacta.
\begin{table}[h]
\center
\caption{Usando el comodín *}
\label{tab3}
\begin{tabular}{p{3cm}|p{6cm}} \hline
\textbf{Expresión} & \textbf{Campos} \\ \hline
fr* & frank fred \\
*ed & ed fred \\
b* & bob \\
*e* & darlene dave ed fred \\
*r* & darlene frank fred \\
\texttt{*} & bob darlene dave ed frank fred \\
d*e & darlene dave \\
g* & g*
\end{tabular}
\end{table}
Los archivos se guardan dentro de los directorios en un orden no especificado; el shell ordena los resultados de cada expansión de comodines. (En algunos sistemas, la ordenación puede estar sujeta a un orden apropiado para la ubicación del sistema, pero que es diferente del orden de cotejo subyacente de la máquina. Los tradicionalistas de Unix pueden utilizar \texttt{export LANG=C} para obtener el comportamiento al que están acostumbrados).
El comodín restante es la construcción set. Un conjunto es una lista de caracteres (por ejemplo, abc), un rango inclusivo (por ejemplo, a-z) o una combinación de ambos. Si desea que el guión forme parte de una lista, sólo tiene que ponerlo en primer o último lugar. La Tabla \ref{tab4} (que asume un entorno ASCII) debería explicar las cosas más claramente.
\begin{table}[h]
\center
\caption{Uso de los comodines de construcción de conjuntos}
\label{tab4}
\begin{tabular}{m{3cm}|m{10cm}} \hline
\textbf{Expresión} & \textbf{Coincidencia} \\ \hline
\texttt{[abc]} & a, b, ó c \\
\texttt{[.,;]} & punto, coma o punto y coma \\
\texttt{[\texttt{-}\_]} & Guión medio o guión bajo \\
\texttt{[a-c]} & a, b ó c \\
\texttt{[a-z]} & Todas las letras minúsculas \\
\texttt{[!0-9]} & Todos los no dígitos \\
\texttt{[0-9!]} & Todos los dígitos y signos de exclamación \\
\texttt{[a-zA-Z]} & Todas las letras mayúsculas y minúsculas \\
\texttt{[a-zA-Z0-9\_\texttt{-}]} & Todas las letras, todos los dígitos, guión bajo y guión \\
\end{tabular}
\end{table}
En el ejemplo del comodín original, \texttt{program.[co]} y \texttt{program.[a-z]} coinciden con \emph{program.c} y \emph{program.o}, pero no con \emph{program.log}.
Un signo de exclamación tras el corchete izquierdo permite <<negar>> un conjunto. Por ejemplo, \texttt{[!.;]} coincide con cualquier carácter excepto punto y coma; \texttt{[!a-zA-Z]} coincide con cualquier carácter que no sea una letra.
La notación de rango es útil, pero no debe hacer demasiadas suposiciones sobre los caracteres que se incluyen en un rango. Por lo general, es seguro utilizar un rango para letras mayúsculas, minúsculas, dígitos o cualquier subrango de éstos (por ejemplo, \texttt{[f-q], [2-6]}). No utilices rangos en caracteres de puntuación o letras mixtas: por ejemplo, no se debe confiar en que \texttt{[a-Z]} y \texttt{[A-z]} incluyan todas las letras y nada más. El problema es que esos rangos no son del todo portables entre distintos tipos de ordenadores\footnote{En concreto, los rangos dependen del esquema de codificación de caracteres que utilice tu ordenador. La gran mayoría utiliza ASCII, pero los mainframes IBM utilizan EBCDIC. (En realidad, en los sistemas EBCDIC, ni siquiera las mayúsculas y minúsculas forman un rango contiguo).}.
Otro problema es que los sistemas modernos admiten diferentes configuraciones regionales, que son formas de describir cómo funciona el juego de caracteres local. En la mayoría de los países, el conjunto de caracteres por defecto de la configuración regional es diferente al del ASCII plano. En el \hyperref[sec:Chapter4]{Capítulo 4}, le mostramos cómo usar expresiones POSIX entre corchetes para denotar letras, dígitos, puntuación y otros tipos de caracteres de una forma portable.
El proceso de hacer coincidir expresiones que contienen comodines con nombres de archivo se denomina \emph{expansión de comodines}. Este es sólo uno de los varios pasos que da el shell al leer y procesar una línea de órdenes; otro que ya hemos visto es la \emph{expansión de tildes}, en la que las tildes se sustituyen por directorios de inicio cuando procede. Veremos otros en capítulos posteriores, y los detalles completos del proceso se enumeran en el \hyperref[sec:Chapter7]{Capítulo 7}.
Sin embargo, es importante tener en cuenta que los comandos que ejecutas sólo ven los resultados de la expansión de comodines. (De hecho, esto es cierto para todas las expansiones.) Es decir, sólo ven una lista de argumentos, y no tienen conocimiento de cómo surgieron esos argumentos. Por ejemplo, si escribe \texttt{ls fr*} y sus archivos son como los descritos anteriormente, entonces el shell expande la línea de comandos a \texttt{ls fred frank} e invoca el comando \emph{ls} con los argumentos \emph{fred} y \emph{frank}. Si escribe \texttt{ls g*}, entonces (porque no hay coincidencia) \emph{ls} recibirá la cadena literal \texttt{g*} y se quejará con el mensaje de error, \texttt{g* no encontrado}. \footnote{Esto es diferente del mecanismo de comodines del shell de C, que imprime un mensaje de error y no ejecuta el comando en absoluto.} (Es probable que el mensaje real varíe de un sistema a otro).
He aquí otro ejemplo que le ayudará a entender por qué esto es importante. Supongamos que eres un programador en C. Esto significa que trabajas con archivos cuyos nombres terminan en \texttt{.c} (programas, también conocidos como archivos fuente), \texttt{.h} (archivos de cabecera para programas) y \texttt{.o} (archivos de código objeto que no son legibles por humanos), así como otros archivos.
Supongamos que quieres listar todos los archivos fuente, objeto y de cabecera de tu directorio de trabajo. El comando \texttt{ls *.[cho]} hace el truco. El shell expande \texttt{*.[cho]} a todos los archivos cuyos nombres terminan en un punto seguido de una \texttt{c, h}, u \texttt{o} y pasa la lista resultante a ls como argumentos.
En otras palabras, \emph{ls} verá los nombres de los ficheros como si se hubieran tecleado individualmente - ¡pero fíjese que no hemos asumido ningún conocimiento de los nombres reales de los ficheros! Dejamos que los comodines hagan el trabajo.
A medida que adquiera experiencia con el shell, reflexione sobre cómo sería la vida sin comodines. Bastante miserable, diríamos nosotros.
Una nota final sobre los comodines. Puede asignar a la variable \texttt{FIGNORE} un patrón del shell que describa los nombres de archivo que deben ignorarse durante la comparación de patrones. (Las capacidades completas de patrones del shell se describen más adelante, en el \hyperref[sec:Chapter4]{Capítulo 4}.) Por ejemplo, \emph{emacs} guarda copias de seguridad de archivos añadiendo un \texttt{\~} al nombre original. A menudo, no necesitas ver estos archivos. Para ignorarlos, puedes añadir lo siguiente a tu archivo \emph{.profile}:
\begin{lstlisting}[language=bash]
export FIGNORE='*~'
\end{lstlisting}
Al igual que con la expansión de comodines, la prueba contra FIGNORE se aplica a todos los componentes de un nombre de ruta, no sólo al final.
\section{Entrada y Salida}
El campo del software -en realidad, cualquier campo científico- tiende a avanzar de forma más rápida e impresionante en esas pocas ocasiones en las que a alguien (es decir, no a un comité) se le ocurre una idea pequeña en concepto pero enorme en sus implicaciones. El esquema de entrada y salida estándar de Unix tiene que estar en la lista corta de tales ideas, junto con innovaciones clásicas como el lenguaje LISP, el modelo de datos relacional y la programación orientada a objetos.
El esquema de E/S de Unix se basa en dos ideas deslumbrantemente simples. En primer lugar, la E/S de archivos Unix adopta la forma de secuencias arbitrariamente largas de caracteres (bytes). Por el contrario, los sistemas de archivos más antiguos tienen esquemas de E/S más complicados (por ejemplo, <<bloque>>, <<registro>>, <<imagen de tarjeta>>, etc.). En segundo lugar, todo lo que en el sistema produce o acepta datos se trata como un archivo; esto incluye dispositivos de hardware como unidades de disco y terminales. Los sistemas antiguos trataban cada dispositivo de forma diferente. Ambas ideas han hecho la vida de los programadores de sistemas mucho más agradable.
\subsection{E/S Estándar}
Por convención, cada programa Unix tiene una única forma de aceptar entrada llamada \emph{entrada estándar}, una única forma de producir salida llamada \emph{salida estándar}, y una única forma de producir mensajes de error llamada \emph{salida de error estándar}, normalmente abreviada como \emph{error estándar}. Por supuesto, un programa puede tener también otras fuentes de entrada y salida, como veremos en el \hyperref[sec:Chapter7]{Capítulo 7}.
La E/S estándar fue el primer esquema de su clase que se diseñó específicamente para usuarios interactivos, en lugar del antiguo estilo de uso por lotes que normalmente implicaba barajas de tarjetas perforadas. Dado que el shell de Unix proporciona la interfaz de usuario, no debería sorprendernos que la E/S estándar se diseñara para encajar perfectamente con el shell.
Todos los shells manejan la E/S estándar básicamente de la misma manera. Cada programa que invocas tiene los tres canales de E/S estándar configurados para tu terminal o ventana de estación de trabajo, de modo que la entrada estándar es tu teclado, y la salida estándar y el error son tu pantalla o ventana. Por ejemplo, la utilidad de correo imprime mensajes para ti en la salida estándar, y cuando la usas para enviar mensajes a otros usuarios, acepta tu entrada en la entrada estándar. Esto significa que ves los mensajes en tu pantalla y escribes los nuevos en tu teclado.
Cuando sea necesario, puede redirigir la entrada y la salida para que procedan o vayan a un archivo en su lugar. Si quieres enviar el contenido de un archivo preexistente a alguien como correo, redirige la entrada estándar de correo para que lea de ese archivo en lugar de tu teclado.
También puedes conectar programas en una \emph{tubería (pipeline)}, en la que la salida estándar de un programa se alimenta directamente de la entrada estándar de otro; por ejemplo, podrías alimentar la salida de mail directamente al programa \emph{lp} para que los mensajes se impriman en lugar de mostrarse en pantalla.
Esto hace posible el uso de utilidades Unix como bloques de construcción para programas más grandes. Muchos programas de utilidades Unix están pensados para ser utilizados de esta manera: cada uno de ellos realiza un tipo específico de operación de filtrado en el texto de entrada. Aunque este no es un libro de texto sobre utilidades Unix, son esenciales para el uso productivo del shell. Las utilidades de filtrado más populares se listan en la Tabla \ref{tab5}
\begin{table}[h]
\center
\caption{Utilidades populares de filtrado de datos Unix}
\label{tab5}
\begin{tabular}{m{2cm}|m{11cm}} \hline
\textbf{Utilidad} & \textbf{Propósito} \\ \hline
cat & Copia la entrada estándar en la salida estándar \\
grep & Busca \emph{strings} en la entrada estándar \\
sort & Ordena las líneas de la entrada estándar \\
sed & Realiza operaciones de edición en la entrada estándar \\
tr & Traduce caracteres de la entrada estándar a otros caracteres \\
\end{tabular}
\end{table}
Es posible que haya utilizado alguna de estas utilidades antes y se haya dado cuenta de que toman nombres de archivos de entrada como argumentos y producen salida en la salida estándar. Sin embargo, puede que no sepa que todas ellas (y la mayoría de las utilidades de Unix) aceptan la entrada estándar si omite el argumento\footnote{Si una determinada utilidad Unix no acepta la entrada estándar cuando se omite el argumento nombre de fichero, pruebe a utilizar - como argumento. Esta es una convención común, aunque no universal.}.
Por ejemplo, la utilidad más básica es \emph{cat}, que simplemente copia su entrada a su salida. Si escribe \texttt{cat} con un argumento de nombre de archivo, imprimirá el contenido de ese archivo en su pantalla. Pero si lo invocas sin argumentos, leerá la entrada estándar y la copiará en la salida estándar. Pruébelo: \emph{cat} esperará a que escriba una línea de texto; cuando teclee ENTER, \emph{cat} repetirá el texto como un loro. Para detener el proceso, pulse CTRL-D al principio de una línea (vea más abajo lo que significa este carácter). Verás \textasciicircum D cuando teclees CTRL-D. Esto es lo que debería parecer:
\begin{lstlisting}[language=bash]
$ cat
Here is a line of text.
Here is a line of text.
This is another line of text.
This is another line of text.
^D
$
\end{lstlisting}
\subsection{Redirección de E/S}
\emph{cat} es en realidad la abreviatura de <<catenate>>, es decir, unir. Acepta varios argumentos de nombre de archivo y los copia en la salida estándar. Pero supongamos, por el momento, que cat y otras utilidades no aceptan argumentos de nombre de archivo y sólo aceptan la entrada estándar. Como dijimos anteriormente, el shell le permite redirigir la entrada estándar para que provenga de un archivo. La notación \texttt{command < filename} hace esto; configura las cosas para que \emph{command} tome la entrada estándar de un archivo en lugar de una terminal.
Por ejemplo, si usted tiene un archivo llamado fred que contiene algún texto, entonces \texttt{cat < fred} imprimirá el contenido de fred en su terminal. \texttt{sort < fred} ordenará las líneas en el archivo fred e imprimirá el resultado en su terminal (recuerde: estamos pretendiendo que las utilidades no toman argumentos de nombre de archivo).
Del mismo modo, \texttt{command > filename} hace que la salida estándar del comando sea redirigida al archivo nombrado. El clásico ejemplo <<canónico>> de esto es \texttt{date > now:} el comando \emph{date} imprime la fecha y hora actual en la salida estándar; el comando anterior la guarda en un archivo llamado \emph{now}.
Las redirecciones de entrada y salida pueden combinarse. Por ejemplo, el comando cp se usa normalmente para copiar archivos; si por alguna razón no existiera o estuviera roto, podrías usar cat de esta manera:
\begin{lstlisting}[language=bash]
cat < file1 > file2
\end{lstlisting}
Esto sería similar a \texttt{cp archivo1 archivo2}.
Como recurso mnemotécnico, piensa en < y > como <<embudos de datos>>. Los datos entran por el extremo grande y salen por el pequeño.
Cuando se utiliza de forma interactiva, el shell Korn le permite utilizar un comodín de shell después de un operador de redirección de E/S. Si el patrón coincide \emph{exactamente} con un archivo, ese archivo se utiliza para la redirección de E/S. De lo contrario, el patrón no se modifica y el shell intenta abrir un archivo cuyo nombre sea exactamente el que ha escrito. Además, no es válido intentar una redirección con la cadena nula como nombre de fichero (como podría ocurrir cuando se utiliza el valor de una variable, y la variable resulta estar vacía).
Por último, es tentador utilizar el mismo fichero tanto para la entrada como para la salida:
\begin{lstlisting}[language=bash]
sort < myfile > myfile
\end{lstlisting}
Esto no funciona. El shell trunca \emph{myfile} cuando lo abre para la salida, y no habrá ningún dato allí para que sort lo procese cuando se ejecute.
\subsection{Tuberías (pipes)}
También es posible redirigir la salida de un comando a la entrada estándar de otro comando en ejecución en lugar de a un archivo. La construcción que hace esto se llama tubería, anotada como |. Una línea de comandos que incluye dos o más comandos conectados con tuberías se denomina \emph{pipeline}.
Las tuberías se utilizan muy a menudo con el comando \emph{more}, que funciona igual que cat excepto que imprime su salida pantalla a pantalla, haciendo una pausa para que el usuario escriba SPACE (siguiente pantalla), ENTER (siguiente línea), u otros comandos. Si estás en un directorio con un gran número de ficheros y quieres ver detalles sobre ellos, \texttt{ls -l | more} te dará un listado detallado pantalla a pantalla.
Los pipelines pueden llegar a ser muy complejos (vea, por ejemplo, la función lsd en el \hyperref[sec:Chapter4]{Capítulo 4} o la versión pipeline del driver del compilador de C en el \hyperref[sec:Chapter7]{Capítulo 7}); también pueden combinarse con otros redireccionadores de E/S. Para ver un listado ordenado del fichero \emph{fred} pantalla a pantalla, escriba \texttt{sort < fred | more}. Para imprimirlo en lugar de verlo en su terminal, escriba \texttt{sort < fred | lp}.
He aquí un ejemplo más complicado. El archivo \texttt{/etc/passwd} almacena información sobre las cuentas de usuario en un sistema Unix. Cada línea del archivo contiene el nombre de usuario, el número de identificación de usuario, la contraseña encriptada, el directorio de inicio, el shell de inicio de sesión y otra información. El primer campo de cada línea es el nombre de usuario; los campos están separados por dos puntos (:). Una línea de ejemplo podría tener este aspecto:
\begin{lstlisting}[language=bash]
billr:5Ae40BGR/tePk:284:93:Bill Rosenblatt:/home/billr:/bin/ksh
\end{lstlisting}
Para obtener un listado ordenado de todos los usuarios del sistema, escriba:
\begin{lstlisting}[language=bash]
cut -d: -f1 < /etc/passwd | sort
\end{lstlisting}
\newpage
El comando cut extrae el primer campo \emph{(-f1)}, donde los campos están separados por dos puntos \emph{(-d:)}, de la entrada. La tubería completa imprime una lista que se parece a esto:
\begin{lstlisting}[language=bash]
al
billr
bob
chris
dave
ed
frank
...
\end{lstlisting}
Si desea enviar la lista directamente a la impresora (en lugar de a la pantalla), puede ampliar el proceso de la siguiente manera:
\begin{lstlisting}[language=bash]
cut -d: -f1 < /etc/passwd | sort | lp
\end{lstlisting}
Ahora deberías ver cómo la redirección de E/S y los pipelines apoyan la filosofía de bloques de construcción de Unix. La notación es extremadamente concisa y poderosa. Igualmente importante, el concepto de tubería elimina la necesidad de archivos temporales desordenados para almacenar la salida de comandos antes de que se alimente a otros comandos.
Por ejemplo, para hacer lo mismo que en la línea de comandos anterior en otros sistemas operativos (suponiendo que hubiera disponibles utilidades equivalentes), se necesitarían tres comandos. En el sistema OpenVMS de Compaq, tendrían este aspecto:
\begin{lstlisting}[language=bash]
$ cut [etc]passwd /d=":" /f=1 /out=temp1
$ sort temp1 /out=temp2
$ print temp2
\end{lstlisting}
Después de practicar lo suficiente, se encontrará tecleando rutinariamente potentes conductos de comandos que hacen en una línea lo que en otros sistemas operativos requeriría varios comandos (y archivos temporales) para lograrlo.
\section{Trabajos en Segundo Plano}
Las tuberías son en realidad un caso especial de una característica más general: hacer más de una cosa a la vez. cualquier otro sistema operativo comercial no tiene esta capacidad, debido a los rígidos límites que tienden a imponer a los usuarios. Unix, por otro lado, fue desarrollado en un laboratorio de investigación y pensado para uso interno, por lo que hace relativamente poco para imponer límites a los recursos disponibles para los usuarios en un ordenador - como de costumbre, inclinándose hacia la simplicidad despejada en lugar de la sobrecomplejidad.
<<Hacer más de una cosa a la vez>> significa ejecutar más de un programa al mismo tiempo. Esto se hace cuando se invoca un pipeline; también se puede hacer iniciando sesión en un sistema Unix tantas veces simultáneamente como se desee. (Si lo intentas en un sistema IBM VM/CMS, por ejemplo, obtendrás un odioso mensaje de <<ya ha iniciado sesión>>).
El shell también le permite ejecutar más de un comando a la vez durante una única sesión de inicio de sesión. Normalmente, cuando escribes un comando y pulsas ENTER, el shell deja que el comando tenga el control de tu terminal hasta que termine; no puedes ejecutar más comandos hasta que termine el primero. Pero si quieres ejecutar un comando que no requiere la entrada del usuario y quieres hacer otras cosas mientras se ejecuta el comando, pon un ampersand (\&) después del comando.
Esto se denomina ejecutar el comando en segundo plano, y un comando que se ejecuta de esta forma se denomina \emph{trabajo en segundo plano}; por el contrario, un trabajo que se ejecuta de la forma normal se denomina \emph{trabajo en primer plano}. Cuando inicias un trabajo en segundo plano, recuperas inmediatamente el prompt del shell, lo que te permite introducir otros comandos.
El uso más obvio de los trabajos en segundo plano son los programas que pueden tardar mucho tiempo en ejecutarse, como \emph{sort} o \emph{gunzip} en archivos grandes. Por ejemplo, supongamos que acabas de cargar en tu directorio un enorme archivo comprimido desde una cinta magnética. Hoy en día, la utilidad \emph{gzip} es la utilidad de compresión de archivos de-facto. \emph{gzip} a menudo logra una compresión del 50\% al 90\% de sus archivos de entrada. Los archivos comprimidos tienen nombres del tipo \emph{nombrearchivo.gz}, donde \emph{nombrearchivo} es el nombre del archivo original sin comprimir. Digamos que el archivo es \emph{gcc-3.0.1.tar.gz}, que es un archivo comprimido que contiene más de 36 MB de código fuente.
Escriba \texttt{gunzip gcc-3.0.1.tar.gz \&}, y el sistema inicia un trabajo en segundo plano que descomprime los datos <<en el lugar>> y termina con el archivo \emph{gcc-3.0.1.tar}. Justo después de escribir el comando, verá una línea como esta:
\begin{lstlisting}[language=bash]
[1] 4692
\end{lstlisting}
seguido de su prompt de shell, lo que significa que puede introducir otros comandos. Estos números te dan formas de referirte a tu trabajo en segundo plano; el \hyperref[sec:Chapter8]{Capítulo 8} los explica en detalle.
Puede verificar los trabajos en segundo plano con el comando \emph{jobs}. Para cada trabajo en segundo plano, jobs imprime una línea similar a la anterior pero con una indicación del estado del trabajo:
\begin{lstlisting}[language=bash]
[1] + Running gunzip gcc-3.0.1.tar.gz
\end{lstlisting}
Cuando el trabajo termine, verás un mensaje como este justo antes de tu prompt de shell:
\begin{lstlisting}[language=bash]
[1] + Done gunzip gcc-3.0.1.tar.gz
\end{lstlisting}
El mensaje cambia si su trabajo en segundo plano finaliza con un error; de nuevo, consulte el \hyperref[sec:Chapter8]{Capítulo 8} para más detalles.
\subsection{E/S en Segundo Plano}
Los trabajos que pongas en segundo plano no deberían hacer E/S a tu terminal. Piénsalo un momento y entenderás por qué.
Por definición, un trabajo en segundo plano no tiene control sobre tu terminal. Entre otras cosas, esto significa que sólo el proceso en primer plano (o, en su defecto, el propio shell) está <<escuchando>> la entrada de tu teclado. Si un trabajo en segundo plano necesita entrada de teclado, a menudo se quedará ahí sin hacer nada hasta que usted haga algo al respecto (como se describe en el \hyperref[sec:Chapter8]{Capítulo 8}).
Si un trabajo en segundo plano produce salida por pantalla, la salida simplemente aparecerá en su pantalla. Si está ejecutando un trabajo en primer plano que también produce salida, la salida de los dos trabajos se intercalará de forma aleatoria (y a menudo molesta).
Si quieres ejecutar un trabajo en segundo plano que espera una entrada estándar o produce una salida estándar, la solución obvia es redirigirlo para que venga o vaya a un fichero. La única excepción es que algunos programas producen pequeños mensajes de una línea (advertencias, mensajes de <<hecho>>, etc.); puede que no te importe si éstos se intercalan con cualquier otra salida que estés viendo en un momento dado.
Por ejemplo, la utilidad \emph{diff} examina dos archivos, cuyos nombres se dan como argumentos, e imprime un resumen de sus diferencias en la salida estándar. Si los archivos son exactamente iguales, \emph{diff} no dice nada. Normalmente, usted invoca \emph{diff} esperando ver unas pocas líneas que son diferentes.
\emph{diff}, al igual que \emph{sort} y \emph{gzip}, puede tardar mucho tiempo en ejecutarse si los ficheros de entrada son muy grandes. Suponga que tiene dos archivos grandes llamados \emph{warandpeace.html} y \emph{warandpeace.html.old}. El comando \texttt{diff warandpeace.html.old warandpeace.html} revela que el autor decidió cambiar el nombre <<Ivan>> por <<Aleksandr>> en todo el archivo - es decir, cientos de diferencias, lo que resulta en grandes cantidades de salida.
Si escribe \texttt{diff warandpeace.html.old warandpeace.html \&}, el sistema le arrojará montones y montones de resultados, que serán muy difíciles de detener - incluso con las técnicas explicadas en el \hyperref[sec:Chapter7]{Capítulo 7}. Sin embargo, si escribe:
\begin{lstlisting}[language=bash]
diff warandpeace.html.antiguo warandpeace.html > wpdiff &
\end{lstlisting}
las diferencias se guardarán en el archivo \emph{wpdiff} ipara que pueda examinarlas más tarde.
\subsection{Trabajos en Segundo Plano y Prioridades}
Las tareas en segundo plano pueden ahorrarte mucho tiempo (o pueden ayudarte a hacer dieta eliminando las excusas para ir corriendo a la máquina de caramelos). Pero recuerda que no hay almuerzo gratis; los trabajos en segundo plano consumen recursos que dejan de estar disponibles para ti o para otros usuarios del sistema. El hecho de que ejecutes varios trabajos a la vez no significa que vayan a funcionar más rápido que si se ejecutaran secuencialmente; de hecho, suele ser peor.
A cada tarea del sistema se le asigna una \emph{prioridad}, un número que indica al sistema operativo cuánta prioridad debe darle a la hora de repartir los recursos (cuanto mayor sea el número, menor será la prioridad). Los comandos en primer plano que se introducen desde el shell suelen tener la misma prioridad estándar. Pero los trabajos en segundo plano, por defecto, tienen una prioridad más baja\footnote{Esta característica se tomó prestada del intérprete de comandos C; no está presente en la mayoría de los intérpretes de comandos Bourne.}. En el \hyperref[sec:Chapter3]{Capítulo 3} descubrirás cómo puedes anular esta asignación de prioridad para que los trabajos en segundo plano se ejecuten con la misma prioridad que los trabajos en primer plano.
Si estás en un gran sistema multiusuario, ejecutar muchos trabajos en segundo plano puede consumir más recursos de los que te corresponden, y deberías considerar si hacer que tu trabajo se ejecute lo más rápido posible es realmente más importante que ser un buen ciudadano.
Por otro lado, si tienes una estación de trabajo dedicada con un procesador rápido y mucha memoria y disco, probablemente tengas ciclos de sobra y no debas preocuparte tanto por ello. De todos modos, el patrón de uso típico de estos sistemas obvia en gran medida la necesidad de procesos en segundo plano: basta con iniciar un trabajo y luego abrir otra ventana y seguir trabajando.
\subsubsection{nice}
Hablando de buena ciudadanía, también hay un comando de shell que te permite bajar la prioridad de cualquier trabajo: el acertadamente llamado \emph{nice}. Si escribes lo siguiente, el comando se ejecutará con una prioridad más baja:
\begin{lstlisting}[language=bash]
nice command
\end{lstlisting}
Puedes controlar cuánto más bajo es dando a \emph{nice} un argumento numérico; consulta la página \emph{man} para más detalles\footnote{Si eres administrador del sistema y has iniciado sesión como \texttt{root}, también puedes utilizar \emph{nice} para aumentar la prioridad de una tarea.}.
\section{Caracteres Especiales y Citas}
Los caracteres \texttt{<, >, | y \&} son cuatro ejemplos de \emph{caracteres especiales} que tienen significados particulares para el shell. Los comodines que vimos anteriormente en este capítulo \texttt{(*, ?, y [...])} también son caracteres especiales.
En la Tabla \ref{tab6} se indican los significados de todos los caracteres especiales sólo dentro de las líneas de comandos del shell. Otros caracteres tienen significados especiales en situaciones específicas, como las expresiones regulares y los operadores de manejo de cadenas que veremos en el \hyperref[sec:Chapter3]{Capítulo 3} y el \hyperref[sec:Chapter4]{Capítulo 4}.
\begin{longtable}[h]{p{2cm}|p{8cm}|p{3cm}}
\caption{Caracteres especiales}
\label{tab6}\\
\hline
\textbf{Caracter} & \textbf{Significado} & \textbf{Ver capítulo} \\\hline
\endfirsthead
\hline
\textbf{Caracter} & \textbf{Significado} & \textbf{Ver capítulo} \\\hline
\endhead
\texttt{\~} & Directorio \emph{Home} & 1 \\
\texttt{`} & Sustitución de comandos (arcaico) & 4 \\
\texttt{\#} & Comentario & 4 \\
\texttt{\$} & Expresión variable & 3 \\
\texttt{\&} & Trabajo en segundo plano & 1 \\
\texttt{*} & Cadena comodín & 1 \\
\texttt{(} & Inicio subshell & 8 \\
\texttt{)} & Fin subshell & 8 \\
\textbackslash{} & Cita siguiente carácter & 1 \\
| & Tubería (Pipe) & 1 \\
\texttt{[} & Inicio conjunto caracteres comodín & 1 \\
\texttt{]} & Fin del juego de caracteres comodín & 1 \\
\textbraceleft & Inicio bloque de código & 7 \\
\textbraceright & Fin del bloque de código & 7 \\
; & Separador de comandos Shell & 3 \\
' & Comilla simple & 1 \\
" & Doble comilla & 1 \\
< & Redirección de entrada & 1 \\
> & Redirección de salida & 1 \\
/ & Separador de directorio & 1 \\
? & Comodín de un solo carácter & 1 \\
\% & Identificador de nombre/número de trabajo & 8 \\
\end{longtable}
\subsection{Citando}
A veces querrá utilizar caracteres especiales literalmente, es decir, sin sus significados especiales. Esto se denomina \emph{entrecomillado}. Si rodea una cadena de caracteres con comillas simples, despojará a todos los caracteres entre comillas de cualquier significado especial que pudieran tener.
La situación más obvia en la que podrías necesitar entrecomillar una cadena es con la orden \emph{print}, que simplemente toma sus argumentos y los imprime en la salida estándar. ¿Para qué sirve esto? Como verá en capítulos posteriores, el intérprete de comandos realiza bastantes procesos en las líneas de comandos, la mayoría de los cuales implican algunos de los caracteres especiales enumerados en la Tabla \ref{tab6}. \emph{print} es una forma de hacer que el resultado de ese proceso esté disponible en la salida estándar.
\newpage
Pero, ¿qué pasaría si quisiéramos imprimir la cadena, \texttt{2 * 3 > 5 is a valid inequality} Supongamos que escribimos esto:
\begin{lstlisting}[language=bash]
print 2 * 3 > 5 is a valid inequality
\end{lstlisting}
Volverías a tener el prompt del shell, ¡como si no hubiera pasado nada! Pero entonces habría un nuevo archivo, con el nombre 5, conteniendo <<2>>, los nombres de todos los archivos en tu directorio actual, y entonces la cadena \texttt{3 is a valid inequality}. Asegúrate de entender por qué\footnote{Esto también debería enseñarte algo sobre la flexibilidad de colocar redireccionadores de E/S en cualquier parte de la línea de comandos, incluso en lugares donde no parecen tener sentido.}.
Sin embargo, si escribes
\begin{lstlisting}[language=bash]
print '2 * 3 > 5 is a valid inequality.'
\end{lstlisting}
el resultado es la cadena, tomada literalmente. No es necesario que cites toda la línea, sólo la parte que contiene caracteres especiales (o caracteres que crees que pueden ser especiales, si quieres estar seguro):
\begin{lstlisting}[language=bash]
print '2 * 3 > 5' is a valid inequality.
\end{lstlisting}
Esto tiene exactamente el mismo resultado.
Observe que la Tabla \ref{tab6} enumera las comillas dobles ('') como comillas simples. Una cadena entre comillas dobles está sujeta a algunos de los pasos que sigue el shell para procesar líneas de órdenes, pero no a todos. (En otras palabras, trata sólo algunos caracteres especiales como especiales.) Verá en capítulos posteriores por qué las comillas dobles son a veces preferibles; el \hyperref[sec:Chapter7]{Capítulo 7} contiene la explicación más completa de las reglas del shell para las comillas y otros aspectos del procesamiento de la línea de órdenes. Por ahora, sin embargo, deberías ceñirte a las comillas simples.
\subsection{Barra invertida}
Otra forma de cambiar el significado de un carácter es precederlo de una barra invertida (\textbackslash{}). Esto se denomina barra invertida-escapar el carácter. En la mayoría de los casos, cuando se escapa una barra invertida, se entrecomilla el carácter. Por ejemplo:
\begin{lstlisting}[language=bash]
print 2 \* 3 \> 5 is a valid inequality.
\end{lstlisting}
produce los mismos resultados que si rodeara la cadena con comillas simples. Para utilizar una barra invertida literal, basta con rodearla de comillas ('\textbackslash{}') o, mejor aún, con escaparla (\textbackslash{} \textbackslash{}).
He aquí un ejemplo más práctico de cómo entrecomillar caracteres especiales. Algunos comandos Unix toman argumentos que a menudo incluyen caracteres comodín, que necesitan ser escapados para que el shell no los procese primero. El comando más común es \emph{find}, que busca archivos en árboles de directorios completos.
Para utilizar \emph{find}, debe proporcionar la raíz del árbol en el que desea buscar y argumentos que describan las características de los archivos que desea encontrar. Por ejemplo, el comando \texttt{find . -name string -print} busca en el árbol de directorios cuya raíz es el directorio actual los archivos cuyos nombres coinciden con la cadena e imprime sus nombres. (Otros argumentos permiten buscar por tamaño del archivo, propietario, permisos, fecha del último acceso, etc.).
Puede utilizar comodines en la cadena, pero debe entrecomillarlos para que el propio comando \emph{find} pueda compararlos con los nombres de los archivos de cada directorio en el que busca. El comando \texttt{find . -name '*.c'} buscará todos los archivos cuyo nombre termine en \texttt{.c} en cualquier lugar del directorio actual, subdirectorios, sub-subdirectorios, etc.
\subsection{Comillas dobles}
También puede utilizar una barra invertida para incluir comillas dobles dentro de una cadena. Por ejemplo
\begin{lstlisting}[language=bash]
print \"2 \* 3 \> 5\" is a valid inequality.
\end{lstlisting}
produce el siguiente resultado:
\begin{lstlisting}[language=bash]
"2 * 3 > 5" is a valid inequality.
\end{lstlisting}
Dentro de una cadena entre comillas dobles, sólo es necesario escapar las comillas dobles:
\begin{lstlisting}[language=bash]
$ print "\"2 * 3 > 5\" is a valid inequality."
"2 * 3 > 5" is a valid inequality.
\end{lstlisting}
Sin embargo, esto no funcionará con comillas simples dentro de expresiones entrecomilladas. Por ejemplo, \texttt{print 'Bob\textbackslash{}'s hair is brown'} no dará como resultado \texttt{Bob's hair is brown}. Puede evitar esta limitación de varias maneras. Primero, intente eliminar las comillas:
\begin{lstlisting}[language=bash]
print Bob\'s hair is brown
\end{lstlisting}
Si no hay otros caracteres especiales (como en este caso), esto funciona. De lo contrario, puede utilizar el siguiente comando:
\begin{lstlisting}[language=bash]
print 'Bob'\''s hair is brown'
\end{lstlisting}
Es decir, '\textbackslash{}'' (comilla simple, barra invertida, comilla simple, comilla simple) actúa como una comilla simple dentro de una cadena entrecomillada. ¿Por qué? La primera ' de '\textbackslash{}'' termina la cadena entrecomillada que empezamos con 'Bob, la \textbackslash{}' inserta una comilla simple literal, y la siguiente ' inicia otra cadena entrecomillada que termina con la palabra <<brown>>. Si entiendes esto, no tendrás problemas para resolver los otros problemas desconcertantes que surgen de la sintaxis a menudo críptica del shell.
Existe un mecanismo algo más legible, específico de \emph{ksh93}, para los casos en los que necesite entrecomillar comillas simples. Este es el mecanismo de entrecomillado extendido del shell: \texttt{\$'...'}. Esto se conoce en la documentación de \emph{ksh} como entrecomillado ANSI C, ya que las reglas se parecen mucho a las del estándar ANSI/ISO C. Los detalles completos se proporcionan en el \hyperref[sec:Chapter7]{Capítulo 7}. A continuación se muestra cómo utilizar el entrecomillado ANSI C para el ejemplo anterior:
\begin{lstlisting}[language=bash]
$ print $'Bob\'s hair is brown'
Bob's hair is brown
\end{lstlisting}
\subsection{Continuación de líneas}
Una cuestión relacionada es cómo continuar el texto de un comando más allá de una sola línea en la ventana de tu terminal o estación de trabajo. La respuesta es conceptualmente sencilla: basta con citar la tecla ENTER. Después de todo, ENTER no es más que otro carácter.
Puede hacerlo de dos maneras: terminando una línea con una barra invertida o no cerrando una comilla (es decir, incluyendo ENTER en una cadena entrecomillada). Si utiliza la barra invertida, no debe haber nada entre ella y el final de la línea - ni siquiera espacios o TABs.
Tanto si utiliza una barra invertida como una comilla simple, le está diciendo al shell que ignore el significado especial del carácter ENTER. Después de pulsar ENTER, el shell entiende que no ha terminado su línea de comandos (es decir, ya que no ha escrito un ENTER <<real>>), por lo que responde con un prompt secundario, que es > por defecto, y espera a que termine la línea. Puedes continuar una línea tantas veces como desees.
Por ejemplo, si quiere que el shell imprima la primera frase de \emph{The Return of the Native} de Thomas Hardy, puede escribir esto:
\begin{lstlisting}[language=bash]
$ print A Saturday afternoon in November was approaching the \
> time of twilight, and the vast tract of unenclosed wild known \
> as Egdon Heath embrowned itself moment by moment.
\end{lstlisting}
O puedes hacerlo así:
\begin{lstlisting}[language=bash]
$ print 'A Saturday afternoon in November was approaching the
> time of twilight, and the vast tract of unenclosed wild known
> as Egdon Heath embrowned itself moment by moment.'
\end{lstlisting}
Hay una diferencia entre los dos métodos. El primero imprime la frase como una línea larga. El segundo conserva las nuevas líneas incrustadas. Pruebe ambos y verá la diferencia.
\subsection{Teclas de control}
Las teclas de control (las que escribes manteniendo pulsada la tecla CONTROL (o CTRL) y pulsando otra tecla) son otro tipo de caracteres especiales. Normalmente no imprimen nada en la pantalla, pero el sistema operativo interpreta algunos de ellos como comandos especiales. Ya conoces uno de ellos: ENTER es en realidad lo mismo que CTRL-M (pruébalo y verás). Probablemente también hayas utilizado la tecla RETROCESO o SUPR para borrar errores tipográficos en tu línea de comandos.
En realidad, muchas teclas de control tienen funciones que realmente no te conciernen - sin embargo, deberías conocerlas para futuras referencias y en caso de que las escribas por accidente.
Quizás lo más difícil de las teclas de control es que pueden diferir de un sistema a otro. La disposición habitual se muestra en la Tabla \ref{Tab:1-7}, que lista las teclas de control que soportan las principales versiones modernas de Unix. Tenga en cuenta que CTRL-\textbackslash{} y CTRL-| (control-barra invertida y control-tubo) son el mismo carácter anotado de dos formas diferentes; lo mismo ocurre con DEL y CTRL-?
Puede utilizar el comando \emph{stty(1)} para averiguar cuál es su configuración y cambiarla si lo desea; consulte el \hyperref[sec:Chapter8]{Capítulo 8} para más detalles. En sistemas Unix modernos (incluyendo GNU/Linux), use \texttt{stty -a} para ver su configuración de teclas de control:
\begin{lstlisting}[language=bash]
$ stty -a
speed 38400 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = ; <undef>;
eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
...
\end{lstlisting}
La notación \^{}X significa CTRL-X.
\newpage
\begin{table}[h]
\center
\caption{Teclas de control}
\label{Tab:1-7}
\begin{tabular}{m{3cm}|m{2cm}|m{9cm}} \hline
\textbf{Tecla de control} & \textbf{Nombre de stty} & \textbf{Descripción de la función} \\ \hline
CTRL-C & intr & Detiene el comando actual \\
CTRL-D & eof & Detiene el comando actual \\
CTRL-\textbackslash{} ó CTRL-| & quit & Detiene el comando actual, si CTRL-C no funciona \\
CTRL-S & stop & Detiene el comando actual, si CTRL-C no funciona \\
CTRL-Q & start & Reinicia la salida a pantalla \\
BACKSPACE ó CTRL-H & erase & Borra el último carácter. Esta es la configuración más común \\
DEL ó CTRL-? & erase & Borra último carácter. Este es un ajuste alternativo común. para el carácter de borrado \\
CTRL-U & kill & Borra toda la línea de comandos \\
CTRL-Z & susp & Suspende el comando actual (ver \hyperref[sec:Chapter8]{Capítulo 8}) \\
CTRL-R & rprnt & Reimprime los caracteres introducidos hasta el momento \\
\end{tabular}
\end{table}
La tecla de control que probablemente utilices más a menudo es CTRL-C, a veces llamada \emph{tecla de interrupción}. Esto detiene - o intenta detener - el comando que se está ejecutando en ese momento. Querrás usarla cuando introduzcas un comando y veas que está tardando demasiado, cuando le des los argumentos equivocados por error, cuando cambies de opinión sobre si quieres ejecutarlo, etc.
A veces CTRL-C no funciona; en ese caso, si realmente quieres detener un trabajo, prueba con CTRL-\textbackslash. Pero no escriba simplemente CTRL-\textbackslash; ¡pruebe siempre CTRL-C primero! El \hyperref[sec:Chapter8]{Capítulo 8} explica por qué en detalle. Por ahora, basta con decir que CTRL-C da al trabajo en ejecución más oportunidad de limpiar antes de salir, para que los ficheros y otros recursos no queden en estados extraños.
Ya hemos visto un ejemplo de CTRL-D. Cuando estás ejecutando un comando que acepta la entrada estándar de tu teclado, CTRL-D (como el primer carácter en la línea) le dice al proceso que tu entrada ha terminado - como si el proceso estuviera leyendo un archivo y llegara al final del archivo. mail es una utilidad en la que esto sucede a menudo. Cuando estás escribiendo un mensaje, terminas tecleando CTRL-D. Esto le dice a mail que su mensaje está completo y listo para ser enviado. La mayoría de las utilidades que aceptan entrada estándar entienden CTRL-D como el carácter de fin de entrada, aunque muchos de estos programas aceptan comandos como \emph{q, quit, exit,} etc. El propio intérprete de comandos entiende CTRL-D como el carácter de fin de entrada: como vimos anteriormente en este capítulo, normalmente puede finalizar una sesión de inicio de sesión escribiendo CTRL-D en el indicador del intérprete de comandos. Sólo le está diciendo al shell que su entrada de comandos ha terminado.
CTRL-S y CTRL-Q se llaman caracteres de control de flujo. Representan una forma anticuada de detener y reiniciar el flujo de salida de un dispositivo a otro (por ejemplo, del ordenador a tu terminal) que era útil cuando la velocidad de dicha salida era baja. Están bastante obsoletos en estos tiempos de redes locales de alta velocidad y líneas de acceso telefónico. De hecho, en estas últimas condiciones, CTRL-S y CTRL-Q son básicamente una molestia. Lo único que realmente necesitas saber sobre ellas es que si la salida de tu pantalla se <<atasca>>, puede que hayas pulsado CTRL-S por accidente. Escribe CTRL-Q para reiniciar la salida; cualquier tecla que hayas pulsado entretanto tendrá efecto.
El último grupo de caracteres de control te ofrece formas rudimentarias de editar tu línea de comandos. RETROCESO o CTRL-H actúan como una tecla de retroceso (de hecho, algunos sistemas usan las teclas DEL o CTRL-? como <<borrar>> en lugar de RETROCESO); CTRL-U borra toda la línea y te permite empezar de nuevo. De nuevo, la mayoría de estas funciones son obsoletas\footnote{¿Por qué se siguen utilizando tantas teclas de control obsoletas? No tienen nada que ver con el shell per se; en su lugar, son reconocidas por el controlador tty, una vieja y vetusta parte de las profundidades inferiores del sistema operativo que controla la entrada y salida a/desde tu terminal. De hecho, es el controlador tty el que entiende CTRL-D y señala el fin de la entrada a los programas que leen desde el terminal, no los propios programas.} En lugar de usarlas, vaya al siguiente capítulo y lea sobre los modos de edición del shell Korn, que están entre sus características más interesantes.

343
Secciones/Capitulo10.tex Normal file
View File

@ -0,0 +1,343 @@
Los administradores de sistemas utilizan el intérprete de comandos como parte de su trabajo de configurar un entorno en todo el sistema para todos los usuarios. En este capítulo, discutiremos las características del shell Korn que se relacionan con esta tarea desde dos perspectivas: la personalización que está disponible para todos los usuarios y la seguridad del sistema. Asumimos que ya conoce los fundamentos de la administración de sistemas Unix.\footnote{Una buena fuente de información sobre administración de sistemas es \emph{Essential System Administration} de Æleen Frisch. Está publicado por O'Reilly \& Associates.}
\section{Instalación del shell Korn como shell estándar}
Como preludio a la personalización de todo el sistema, queremos destacar algo sobre el shell Korn que no se aplica a la mayoría de los otros shells: puede instalarlo como si fuera el shell Bourne estándar, es decir, como \texttt{/bin/sh}. Simplemente guarde el shell Bourne real como otro nombre de archivo, como \texttt{/bin/bsh}, en caso de que alguien realmente lo necesite para algo (lo cual es dudoso), luego renombre (o enlace) su shell Korn como \texttt{/bin/sh}.
Muchas instalaciones han hecho esto sin ningún efecto negativo. Esto no sólo hace que el shell Korn sea el shell de inicio de sesión estándar de su sistema, sino que también hace que la mayoría de los scripts del shell Bourne existentes se ejecuten más rápido, y tiene ventajas de seguridad que veremos más adelante en este capítulo.
Como veremos en el \hyperref[sec:ApendiceA]{Apéndice A}, el shell Korn es retrocompatible con el shell Bourne excepto que no soporta \textasciicircum{} como sinónimo del carácter de tubería |. A menos que tenga un sistema Unix antiguo, o tenga algunos scripts de shell muy, muy antiguos, no necesita preocuparse por esto.
Pero si quiere estar absolutamente seguro, simplemente busque entre todos los scripts de shell en todos los directorios de su \texttt{PATH}. Una manera fácil de hacer esto es usar el comando \emph{file(1)}, que vimos en el \hyperref[sec:Chapter5]{Capítulo 5} y en el \hyperref[sec:Chapter9]{Capítulo 9}. \emph{file} imprime <<script de shell ejecutable>> cuando se le da el nombre de uno. (El mensaje exacto varía de un sistema a otro; asegúrese de que el suyo imprime este mensaje cuando se le da el nombre de un script de shell. Si no es así, simplemente sustituya el mensaje que imprime su comando \emph{file} por <<shell script>> en el siguiente ejemplo). He aquí un script que busca \textasciicircum{} en scripts de shell en cada directorio de su \texttt{PATH}:\footnote{Este script fallará si su PATH tiene directorios cuyos nombres contienen espacios. Considere solucionar este problema como un ejercicio avanzado para el lector.}
\begin{lstlisting}[language=bash]
dirs=$(print -- $PATH |
sed -e 's/^:/.:/' -e 's/::/:.:/' -e s'/:$/:./' -e 's/:/ /g')
for d in $dirs
do
print "checking $d:"
cd "$d"
scripts=$(file * | grep 'shell script' | cut -d: -f1)
grep -l '\^' $scripts /dev/null
done
\end{lstlisting}
La primera sentencia de este script separa \texttt{\$PATH} en directorios separados, incluyendo el manejo de varios casos de separadores vacíos que significan el directorio actual. El programa \emph{sed(1)} es un editor de flujo que realiza operaciones de edición en su entrada, e imprime los contenidos cambiados en su salida. El resultado se asigna a \emph{dirs}, que se utiliza como lista de elementos en el bucle \emph{for}. Para cada directorio, \emph{cd}s allí y encuentra todos los scripts de shell mediante la canalización del comando \emph{file} en \emph{grep} y luego, para extraer sólo el nombre de archivo, en \emph{cut}. A continuación, busca el carácter \textasciicircum{} en cada script. La opción \texttt{-l} de \emph{grep|} simplemente lista todos los nombres de archivo que coinciden con el patrón, sin imprimir las líneas coincidentes. El comando \emph{grep} tiene \emph{/dev/null} al final de la lista de archivos en caso de que \texttt{\$scripts} esté vacío. Si eres aventurero, puedes hacer todo el trabajo en una sola línea:
\begin{lstlisting}[language=bash]
grep -l '\^' $(file * | grep 'shell script' | cut -d: -f1) /dev/null
\end{lstlisting}
Si ejecutas este script, probablemente encontrarás varias apariciones de \textasciicircum{} pero éstas deberían ser usadas dentro de expresiones regulares en comandos \emph{grep, sed,} o \emph{awk}, no como caracteres de tubería. Asumiendo que este es el caso, es seguro que instale el shell Korn como \emph{/bin/sh}.
\section{Personalización del entorno}
Al igual que el shell de Bourne, el shell de Korn utiliza el archivo \texttt{/etc/profile} para la personalización a nivel del sistema. Cuando un usuario inicia sesión, el shell lee y ejecuta \texttt{/etc/profile} antes de ejecutar el \emph{.profile} del usuario.
No cubrimos todos los comandos posibles que podría querer colocar en \texttt{/etc/profile}. Pero el shell de Korn tiene algunas características únicas que son particularmente relevantes para la personalización a nivel del sistema; las discutimos aquí.
Comenzaremos con dos comandos integrados que puede utilizar en \texttt{/etc/profile} para adaptar los entornos de sus usuarios y limitar su uso de los recursos del sistema. Los usuarios también pueden usar estos comandos en su \emph{.profile}, o en cualquier otro momento, para anular la configuración predeterminada.
\subsection{umask}
\emph{umask}, al igual que el mismo comando en la mayoría de las otras shells, le permite especificar los permisos predeterminados que tienen los archivos cuando los usuarios los crean. Con \emph{ksh}, toma los mismos tipos de argumentos que el comando \emph{chmod}, es decir, valores de permisos absolutos (números octales) o simbólicos.
\emph{umask} contiene los permisos que se desactivan de manera predeterminada cada vez que un proceso crea un archivo, independientemente de los permisos que especifique el proceso. \footnote{Si conoce C, C++ o Java, y se siente cómodo con las operaciones bit a bit, la operación de \emph{umask} funciona así: \texttt{permiso\_real = permiso\_solicitado \& (\~{} umask)}.}
Otra forma de pensar en esto es como una sustracción libre de préstamos bit a bit: \emph{permisos reales = permisos solicitados - umask}.
Usaremos la notación octal para mostrar cómo funciona esto. Como debería saber, los dígitos en un número de permiso representan (de izquierda a derecha) los permisos del propietario, el grupo del propietario y todos los demás usuarios, respectivamente. Cada dígito, a su vez, consta de tres bits, que especifican permisos de lectura, escritura y ejecución de izquierda a derecha. (Si un archivo es un directorio, el permiso <<ejecutar>> se convierte en permiso <<buscar>>, es decir, permiso para \emph{cd} a él y recorrerlo como parte de una ruta de acceso).
Por ejemplo, el número octal 640 es igual al número binario 110 100 000. Si un archivo tiene este permiso, entonces su propietario puede leerlo y escribirlo; los usuarios en el grupo del propietario solo pueden leerlo; los demás no tienen permisos sobre él. Un archivo con permiso 755 (111 101 101 en binario) le da a su propietario el derecho de leer, escribir y ejecutarlo y a todos los demás el derecho de leer y ejecutar (pero no escribir).
022 es un valor de \emph{umask} común. Esto implica que cuando se crea un archivo, el permiso máximo que podría tener es 755, que es el permiso habitual de un ejecutable que un compilador podría crear. Un editor de texto, por otro lado, podría crear un archivo con permiso 666 (lectura y escritura para todos), pero la \emph{umask} obliga a que sea 644 en su lugar.
La opción \texttt{-S} para \emph{umask} hace que funcione con valores simbólicos en lugar de con números octales. Cuando se usa sin un argumento, \texttt{umask -S} imprime \emph{umask} en forma simbólica. Con un argumento, se cambia la máscara. En ambos casos, una máscara simbólica representa los permisos que se deben \emph{mantener} para un archivo. (Esto termina siendo el complemento bit a bit del umask octal tradicional, que representa permisos a eliminar). Si está confundido, algunos ejemplos deberían aclarar las cosas:
\begin{lstlisting}[language=bash]
$ umask # Imprime la umask actual, en octal
0022
$ umask -S # Imprime la umask actual, en forma simbólica
u=rwx,g=rx,o=rx
$ umask -S u=rwx,g=r,o= # Cambia la umask usando la forma simbólica
$ umask -S # Imprímelo nuevamente de manera simbólica
u=rwx,g=r,o=
$ umask # Imprímelo en octal
0037
\end{lstlisting}
\subsubsection{ulimit}
En los primeros sistemas Unix, no se imponían límites a los recursos que un proceso podía utilizar. Si un programa quería ejecutarse para siempre, podía hacerlo. Si un programa quería crear archivos grandes y llenar un disco, podía hacerlo. Y así sucesivamente.
A medida que Unix se desarrolló y maduró, se hizo posible controlar explícitamente o \emph{limitar} una variedad de recursos del sistema, como el tiempo de CPU y el espacio en disco. El comando \emph{ulimit} es la interfaz del shell de Korn para ver y cambiar los límites de los recursos del sistema. La Tabla \ref{Tab:10-1} enumera las opciones que acepta y los recursos correspondientes. No todas las opciones están disponibles en todos los sistemas Unix. Muchas no estarán disponibles en sistemas no Unix.
\begin{table}[h]
\center
\caption{Opciones de recursos \texttt{ulimit}}
\label{Tab:10-1}
\begin{tabular}{|m{2cm}|m{5.5cm}|m{2cm}|m{5.5cm}|} \hline
\textbf{Opción} & \textbf{Recursos limitados} & \textbf{Opción} & \textbf{Recursos limitados} \\\hline
-a & Todos (sólo para imprimir valores) & -n & Descriptores de archivos \\\hline
-c & Tamaño del archivo principal ($\frac{1}{2}$ bloques de kb) & -p & Tamaño del búfer de la tubería ($\frac{1}{2}$ bloques de kb) \tablefootnote{La mayoría de los sistemas Unix no tienen esta característica.} \\\hline
-d & Segmento de datos de proceso (kb) & -s & Segmento de pila de proceso (kb) \\\hline
-f & Tamaño del fichero ( $\frac{1}{2}$ kb bloques) & -t & Tamaño del fichero ( kb bloques) \\\hline
-m & Memoria física (kb) & -v & Memoria física (kb) \\\hline
\end{tabular}
\end{table}
Cada opción toma un argumento numérico que especifica el límite en las unidades que se muestran en la tabla. (Puede usar una expresión aritmética para el límite; \emph{ksh} evalúa automáticamente la expresión). También puede dar el argumento <<ilimitado>> (que en realidad puede significar algún límite físico), o puede omitir el argumento, en cuyo caso imprime el límite actual. \texttt{ulimit -a} imprime los límites (o <<ilimitado>>) para todos los tipos. Solo puede especificar un tipo de recurso a la vez. Si no especifica ninguna opción, se asume \texttt{-f}.
Algunas de estas opciones dependen de capacidades del sistema operativo que no existen en versiones antiguas de Unix. En particular, algunas versiones antiguas tienen un límite fijo de 20 descriptores de archivos por proceso (haciendo que \texttt{-n} sea irrelevante), y algunas no admiten memoria virtual (haciendo que \texttt{-v} sea irrelevante).
Las opciones \texttt{-d} y \texttt{-s} tienen que ver con la \emph{asignación de memoria dinámica}, es decir, la memoria que un proceso solicita al sistema operativo en tiempo de ejecución. No es necesario que los usuarios ocasionales limiten estos, aunque los desarrolladores de software pueden querer hacerlo para evitar que programas con errores intenten asignar cantidades interminables de memoria debido a bucles infinitos.
La opción \texttt{-v} es similar; pone un límite a todos los usos de memoria. No necesita esto a menos que su sistema tenga restricciones severas de memoria o desee limitar el tamaño del proceso para evitar la fragmentación.
Es posible que desee especificar límites en el tamaño de los archivos (\texttt{-f} y \texttt{-c}) si tiene restricciones de espacio en disco. A veces, los usuarios realmente quieren crear archivos enormes, pero más a menudo, un archivo enorme es el resultado de un programa con errores que entra en un bucle infinito. Los desarrolladores de software que usan depuradores como \emph{gdb} y \emph{dbx} no deben limitar el tamaño del archivo central, porque los volcados centrales a menudo son útiles para la depuración.
La opción \texttt{-t} es otra posible protección contra bucles infinitos. En sistemas de un solo usuario, un programa que está en un bucle infinito pero no está asignando memoria, escribiendo archivos ni utilizando la red no es particularmente peligroso; es mejor dejar esto ilimitado y simplemente permitir que el usuario detenga el programa ofensor. Sin embargo, en sistemas de servidores compartidos, tales programas degradan definitivamente el entorno general. El problema en ese caso es que es difícil saber qué límite establecer: hay usos importantes y legítimos para programas de larga duración.
Además de los tipos de recursos que puede limitar, \emph{ulimit} le permite especificar límites duros o blandos. Los límites duros pueden disminuirlos cualquier usuario, pero solo aumentarlos el superusuario (\texttt{root}); los usuarios pueden disminuir los límites blandos y aumentarlos, pero solo hasta el límite duro para ese recurso.
Si proporciona \texttt{-H} junto con una (o más) de las opciones anteriores, \emph{ulimit} establece límites duros; \texttt{-S} establece límites blandos. Sin ninguno de estos, \emph{ulimit} establece ambos. Por ejemplo, los siguientes comandos establecen el límite blando en descriptores de archivos en 64 y el límite duro en ilimitado:
\begin{lstlisting}[language=bash]
ulimit -Sn 64
ulimit -Hn unlimited
\end{lstlisting}
Cuando \emph{ulimit} imprime los límites actuales, imprime los límites blandos a menos que especifique \texttt{-H}.
\subsection{Tipos de personalización global}
El enfoque más adecuado para la personalización global disponible sería un archivo de entorno a nivel del sistema que sea independiente del archivo de entorno de cada usuario, al igual que \texttt{/etc/profile} es independiente del \emph{.profile} de cada usuario.
Desafortunadamente, el shell de Korn no tiene esta característica. Si asigna un nombre de archivo a la variable de entorno ENV, podría anularse en el \emph{.profile} de un usuario. Esto le permite proporcionar un archivo de entorno predeterminado para los usuarios que no tienen el suyo propio, pero no le permite tener un archivo de entorno a nivel del sistema que se ejecute además de los de los usuarios. Además, el archivo de entorno solo se ejecuta para shells interactivas, no para todas las shells.
Sin embargo, el shell le proporciona algunas formas de configurar personalizaciones que están disponibles para todos los usuarios en todo momento. Las variables de entorno son las más obvias; su archivo \texttt{/etc/profile} seguramente contendrá definiciones para varias de ellas, incluyendo \texttt{PATH} y \texttt{TERM}.
La variable \texttt{TMOUT} es útil cuando su sistema admite líneas de conexión telefónica. Ya hemos visto que afecta al comando \emph{read} y al bucle del menú \texttt{select}. Cuando se establece en un número \emph{N}, si un usuario no ingresa un comando dentro de \emph{N} segundos después de que el shell emitió por última vez un indicador, el shell imprime el mensaje de advertencia <<el shell se cerrará en 60 segundos debido a inactividad>>. Si, después de otros 60 segundos, el usuario no ingresa nada, el shell se cierra. Esta función es útil para evitar que las personas <<acaparen>> las líneas telefónicas. ¡Solo asegúrese de establecerlo en un valor razonable!
Es posible que desee incluir algunas personalizaciones más complejas que involucren variables de entorno, como la cadena de indicadores PS1 que contiene el directorio actual, el nombre de usuario o el nombre del host (como se ve en el \hyperref[sec:Chapter4]{Capítulo 4}).
También puede activar opciones, como los modos de edición \emph{emacs} o \emph{vi}, \emph{noclobber} para protegerse contra la sobrescritura inadvertida de archivos y tal vez \emph{ignoreeof} para evitar que las personas cierren la sesión accidentalmente cuando escriben demasiados caracteres CTRL-D. Cualquier script de shell que haya escrito para uso general también contribuye a la personalización.
Desafortunadamente, no es posible crear un alias global. Puede definir alias en \texttt{/etc/profile}, pero no hay forma de hacer que formen parte del entorno para que sus definiciones se propaguen a los subprocesos del shell.
Sin embargo, puede configurar funciones globales. Estas son una excelente manera de personalizar el entorno de su sistema, porque las funciones son parte del shell, no procesos separados. Por ejemplo, es posible que desee hacer que \emph{pushd} y \emph{popd} (consulte el \hyperref[sec:Chapter4]{Capítulo 4} a través del \hyperref[sec:Chapter6]{Capítulo 6}) estén disponibles de manera global.
La mejor manera de crear funciones globales es utilizar la variable integrada \texttt{FPATH} para la carga automática de funciones que presentamos en el \hyperref[sec:Chapter4]{Capítulo 4}. Simplemente defina \texttt{FPATH} como un directorio de biblioteca de funciones, tal vez \texttt{/usr/local/functions}, y conviértalo en una variable de entorno \emph{exportándola}. Luego, asegúrese de que el directorio enumerado en \texttt{FPATH} también esté incluido en \texttt{PATH}. En otras palabras, coloque este código o similar en \texttt{/etc/profile}:
\begin{lstlisting}[language=bash]
FPATH=/usr/local/functions
PATH=$PATH:$FPATH
export FPATH PATH
\end{lstlisting}
Luego, coloque la definición de cada función global en un archivo en ese directorio con el mismo nombre que la función.
En cualquier caso, sugerimos utilizar funciones globales para la personalización global en lugar de scripts de shell. Dada la abundancia de memoria en la actualidad, no hay razón para no incluir funciones generalmente útiles como parte del entorno de sus usuarios.
\section{Personalización de los modos de edición}
Como vimos en el \hyperref[sec:Chapter2]{Capítulo 2}, tienes la opción de utilizar los modos de edición de emacs o vi al editar tu línea de comandos. Además de los comandos disponibles en cada modo, puedes personalizar el comportamiento de los modos de edición según tus necesidades o entorno.
El \hyperref[sec:ApendiceA]{Apéndice A} aborda varios shells de terceros basados en el diseño de las shells Bourne y Korn. Esos shells generalmente proporcionan edición de línea de comandos, así como la capacidad de personalizar el editor mediante un comando integrado especial, un archivo de inicio especial o ambos.
El enfoque del shell de Korn es diferente. Se basa en un paradigma en el que programas el comportamiento que deseas del shell. Esto se logra mediante una trampa falsa, llamada KEYBD. Si existe, la trampa establecida para KEYBD se evalúa cuando \emph{ksh} procesa caracteres normales de entrada de línea de comandos. \footnote{Los caracteres para cadenas de búsqueda y argumentos numéricos para comandos de modos \emph{vi} y \emph{emacs} no activan la trampa KEYBD.}
Dentro del código ejecutado para la trampa, dos variables especiales contienen el texto de la línea de comandos y el texto que se está ingresando y que causó la trampa. Variables adicionales especiales te permiten distinguir entre los modos \emph{emacs} y \emph{vi} e indicar la posición actual en la línea de entrada. Estas variables se enumeran en la Tabla \ref{Tab:10-2}.
\begin{table}[h]
\center
\caption{Variables de edición especiales}
\label{Tab:10-2}
\begin{tabular}{|m{3cm}|m{12cm}|} \hline
\textbf{Variable} & \textbf{Significado} \\ \hline
.sh.edchar & El carácter o secuencia de escape introducido por el usuario que causó la trampa KEYBD. El valor de \texttt{.sh.edchar} al final de la trampa se utiliza entonces para dirigir las acciones del editor incorporado. \\\hline
.sh.edcol & La posición del cursor en la línea de entrada actual. \\\hline
.sh.edmode & Igual a ESC en modo \emph{vi}, vacío en caso contrario. (Utilice \texttt{[[ -n \$\{.sh.edmode\} ]]} para probarlo). \\\hline
.sh.edext & El texto de la línea de entrada actual. \\\hline
\end{tabular}
\end{table}
Al ingresar en la trampa KEYBD, el contenido de \texttt{.sh.edchar} será un solo carácter, ESC seguido de un solo carácter o ESC, [, y un solo carácter. Puedes asignar un nuevo valor a \texttt{.sh.edchar} para cambiar la entrada que recibe el modo de edición actual. Así, la trampa KEYBD te permite interponer un <<filtro>> entre lo que ingresa el usuario y lo que los modos de edición del shell procesan realmente. El siguiente ejemplo es de la página 98 de <<The New KornShell Command and Programming Language>>. \footnote{Este es el libro sobre \emph{ksh93} escrito por David Korn y Morris Bolsky y publicado por Prentice Hall.}
Presenta una función \emph{keybind} que te permite asignar nuevas acciones a secuencias clave de entrada, similar al comando integrado \emph{bind} de muchas otras shells.
\begin{lstlisting}[language=bash]
# Citado de la Página 98 de
# "The New KornShell Command and Programming Language"
1 typeset -A Keytable
2 trap 'eval "${Keytable[${.sh.edchar}]}"' KEYBD
3 function keybind # key [action]
4 {
5 typeset key=$(print -f "%q" "$2")
6 case $# in
7 2) Keytable[$1]=' .sh.edchar=${.sh.edmode}'"$key"
8 ;;
9 1) unset Keytable[$1]
10 ;;
11 *) print -u2 "Usage: $0 key [action]"
12 return 2 # los errores de uso devuelven 2 por defecto
13 ;;
14 esac
15 }
\end{lstlisting}
Esta es una función interesante. Vamos a revisarla línea por línea. La línea 1 crea un array asociativo para actuar como una tabla de pares clave/acción. La línea 2 establece la trampa KEYBD. Obtiene la acción del array asociativo y luego la ejecuta usando \emph{eval}. La línea 3 inicia la función \emph{keybind}, que toma uno o dos argumentos. Con dos argumentos, el segundo argumento se cita adecuadamente primero (línea 5, la variable \emph{key} habría sido mejor nombrada \texttt{action}). La línea 7 luego crea la entrada en el array, utilizando \texttt{\$1} (la secuencia de teclas del usuario) como índice y action citada como el valor a asignar a \texttt{.sh.edchar}. Observa cómo también se incluye \texttt{\$\{.sh.mode\}}. Esto tiene el efecto de forzar un cambio al modo de comando para el modo de edición \emph{vi}. Es esta declaración de asignación generada la que se \emph{evalúa} cada vez que se ejecuta la trampa.
El resto de la función es principalmente tareas administrativas: con un argumento (línea 9), se elimina la entrada dada en el array \texttt{Keytable}. Si hay más de dos argumentos (línea 11), \emph{keybind} imprime un mensaje y luego devuelve el valor (falso) 2.
Aunque algo inusual, el mecanismo de trampa KEYBD para tratar la entrada del usuario es tanto general como extensible; puedes hacer lo que quieras, simplemente como un Asunto Simple de Programación. Con otras shells, estás limitado a las facilidades integradas que proporcionan.
\section{Funciones de seguridad del sistema}
Características de Seguridad del Sistema
La seguridad de Unix es un problema de notoria notoriedad. Prácticamente todos los aspectos de un sistema Unix tienen algún problema de seguridad asociado, y generalmente es trabajo del administrador del sistema preocuparse por este problema.
\textbf{NOTA:} Este no es un libro de texto sobre seguridad del sistema Unix. Ten en cuenta que esta sección apenas toca la punta del iceberg y que hay innumerables otros aspectos de la seguridad del sistema Unix además de cómo está configurada el shell. Consulta al final del capítulo un libro que recomendamos.
Primero presentamos una lista de <<consejos>> para escribir scripts de shell que tengan una mejor probabilidad de evitar problemas de seguridad. Luego cubrimos la \emph{shell restringida}, que intenta poner un chaleco de fuerza alrededor del entorno del usuario. Luego presentamos la idea de un <<caballo de Troya>> y por qué se deben evitar tales cosas. Finalmente, discutimos el \emph{modo privilegiado} del shell de Korn, que se utiliza con scripts de shell que se ejecutan como si el usuario fuera \texttt{root}.
\subsection{Consejos para Scripts de Shell Seguros}
Aquí tienes algunos consejos para escribir scripts de shell más seguros, cortesía del Profesor Eugene (Gene) Spafford, director del Centro de Educación e Investigación en Aseguramiento de la Información y Seguridad de la Universidad de Purdue \footnote{Ver \url {http://www.cerias.purdue.edu}.}:
\begin{description}
\item[No incluyas el punto (\emph{dot})en PATH] Este problema se describió en el \hyperref[sec:Chapter3]{Capítulo 3}. Esto abre la puerta de par en par a <<caballos de Troya>>, que se describen en la siguiente sección.
\item[Protege los directorios \texttt{bin}] Asegúrate de que cada directorio en \texttt{\$PATH} solo sea escribible por su propietario y por nadie más. Lo mismo se aplica a todos los programas \emph{en} los directorios \emph{bin}.
\item[Diseña antes de codificar] Dedica tiempo a pensar en lo que quieres hacer y cómo hacerlo; no simplemente escribas cosas con un editor de texto y sigas pirateando hasta que parezca funcionar. Incluye código para manejar errores y fallas de manera elegante.
\item[Verifica la validez de todos los argumentos de entrada] Si esperas un número, verifica que obtuviste un número. Comprueba que el número esté en el rango correcto. Haz lo mismo para otros tipos de datos; las instalaciones de expresiones regulares del shell son particularmente útiles para esto.
\item[Verifica los códigos de error de todos los comandos que pueden devolver errores] Cosas que no esperas que fallen podrían forzarse maliciosamente a fallar para hacer que el script se comporte de manera incorrecta. Por ejemplo, es posible hacer que algunos comandos fallen incluso como root si el argumento es un disco montado por NFS o un archivo de dispositivo orientado a caracteres.
\item[No confíes en las variables de entorno proporcionadas] Verifícalas y restablécelas a valores conocidos si son utilizadas por comandos posteriores (por ejemplo, \texttt{TZ, FPATH, PATH, IFS}, etc.). El shell Korn restablece automáticamente \texttt{IFS} a su valor predeterminado al inicio, ignorando lo que estaba en el entorno, pero muchas otras shells no lo hacen. En todos los casos, es una excelente idea establecer explícitamente \texttt{PATH} para que contenga solo los directorios \emph{bin} del sistema.
\item[Comienza en un lugar conocido] Cambia explícitamente al directorio conocido cuando el script comienza para que cualquier ruta relativa posterior sea a un lugar conocido. Asegúrate de que el \emph{cd} tenga éxito:
\begin{lstlisting}[language=bash]
cd app-dir || exit 1
\end{lstlisting}
\item[Usa rutas completas para los comandos] Haz esto para saber qué versión estás obteniendo, independientemente de \texttt{\$PATH}.
\item[Utiliza \emph{syslog(8)} para mantener un registro de auditoría] Registra la fecha y hora de la invocación, el nombre de usuario, etc.; consulta \emph{logger(1)}. Si no tienes \emph{syslog}, crea una función para mantener un archivo de registro:
\begin{lstlisting}[language=bash]
function logsys {
print -r -- "$@" >> /var/adm/logsysfile
}
logsys "Ejecutado por el usuario " $(/bin/whoami) "($USER) en " $(/bin/date)
\end{lstlisting}
(\emph{whoami(1)} imprime el nombre de inicio del usuario efectivo, un concepto descrito más adelante en este capítulo).
\item[Siempre entrecomilla la entrada del usuario al usar esa entrada] Por ejemplo, \texttt{"\$1"} y \texttt{"\$*"}. Esto evita que la entrada del usuario maliciosa se evalúe y ejecute aún más.
\item[No uses \texttt{eval} en la entrada del usuario] Además de entrecomillar la entrada del usuario, no la pases a el shell para volver a procesarla con \emph{eval}. Si el usuario lee tu script y ve que usa \emph{eval}, es fácil subvertir el script para que haga casi cualquier cosa.
\item[Entrecomilla los resultados de la expansión de comodines] Hay varias cosas desagradables que puedes hacer a un administrador del sistema creando archivos con espacios, puntos y comas, comillas invertidas, etc., en los nombres de archivos. Si los scripts administrativos no entrecomillan los argumentos de los nombres de archivo, los scripts pueden dañar o revelar el sistema.
\item[Verifica la entrada del usuario en busca de metacaracteres] Busca metacaracteres como \texttt{\$} o \texttt{`} (sustitución de comandos antigua) si utilizas la entrada en un \emph{eval} o \texttt{\$(...)}.
\item[Prueba tu código y léelo críticamente] Busca suposiciones y errores que puedan ser explotados. Ponte de mal humor y lee tu código con la intención de tratar de descubrir cómo subvertirlo. Luego corrige cualquier problema que encuentres.
\item[Ten en cuenta las condiciones de carrera] Si un atacante puede ejecutar comandos arbitrarios entre dos comandos en tu script, ¿comprometerá la seguridad? Si es así, busca otra forma de hacerlo.
\item[Desconfía de los enlaces simbólicos] Cuando \emph{cambias} los permisos o editas un archivo, verifícalo para asegurarte de que sea un archivo y no un enlace simbólico a un archivo crítico del sistema. (Utiliza \texttt{[[ -L file ]]} o \texttt{[[ -h file ]]} para comprobar si \emph{file} es un enlace simbólico).
\item[Haz que otra persona revise tu código en busca de errores] A menudo, un par de ojos frescos puede detectar cosas que el autor original de un programa pasa por alto.
\item[Usa \emph{setgid} en lugar de \emph{setuid}, si es posible] Estos términos se discuten más adelante en este capítulo. En resumen, al usar \emph{setgid}, restringes la cantidad de daño que se puede hacer al grupo que está comprometido.
\item[Utiliza un usuario nuevo en lugar de \texttt{root}] Si debes usar \emph{setuid} para acceder a un grupo de archivos, considera crear un usuario nuevo, \emph{no root}, con ese propósito, y establece \emph{setuid} en él.
\item[Limita el código \emph{setuid} tanto como sea posible] Reduce al mínimo la cantidad de código \emph{setuid} que puedas. Muévelo a un programa separado e invócalo desde un script más grande cuando sea necesario. Sin embargo, ¡asegúrate de programar defensivamente como si el script pudiera ser invocado por cualquiera desde cualquier lugar!
\end{description}
Chet Ramey, el mantenedor de \emph{bash}, ofrece el siguiente prólogo para usar en scripts de shell que necesitan ser más seguros:
\begin{lstlisting}[language=bash]
# restablecer IFS, aunque ksh no importa IFS del entorno,
# $ENV podría establecerlo
IFS=$' \t\n'
# asegurarse de que unalias no sea una función, ya que es un comando incorporado regular
# unset es un comando incorporado especial, por lo que se encontrará antes que las funciones
unset -f unalias
# deshacer todos los alias
# citar unalias para que no sea alias-expandido
\unalias -a
# asegurarse de que command no sea una función, ya que es un comando incorporado regular
# unset es un comando incorporado especial, por lo que se encontrará antes que las funciones
unset -f command
# obtener un prefijo de ruta confiable, manejando el caso en que getconf no está disponible
# (no es demasiado necesario, ya que getconf es un comando incorporado de ksh93)
SYSPATH="$(command -p getconf PATH 2>/dev/null)"
if [[ -z "$SYSPATH" ]]; then
SYSPATH="/usr/bin:/bin" # elige tu veneno
fi
PATH="$SYSPATH:$PATH"
\end{lstlisting}
\subsubsection{Shell restringida}
El shell restringida está diseñada para colocar al usuario en un entorno donde su capacidad para moverse y escribir archivos esté severamente limitada. Generalmente se utiliza para cuentas de invitados. Cuando se invoca como \emph{rksh} (o con la opción \texttt{-r}), \emph{ksh} actúa como una shell restringida. Puedes hacer que el shell de inicio de sesión de un usuario sea restringida colocando la ruta completa de \emph{rksh} en la entrada \texttt{/etc/passwd} del usuario. El archivo ejecutable \emph{ksh} debe tener un enlace a él con el nombre \emph{rksh} para que esto funcione.
Las restricciones específicas impuestas por el shell restringida impiden al usuario hacer lo siguiente:
\begin{itemize}
\item Cambiar directorios de trabajo: \emph{cd} no está operativo. Si intentas usarlo, obtendrás el mensaje de error \texttt{ksh: cd: restricted}.
\item Redirigir la salida a un archivo: los redireccionadores \texttt{>, >|, <>, y >>} no están permitidos. Esto incluye el uso de \emph{exec}.
\item Asignar un nuevo valor a las variables de entorno \texttt{ENV, FPATH, PATH} o \texttt{SHELL}, o intentar cambiar sus atributos con \emph{typeset}.
\item Especificar rutas de comandos con barras inclinadas (/) en ellas. El shell solo ejecuta comandos encontrados a lo largo de \texttt{\$PATH}.
\item Agregar nuevos comandos incorporados con el comando \emph{builtin}. (Esta función muy avanzada está fuera del alcance de este libro).
\end{itemize}
Estas restricciones entran en vigencia después de que se ejecutan los archivos \emph{.profile} y de entorno del usuario. Esto significa que el entorno completo del usuario del shell restringida se configura en \emph{.profile}. Esto permite que el administrador del sistema configure el entorno según considere conveniente.
Para evitar que el usuario sobrescriba \texttt{\~{}/.profile}, no es suficiente hacer que el archivo sea de solo lectura para el usuario. O bien, el directorio principal no debe ser escribible por el usuario, o los comandos en \texttt{\~{}/.profile} deben \emph{cd} a un directorio diferente.
Dos formas comunes de configurar dichos entornos son configurar un directorio de comandos <<seguros>> y hacer que ese directorio sea el único en \texttt{PATH}, y configurar un menú de comandos del cual el usuario no pueda escapar sin salir del shell. En cualquier caso, asegúrate de que no haya otra shell en ningún directorio enumerado en \texttt{\$PATH}; de lo contrario, el usuario puede simplemente ejecutar esa shell y evitar las restricciones mencionadas anteriormente.
\textbf{ADVERTENCIA:} Aunque la capacidad de restringir el shell ha estado disponible (si no necesariamente compilada o documentada) desde la versión original 7 de Bourne shell, rara vez se usa. Configurar un entorno utilizable pero correctamente restringido es difícil en la práctica. Entonces, \emph{caveat emptor}.
\subsection{Caballos de Troya}
El concepto de un \emph{caballo de Troya} fue introducido brevemente en el \hyperref[sec:Chapter3]{Capítulo 3}. Un caballo de Troya es algo que parece inofensivo, o incluso útil, pero que contiene un peligro oculto.
Considera el siguiente escenario. El usuario John Q. Programmer (nombre de inicio de sesión \texttt{jprog}) es un excelente programador y tiene una gran colección de programas personales en \texttt{\~{}jprog/bin}. Este directorio ocurre primero en la variable \texttt{PATH} en \texttt{\~{}jprog/.profile}. Dado que es un buen programador, la administración lo ascendió recientemente a administrador del sistema.
Este es un campo completamente nuevo de actividad, y John, sin saberlo mejor, lamentablemente dejó su directorio \emph{bin} escribible. Llega W. M. Badguy, quien crea el siguiente script de shell, llamado \emph{grep}, en el directorio \emph{bin} de John:
\begin{lstlisting}[language=bash]
/bin/grep "$@"
case $(whoami) in # Verifica el nombre del ID de usuario efectivo
root) Cosas malas aquí # ¡Peligro, Will Robinson, peligro!
rm ~/jprog/bin/grep # Ocultar la evidencia
;;
esac
\end{lstlisting}
En sí mismo, este script no puede causar daño cuando jprog está trabajando como \emph{él mismo}. El problema surge cuando \texttt{jprog} usa el comando \emph{su(1)}. Este comando permite a un usuario regular <<cambiar de usuario>> a un usuario diferente. Por defecto, permite a un usuario regular convertirse en \texttt{root} (siempre y cuando ese usuario conozca la contraseña, por supuesto). El problema es que normalmente, \emph{su} usa cualquier \texttt{PATH} que hereda.\footnote{Adquiere el hábito de usar \texttt{su - user} para cambiar al usuario como si el usuario estuviera haciendo un inicio de sesión real. Esto evita la importación del \texttt{PATH} existente.}
En este caso, \texttt{\$PATH} incluye \texttt{\~{}jprog/bin}. Ahora, cuando \texttt{jprog}, trabajando como \texttt{root}, ejecuta \emph{grep}, en realidad ejecuta la versión del \emph{caballo de Troya} en su \emph{bin}. Esta versión ejecuta el \emph{grep} real, por lo que \texttt{jprog} obtiene los resultados que espera. Pero también ejecuta silenciosamente la parte de cosas malas aquí, como \texttt{root}. Esto significa que Unix permitirá que el script haga lo que quiera. \emph{Cualquier cosa}. Y para empeorar las cosas, al eliminar el caballo de Troya cuando ha terminado, ya no hay evidencia.
Los directorios \emph{bin} escribibles abren una puerta para los \emph{caballos de Troya}, al igual que tener un punto en el \texttt{PATH}. Tener scripts de shell escribibles en cualquier directorio bin es otra puerta. Así como cierras y aseguras las puertas de tu casa por la noche, ¡asegúrate de cerrar cualquier puerta en tu sistema!
\subsubsection{Setuid y modo privilegiado}
Muchos problemas de seguridad de Unix se centran en un atributo de archivo de Unix llamado el bit \emph{setuid} (set user ID). Es como un bit de permiso (consulta la discusión anterior sobre \emph{umask}): cuando un archivo ejecutable lo tiene activado, el archivo se ejecuta con un ID de usuario efectivo igual al propietario del archivo. El ID de usuario efectivo es distinto del ID de usuario real del proceso, y Unix aplica sus pruebas de permisos al ID de usuario efectivo del proceso.
Por ejemplo, supongamos que has escrito un programa de juego muy ingenioso que mantiene un archivo de puntuación privado mostrando a los 15 mejores jugadores en tu sistema. No quieres que el archivo de puntuación sea escribible por cualquier persona, porque cualquiera podría llegar y editarlo para convertirse en el jugador principal. Haciendo tu juego setuid a tu ID de usuario, el programa del juego puede actualizar el archivo, que es de tu propiedad, pero nadie más puede actualizarlo. (El programa del juego puede determinar quién lo ejecutó al mirar su ID de usuario real y usar eso para determinar el nombre de inicio de sesión).
La facilidad \emph{setuid} es una característica agradable para juegos y archivos de puntuación, pero se vuelve mucho más peligrosa cuando se usa para \texttt{root}. Hacer programas \emph{setuid} \texttt{root} permite a los administradores escribir programas que realizan ciertas tareas que requieren privilegios de \texttt{root} (por ejemplo, configurar impresoras) de manera controlada. Para establecer el bit \emph{setuid} de un archivo, el superusuario puede escribir \texttt{chmod 4755 nombrearchivo}; el 4 es el bit setuid.
Existe una facilidad similar a nivel de grupo, conocida (no sorprendentemente) como \emph{setgid} (set group ID). Usa \texttt{chmod 2755 nombrearchivo} para activar los permisos \emph{setgid}. Cuando haces un \texttt{ls -l} en un archivo \emph{setuid} o \emph{setgid}, la \emph{x} en el modo de permisos se reemplaza con una \emph{s}; por ejemplo, \texttt{-rws--s--x} para un archivo que es legible y escribible por el propietario, ejecutable por todos y tiene ambos bits \emph{setuid} y \emph{setgid} activados (modo octal 6711).
La sabiduría moderna de la administración del sistema dice que crear scripts de shell setuid y setgid es una muy, muy mala idea. Esto ha sido especialmente cierto bajo la C shell, porque su archivo de entorno \emph{.cshrc} introduce numerosas oportunidades para intrusiones. En particular, hay varias formas de engañar a un script de shell setuid para que se convierta en una shell \emph{interactiva} con un ID de usuario efectivo de \texttt{root}. Esto es lo mejor que un cracker podría esperar: la capacidad de ejecutar cualquier comando como \texttt{root}.
\textbf{NOTA:} Existe una diferencia importante entre un script de shell \emph{setuid} y una shell \emph{setuid}. Esta última es una copia del ejecutable del shell, que se ha hecho pertenecer a \texttt{root} y se le ha aplicado el bit setuid. En la sección anterior sobre caballos de Troya, supongamos que la parte de \emph{cosas malas} aquí era este código:
\begin{lstlisting}[language=bash]
cp /bin/ksh ~badguy/bin/myls
chown root ~badguy/bin/myls
chmod 4755 ~badguy/bin/myls
\end{lstlisting}
Recuerda, este código se ejecuta como \texttt{root}, por lo que funcionará. Cuando \texttt{badguy} ejecuta \emph{myls}, es un archivo ejecutable en código de máquina, y se honra el bit setuid. Hola shell que se ejecuta como \texttt{root}. ¡Adiós seguridad!
El \emph{modo privilegiado} fue diseñado para proteger contra scripts de shell setuid. Esta es una opción \emph{set -o} (\texttt{set -o privileged} o \texttt{set -p}), pero el shell la activa automáticamente cada vez que ejecuta un script cuyo bit \emph{setuid} está activado, es decir, cuando el ID de usuario efectivo es diferente del ID de usuario real.
En el \emph{modo privilegiado}, cuando se invoca un script de shell Korn \emph{setuid}, el shell ejecuta el archivo \texttt{/etc/suid\_profile}. Este archivo debe escribirse de manera que restrinja los scripts de shell setuid de manera similar a como lo hace la shell restringida. Como mínimo, debería hacer que \texttt{PATH} sea de solo lectura (\texttt{typeset -r PATH} o \texttt{readonly PATH}) y configurarlo en uno o más directorios <<seguros>>. Una vez más, esto evita que se invoquen señuelos.
Dado que el modo privilegiado es una opción, es posible desactivarlo con el comando \texttt{set +o privileged} (o \texttt{set +p}). Pero esto no ayuda al posible cracker del sistema: el shell cambia automáticamente su ID de usuario efectivo para que sea igual al ID de usuario real, es decir, si desactivas el modo privilegiado, también desactivas \emph{setuid}.
Además del modo privilegiado, \emph{ksh} proporciona un programa <<agente>> especial, \texttt{/etc/suid\_exec}, que ejecuta scripts de shell setuid (o scripts de shell que son ejecutables pero no legibles).
Para que esto funcione, el script no debe comenzar con \texttt{\#! /bin/ksh}. Cuando se invoca el programa, \emph{ksh} intenta ejecutar el programa como un ejecutable binario regular. Cuando el sistema operativo no puede ejecutar el script (porque no es binario y porque no tiene el nombre de un intérprete especificado con \texttt{\#!}), \emph{ksh} se da cuenta de que es un script e invoca \texttt{/etc/suid\_exec} con el nombre del script y sus argumentos. También se encarga de pasar un <<token>> de autenticación a \texttt{/etc/suid\_exec} que indica los ID de usuario y grupo reales y efectivos del script. \texttt{/etc/suid\_exec} verifica que es seguro ejecutar el script y luego se encarga de invocar \emph{ksh} con los ID de usuario y grupo reales y efectivos adecuados en el script.
Aunque la combinación de modo privilegiado y \texttt{/etc/suid\_exec} te permite evitar muchos de los ataques a scripts setuid, escribir scripts que se puedan ejecutar setuid de manera segura es un arte difícil, que requiere una buena cantidad de conocimiento y experiencia. Debería hacerse con mucho cuidado.
De hecho, los peligros de scripts de shell setuid y setgid (al menos para shells además de \emph{ksh}) son tan grandes que los sistemas Unix modernos, tanto los sistemas Unix comerciales como los clones de software libre (derivados de 4.4-BSD y GNU/Linux), desactivan los bits setuid y setgid en scripts de shell. Incluso si aplicas los bits al archivo, el sistema operativo no los respeta. \footnote{MacOS X parece ser una notable excepción. ¡Ten mucho cuidado si ejecutas uno o más de esos sistemas!}
Aunque los scripts de shell setuid no funcionan en sistemas modernos, hay ocasiones en las que el modo privilegiado sigue siendo útil. En particular, existe un programa de terceros ampliamente utilizado llamado \emph{sudo}, que, citando la página web, <<permite a un administrador del sistema dar a ciertos usuarios (o grupos de usuarios) la capacidad de ejecutar algunos (o todos) los comandos como \texttt{root} u otro usuario mientras registra los comandos y argumentos>>. La página de inicio de \emph{sudo} es \url{http://www.courtesan.com/sudo/}. Un administrador del sistema podría ejecutar fácilmente \texttt{sudo /bin/ksh -p} para obtener un entorno conocido para realizar tareas administrativas.
Finalmente, si deseas aprender más sobre la seguridad de Unix, recomendamos \emph{Practical UNIX \& Internet Security} de Simson Garfinkel y Gene Spafford. Está publicado por O'Reilly \& Associates.

933
Secciones/Capitulo2.tex Normal file
View File

@ -0,0 +1,933 @@
Siempre es posible cometer errores cuando se escribe en el teclado de un ordenador, pero quizás aún más cuando se utiliza una shell Unix. La sintaxis del shell Unix es potente, pero concisa, llena de caracteres extraños y no especialmente mnemotécnica, lo que permite construir líneas de órdenes tan crípticas como complejas. Los shells Bourne y C agravan esta situación al ofrecerte formas extremadamente limitadas de editar tus líneas de órdenes.
En particular, no hay forma de recuperar una línea de órdenes anterior para poder corregir un error. Por ejemplo, en el \hyperref[sec:Chapter7]{Capítulo 7} veremos líneas de comando complejas como:
\begin{lstlisting}[language=bash]
eval cat \$srcname \| ccom \| optimize \| as \> \$objname
\end{lstlisting}
Si eres un usuario experimentado del shell Bourne, sin duda conoces la frustración de tener que volver a escribir líneas como ésta. Puedes usar la tecla de retroceso para editar, pero una vez que pulsas ENTER, ¡se ha ido para siempre!
El intérprete de comandos C ofrece una pequeña mejora a través de su mecanismo de historial, que proporciona algunas formas muy incómodas de editar comandos anteriores. Pero hay más de una persona que se ha preguntado: <<¿Por qué no puedo editar mis líneas de comandos Unix de la misma forma que puedo editar texto con un editor?>>
Esto es exactamente lo que el shell Korn te permite hacer. Tiene modos de edición que te permiten editar líneas de comandos con comandos de edición similares a los de los dos editores más populares de Unix, vi y Emacs.\footnote{Por alguna razón desconocida, la documentación sobre el modo emacs ha sido eliminada de las páginas de manual de ksh(1) en algunos sistemas Unix. Esto no significa, sin embargo, que el modo no exista o no funcione correctamente.} También proporciona un análogo mucho más extendido del mecanismo de historia del shell de C llamado \emph{hist} (por <<historia>>) que, entre otras cosas, te permite usar tu editor favorito directamente para editar tus líneas de comandos.
En este capítulo, discutiremos las características comunes a todas las facilidades comando-historial del intérprete de órdenes Korn; después trataremos cada una de esas facilidades en detalle. Si usas vi o Emacs, puede que quieras leer sólo la sección sobre el modo de emulación para el que uses.\footnote{Sacarás el máximo provecho de estas secciones si ya estás familiarizado con el/los editor/es en cuestión. Buenas fuentes para información más completa sobre los editores son Learning the vi Editor por Linda Lamb y Arnold Robbins y Learning GNU Emacs por Debra Cameron, Bill Rosenblatt, y Eric Raymond. Ambos publicados por O'Reilly \& Associates.} Si no usas ni \emph{vi} ni Emacs pero estás interesado en aprender uno de los modos de edición de todas formas, sugerimos el modo emacs, porque es más una extensión natural de la capacidad mínima de edición que obtienes con el shell desnudo.
Deberíamos mencionar por adelantado que tanto el modo emacs como el vi introducen la posibilidad de conflictos con las teclas de control establecidas por la interfaz de terminal de Unix. Recuerde las teclas de control mostradas en el \hyperref[sec:Chapter1]{Capítulo 1} en la Tabla \ref{Tab:1-7} y el ejemplo de salida del comando \emph{stty}. Las teclas de control mostradas allí anulan sus funciones en los modos de edición.
Durante el resto de este capítulo, le advertiremos cuando un comando de edición choque con la configuración por defecto de una tecla de control de la interfaz de terminal. Pero si usted (o su administrador de sistema) elige personalizar su interfaz de terminal, como mostramos en el \hyperref[sec:Chapter8]{Capítulo 8}, usted está por su cuenta en lo que concierne a los modos de edición.
\section{Activando la Edición de Línea de Comandos}
Hay dos formas de entrar en uno u otro modo de edición. Primero, puede establecer su modo de edición usando la variable de entorno \texttt{VISUAL}. El shell Korn comprueba si esta variable termina con \texttt{vi} o {emacs}.\footnote{GNU Emacs se instala a veces como gmacs o gnumacs.} Una forma excelente de establecer \texttt{VISUAL} es poner una línea como la siguiente en su fichero \emph{.profile} o de entorno:
\begin{lstlisting}[language=bash]
VISUAL=$(whence emacs)
\end{lstlisting}
o
\begin{lstlisting}[language=bash]
VISUAL=$(whence vi)
\end{lstlisting}
Como se verá en los Capítulos \hyperref[sec:Chapter3]{3} y \hyperref[sec:Chapter4]{4}, el comando incorporado \emph{whence} toma el nombre de otro comando como argumento y escribe la ruta completa del comando en la salida estándar; la forma \emph{\$(command)} devuelve la salida estándar generada por \emph{coamand} como un valor de cadena. Así, la línea anterior encuentra la ruta completa de tu editor favorito y la almacena en la variable de entorno \texttt{VISUAL}. La ventaja de este código es que es portable a otros sistemas, que pueden tener los ejecutables de los editores almacenados en directorios diferentes.
La segunda forma de seleccionar un modo de edición es establecer la opción explícitamente con el comando \emph{set -o}:
\begin{lstlisting}[language=bash]
set -o emacs
\end{lstlisting}
o
\begin{lstlisting}[language=bash]
set -o vi
\end{lstlisting}
Los usuarios de vi pueden desear añadir:
\begin{lstlisting}[language=bash]
set -o viraw
\end{lstlisting}
junto con \texttt{set -o vi}. Esto permite completar TAB en versiones recientes de \emph{ksh93}. La sobrecarga adicional, particularmente en sistemas monousuario, es nominal y, en cualquier caso, no es peor que la del modo emacs. (A partir de \emph{ksh93n}, la opción \emph{viraw} se activa automáticamente cuando se utiliza el modo vi).
Encontrará que los modos de edición vi y emacs son buenos emulando los comandos básicos de estos editores, pero no las características avanzadas; su propósito principal es permitirle transferir <<hábitos de dedo>> de su editor favorito al shell. \emph{hist} es una facilidad poderosa; está pensada principalmente para suplantar al historial del shell C y como una <<escotilla de escape>> para usuarios de editores que no sean vi o Emacs. Por lo tanto, la sección sobre \emph{hist} se recomienda sobre todo a los usuarios del intérprete de comandos C y a aquellos que no utilizan ninguno de los dos editores estándar.
Antes de entrar en detalles, vale la pena mencionar otros dos puntos que se aplican a ambos modos de edición:
\begin{itemize}
\item{\emph{ksh} indica que una línea es más ancha que su pantalla marcando la última columna de la línea visible con un carácter especial: < indica que hay más texto a la izquierda de lo que se ve actualmente, > indica que hay más texto a la derecha de lo que se ve, y * indica que hay texto a ambos lados de lo que se ve actualmente.
\begin{lstlisting}[language=bash]
print this is a very long line that just runs on and >
\end{lstlisting}
}
\item{La personalización de los modos de edición de \emph{ksh93} es posible pero requiere el conocimiento de características avanzadas que aún no hemos cubierto. Consulte el \hyperref[sec:Chapter10]{Capítulo 10} para más detalles.\footnote{El shell de dominio público Korn, \emph{bash} y \emph{zsh} tienen modos de edición personalizables, pero de forma diferente a \emph{ksh93}. Véase el Apéndice A.}}
\end{itemize}
\section{El Archivo Histórico}
Todas las facilidades del historial de comandos del shell Korn dependen de un fichero que almacena los comandos a medida que los tecleas. Este fichero es normalmente \emph{.sh\_history} en tu directorio home, pero puedes llamarlo como quieras configurando la variable de entorno \texttt{HISTFILE} (ver \hyperref[sec:Chapter3]{Capítulo 3}). Cuando ejecutas uno de los modos de edición del shell Korn, en realidad estás ejecutando un mini-editor en tu fichero histórico.
Si ejecuta más de una sesión de inicio de sesión a la vez (por ejemplo, más de un \emph{xterm} en una estación de trabajo X Windows), puede encontrar ventajoso mantener un fichero de historial separado para cada sesión de inicio de sesión. Ponga la siguiente línea en su \emph{.profile}:
\begin{lstlisting}[language=bash]
HISTFILE=~/.hist.$(tty | sed 's;.*/;;')
\end{lstlisting}
Esto crea un archivo de historial cuyo nombre termina con el último componente del nombre del dispositivo de su terminal. Por ejemplo, el nombre del dispositivo de terminal de su ventana podría ser \emph{/dev/pts/42}. El comando \emph{sed} elimina todo hasta la última barra, dejando sólo el 42. El archivo histórico se convierte entonces en \emph{\~{}/.hist.com}. El archivo histórico se convierte entonces en \~{}/.hist.42. Puede eliminar el archivo de historial al cerrar la sesión, como se explica en el \hyperref[sec:Chapter4]{Capítulo 4}. O puede dejar los archivos, como se explica en el \hyperref[sec:Chapter5]{Capítulo 5}. O puede dejar los archivos, y su historial estará allí la próxima vez que inicie una ventana en el mismo dispositivo terminal. (Preservar el historial entre sesiones es el objetivo del archivo de historial, después de todo).
Una alternativa atractiva es utilizar un único fichero de historial para todas las ventanas. Cada instancia en ejecución del shell Korn es lo suficientemente inteligente como para compartir su archivo con otras instancias en ejecución; desde una segunda ventana, puede recuperar y editar los comandos ejecutados en la primera ventana.
Otra variable de entorno, \texttt{HISTSIZE}, se puede utilizar para determinar el número máximo de comandos accesibles desde el archivo de historial. El valor predeterminado es 128 (es decir, los 128 comandos más recientes), que debería ser más que suficiente.
\section{Modo de Edición Emacs}
Si eres usuario de Emacs, te resultará muy útil pensar en el modo de edición emacs como un Emacs simplificado con una única ventana de una línea. Todos los comandos básicos están disponibles para el movimiento del cursor, cortar y pegar, y buscar.
\subsection{Comando Básicos}
El modo Emacs utiliza teclas de control para las funciones de edición más básicas. Si no estás familiarizado con Emacs, puedes pensar en ellas como extensiones del rudimentario carácter <<borrar>> (normalmente retroceso o DEL) que Unix proporciona a través de su interfaz a los terminales de los usuarios. De hecho, el modo-emacs averigua cuál es tu carácter de borrado y lo usa como tecla de borrar-atrás. En aras de la consistencia, asumiremos que tu carácter de borrado es DEL a partir de ahora; si es CTRL-H o cualquier otro, necesitarás hacer una sustitución mental. Los comandos más básicos de las teclas de control se muestran en la Tabla \ref{tab2.1}.
\begin{table}[h]
\center
\caption{Comandos básicos del modo emacs}
\label{tab2.1}
\begin{tabular}{m{2cm}|m{10cm}} \hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
CTRL-B & Retroceder un carácter (sin borrar) \\
CTRL-F & Avanzar un carácter \\
DEL & Borrar un carácter hacia atrás \\
CTRL-D & Borrar un carácter hacia delante \\
CTRL-Y & Recuperar (<<yank>>) el último elemento borrado \\
\end{tabular}
\end{table}
\textbf{ADVERTENCIA:} ¡Recuerda que teclear CTRL-D cuando tu línea de comandos está vacía puede cerrar tu sesión!
Los hábitos básicos del modo emacs son fáciles de aprender, pero requieren que asimiles un par de conceptos peculiares del editor Emacs.
El primero de ellos es el uso de CTRL-B y CTRL-F para avanzar y retroceder el cursor. Estas teclas tienen la ventaja de ser mnemotécnicas obvias, pero mucha gente prefiere usar las teclas de flecha que hay en casi todos los teclados hoy en día.
Desafortunadamente, el modo emacs no usa las teclas de dirección,\footnote{De hecho, como se describe en el Apéndice B, a partir de \emph{ksh93h}, si su terminal utiliza secuencias de escape estándar ANSI para las teclas de flecha, puede utilizarlas.} porque los códigos que transmiten al ordenador no están completamente estandarizados; el modo emacs fue diseñado para funcionar en la mayor variedad posible de terminales sin la pesada personalización que necesita el Emacs completo. Casi los únicos requisitos de hardware del modo emacs son que el carácter ESPACIO sobrescriba el carácter sobre el que se escribe, y que RETROCESO se mueva a la izquierda sin sobrescribir el carácter actual.
En modo emacs, el punto (a veces también llamado punto) es un lugar imaginario justo a la izquierda del carácter sobre el que está el cursor. En las descripciones de los comandos de la Tabla 2-1, algunos dicen <<hacia delante>> mientras que otros dicen <<hacia atrás>>. Piensa en adelante como <<a la derecha del punto>> y atrás como <<a la izquierda del punto>>.
Por ejemplo, digamos que tecleas una línea y, en lugar de teclear ENTER, tecleas CTRL-B y lo mantienes pulsado para que se repita. El cursor se moverá hacia la izquierda hasta situarse sobre el primer carácter de la línea, de esta forma:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (*\highlight{f}*)grep -l Bob < ~pete/wk/names
\end{lstlisting}
Ahora el cursor está en la f, y el punto está al principio de la línea, justo antes de la f. Si tecleas DEL, no ocurrirá nada porque no hay caracteres a la izquierda del punto. Sin embargo, si pulsas CTRL-D (el comando <<borrar carácter hacia delante>>) borrarás la primera letra:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (*\highlight{g}*)rep -l Bob < ~pete/wk/names
\end{lstlisting}
El punto todavía está al principio de la línea. Si este fuera el comando deseado, podría pulsar ENTER ahora y ejecutarlo; no necesita mover el cursor de vuelta al final de la línea. Sin embargo, si quisiera, podría teclear CTRL-F repetidamente para llegar allí:
\begin{lstlisting}[language=bash]
$ grep -l Bob < ~pete/wk/names
\end{lstlisting}
En este punto, teclear CTRL-D no haría nada, pero pulsar DEL borraría la s final. Si tecleas DEL y decides que quieres recuperar la s, pulsa CTRL-Y para recuperarla. Si piensas que este ejemplo es una tontería, tienes razón en este caso concreto, pero ten en cuenta que CTRL-Y deshace el último comando de borrado de cualquier tipo, incluidos los comandos borrar-palabra y borrar-línea que veremos en breve \footnote{Los usuarios de Emacs deben tener en cuenta que este uso de CTRL-Y es diferente del editor completo, que no guarda los borrados de caracteres.}.
Si haces varios borrados seguidos, CTRL-Y te devuelve todo lo que has borrado. Su memoria se remonta a la última pulsación que no haya sido un borrado; los borrados no tienen por qué ser del mismo tipo. Por ejemplo, si escribes DEL SPACE DEL SPACE CTRL-D CTRL-K, al escribir CTRL-Y recuperas el resultado de las tres últimas operaciones, pero no el primer borrado.
\subsection{Comandos de palabras}
Los comandos básicos son realmente todo lo que necesitas para moverte por una línea de comandos, pero un conjunto de comandos más avanzados te permite hacerlo con menos pulsaciones. Estos comandos operan sobre \emph{palabras} en lugar de sobre caracteres sueltos; el modo emacs define una palabra como una secuencia de uno o más caracteres alfanuméricos o guiones bajos. (Para el resto de esta discusión, será útil pensar en el guión bajo como una letra, aunque realmente no lo sea).
Los comandos de palabra se muestran en la Tabla \ref{tab2.2}. Mientras que los comandos básicos son todos de un solo carácter, los comandos de palabra consisten en dos pulsaciones de tecla, ESC seguida de una letra. Notará que el comando ESC X, donde X es cualquier letra, a menudo hace para una palabra lo que CTRL-X hace para un solo carácter. La multiplicidad de opciones para borrar-palabra-atrás surge del hecho de que tu carácter de borrado puede ser CTRL-H o DEL.
\begin{table}[h]
\center
\caption{Comandos de palabra en modo Emacs}
\label{tab2.2}
\begin{tabular}{m{6cm}|m{7cm}} \hline
\textbf{Opción} & \textbf{Significado} \\ \hline
ESC b & Retroceder una palabra \\
ESC f & Mover una palabra hacia adelante \\
ESC DEL, ESC h, ESC CTRL-H & Borrar una palabra hacia atrás \\
ESC d & Borrar una palabra hacia adelante \\
\end{tabular}
\end{table}
Volviendo a nuestro ejemplo: si tecleamos ESC b, point retrocede una palabra. Como / no es un carácter alfanumérico, emacs-mode se detiene ahí:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ grep -l Bob < ~pete/wk/(*\highlight{n}*)ames
\end{lstlisting}
El cursor está sobre la \texttt{n} en \emph{names}, y el punto está entre la / y la \texttt{n}. Ahora digamos que queremos cambiar el argumento de la opción \emph{-l} de este comando de \emph{Bob} a \emph{Dave}. Necesitamos retroceder en la línea de comandos, así que tecleamos ESC b dos veces más. Esto nos lleva aquí:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ grep -l Bob < ~(*\highlight{p}*)ete/wk/names
\end{lstlisting}
Si tecleamos ESC b de nuevo, acabaremos al principio de Bob:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ grep -l (*\highlight{B}*)ob < ~pete/wk/names
\end{lstlisting}
¿Por qué? Recuerde que una palabra se define como una secuencia de caracteres alfanuméricos solamente; por lo tanto < no es una palabra, y la siguiente palabra en la dirección hacia atrás es Bob. Ahora estamos en la posición correcta para borrar Bob, así que tecleamos ESC d y obtenemos:
\begin{lstlisting}[language=bash]
$ grep -l < ~pete/wk/names
\end{lstlisting}
Ahora podemos escribir el argumento deseado:
\begin{lstlisting}[language=bash]
$ grep -l Dave< ~pete/wk/names
\end{lstlisting}
El comando CTRL-Y <<undelete>> recuperará una palabra entera, en lugar de un carácter, si una palabra fue lo último que se borró.
\subsection{Comandos de línea}
Todavía hay formas más eficientes de moverse por una línea de órdenes en modo emacs. Unos pocos comandos se ocupan de toda la línea; se muestran en la Tabla \ref{tab2.3}.
\begin{table}[h]
\center
\caption{Comandos de línea en modo Emacs}
\label{tab2.3}
\begin{tabular}{m{2cm}|m{10cm}} \hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
CTRL-A & Mover al principio de la línea \\
CTRL-E & Mover al final de la línea \\
CTRL-K & Suprimir (<<kill>>) hacia delante hasta el final de línea \\
CTRL-C & Poner en mayúscula el carácter después del punto \\
\end{tabular}
\end{table}
CTRL-C es a menudo la tecla de <<interrupción>> que Unix proporciona a través de su interfaz con tu terminal. Si este es el caso, CTRL-C en modo emacs borrará toda la línea, como si se pulsaran CTRL-A y CTRL-K. En sistemas donde la tecla de interrupción está configurada para otra cosa (a menudo DEL), CTRL-C pone en mayúsculas el carácter actual.
Usar CTRL-A, CTRL-E y CTRL-K debería ser sencillo. Recuerda que CTRL-Y siempre deshará lo último borrado, ya sea de un solo comando de borrado o de varios comandos de borrado seguidos. Si usas CTRL-K, podrían ser bastantes caracteres.
\subsection{Desplazarse por el archivo histórico}
Ahora sabemos cómo movernos eficientemente por la línea de comandos y hacer cambios. Pero eso no resuelve el problema original de recuperar comandos anteriores accediendo al fichero de historial. El modo Emacs tiene varios comandos para hacer esto, resumidos en la Tabla \ref{tab2.4}.
\newpage
\begin{table}[h]
\center
\caption{Comandos del modo Emacs para moverse por el fichero histórico}
\label{tab2.4}
\begin{tabular}{m{2cm}|m{7cm}} \hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
CTRL-P & Ir a la línea anteriora \\
CTRL-N & Ir a la línea siguiente \\
CTRL-R & Buscar hacia atrás \\
ESC < & Ir a la primera línea del historial \\
ESC > & Ir a la última línea del historial \\
\end{tabular}
\end{table}
CTRL-P es, con diferencia, la que usarás más a menudo: es la tecla de <<he cometido un error; déjame volver atrás y arreglarlo>>. Puedes utilizarla tantas veces como desees para desplazarte hacia atrás por el historial. Si quieres volver al último comando que introdujiste, puedes mantener pulsado CTRL-N hasta que el shell Korn emita un pitido, o simplemente teclear ESC >. Por ejemplo, pulsa ENTER para ejecutar el comando anterior, pero aparece un mensaje de error que le indica que la letra de la opción era incorrecta. Quieres cambiarla sin volver a escribir todo. Primero, teclearías CTRL-P para recuperar el comando incorrecto. Lo recuperas con punto al final:
\begin{lstlisting}[language=bash]
$ grep -l Dave < ~pete/wk/names
\end{lstlisting}
Después de CTRL-A, ESC f, dos CTRL-F y CTRL-D, tienes:
\begin{lstlisting}[language=bash]
ve < ~pete/wk/names
\end{lstlisting}
Decides probar con \emph{-s} en lugar de \emph{-l}, así que tecleas \texttt{s} y pulsas ENTER. Obtienes el mismo mensaje de error, así que te rindes y buscas en el manual. Descubres que el comando que quieres es \emph{fgrep} -- no \emph{grep} -- después de todo. Suspiras pesadamente y vuelves a buscar el comando \emph{fgrep} que tecleaste hace una hora. Para ello, tecleas CTRL-R; lo que había en la línea desaparece y es sustituido por \^{}R. Luego escribes \emph{fgrep}, y ves esto:
\begin{lstlisting}[language=bash]
$ ^Rfgrep
\end{lstlisting}
Pulsa ENTER, y el intérprete de comandos buscará en el historial una línea que contenga <<fgrep>>. Si no encuentra ninguna, emite un pitido. Pero si encuentra una, la muestra, y tu <<línea actual>> será esa línea (es decir, estarás en algún lugar en medio del fichero histórico, no al final como es habitual):
\begin{lstlisting}[language=bash]
$ fgrep -l Bob < ~pete/wk/names
\end{lstlisting}
Escribir CTRL-R sin un argumento (es decir, sólo CTRL-R seguido de ENTER) hace que el shell repita su última búsqueda hacia atrás. Si intentas el comando \emph{fgrep} pulsando ENTER de nuevo, ocurren dos cosas. Primero, por supuesto, el comando se ejecuta. Segundo, la línea de comando ejecutada se introduce en el archivo de historial al final, y su <<línea actual>> también estará al final. Ya no estarás en medio del fichero histórico.
CTRL-P y CTRL-R son claramente los comandos más importantes del modo emacs que tienen que ver con el fichero histórico, y puede que utilices CTRL-N ocasionalmente. Los otros son menos útiles, y sospechamos que se incluyeron principalmente por compatibilidad con el editor Emacs completo.
Los usuarios de Emacs también deberían tener en cuenta que las capacidades de búsqueda <<de lujo>> del editor completo, como la búsqueda incremental y de expresiones regulares, no están disponibles en el modo emacs de el Shell de Korn -- con una pequeña excepción: si usas CTRL-R y precedes tu cadena de búsqueda con un \^{} (carácter de intercalación), sólo coincidirá con comandos que tengan la cadena de búsqueda al principio de la línea.
\subsection{Nombre de archivo y finalización y expansión de variable}
Una de las funciones más potentes (y normalmente infrautilizadas) del modo emacs es su función de \emph{completado de names} de archivo, inspirada en funciones similares del editor Emacs completo, el intérprete de comandos C y (originalmente) el antiguo sistema operativo DEC TOPS-20.
La premisa detrás del completado de names de archivo es que cuando necesites escribir un nombre de archivo, no deberías tener que escribir más de lo necesario para identificar el archivo sin ambigüedades. Se trata de una función excelente; existe una análoga en el modo vi. Te recomendamos que la domines, ya que te ahorrará bastante tecleo.
Hay tres comandos en modo emacs relacionados con la compleción de names de archivo. El más importante es TAB. (A los usuarios de Emacs les resultará familiar; es lo mismo que completar el minibuffer con la tecla TAB). Cuando escribe una palabra de texto seguida de TAB, el shell Korn intenta completar el nombre de un fichero en el directorio actual. Entonces puede ocurrir una de estas cuatro cosas:
\begin{enumerate}
\item{Si no hay ningún fichero cuyo nombre empiece por la palabra, el shell emite un pitido y no ocurre nada más.}
\item{Si hay exactamente una forma de completar el nombre del archivo, y el archivo es un archivo normal, el shell escribe el resto del nombre del archivo y lo sigue con un espacio para que pueda escribir más argumentos de comando.}
\item{Si hay exactamente una forma de completar el nombre del archivo, y el archivo es un directorio, el shell completa el nombre del archivo y lo sigue con una barra.}
\item{Si hay más de una forma de completar el nombre de archivo, el shell lo completa con el prefijo común más largo entre las opciones disponibles.}
\end{enumerate}
Por ejemplo, suponga que tiene un directorio con los archivos \emph{program.c} y \emph{problem.c}. Desea compilar el primero de ellos escribiendo \texttt{cc programa.c}. Escriba \texttt{cc pr} seguido de TAB. Se trata de un prefijo ambiguo, ya que el prefijo <<pro>> es común a ambos names de archivo, por lo que el intérprete de comandos sólo completa \texttt{cc pro}. Necesitas escribir más letras para desambiguar, así que escribes g y pulsas TAB de nuevo. Entonces el intérprete de comandos completa a <<\texttt{cc program.c}>>, dejando el espacio extra para que usted escriba otros names de archivo u opciones.
Un comando relacionado es ESC *, que expande el prefijo a todas las opciones posibles. ESC * actúa como el carácter comodín estándar * del shell, excepto que expande las opciones para que las veas y no ejecuta el comando. En el ejemplo anterior, si escribes ESC * en lugar de TAB, el shell se expandirá a <<\texttt{cc problem.c program.c}>>, Si escribe ESC = en lugar de ESC *, verá una lista numerada de expansiones impresa en el error estándar.
A partir de \emph{ksh93m}, el comando ESC = acepta un prefijo numérico. Cuando se proporciona un prefijo, el shell lo trata como el número de uno de los comandos mostrados por un listado ESC = anterior y completa el nombre de archivo. (Más adelante en este capítulo se proporciona un ejemplo donde se describe la versión en modo vi de este comando).
Cuando se usan TAB, ESC * y ESC = en la primera palabra de la línea de comandos, expanden alias, funciones y comandos. Esta característica tan útil se conoce como \emph{finalización de comandos}.
Por compatibilidad con \emph{ksh88} y versiones de \emph{ksh93} anteriores a \emph{ksh93h}, puede escribir ESC ESC para completar el nombre de archivo y el comando.
A partir de \emph{ksh93l}, los modos de edición entienden las reglas de entrecomillado de \emph{ksh}; las expansiones se ignoran dentro de las comillas. Sin embargo, si ha escrito una comilla inicial pero aún no ha cerrado las comillas, los comandos de finalización funcionan. Además, las tres expansiones funcionan también con names de variables. (Las variables se tratan en el \hyperref[sec:Chapter4]{Capítulo 4}.) Cuando \emph{ksh} ve un \$ o ''\$ y parte de un nombre de variable, puede usar cualquiera de las tres expansiones para ver qué names de variables coinciden con lo que ha escrito.
\newpage
\subsection{Comandos misceláneos}
Varios comandos misceláneos completan el modo de edición de emacs; se muestran en la Tabla \ref{tab2.5}.
\begin{table}[h]
\center
\caption{Comandos varios en modo Emacs}
\label{tab2.5}
\begin{tabular}{|m{4cm}|m{11cm}|} \hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
CTRL-J & Igual que ENTER. \\\hline
CTRL-L & Vuelve a mostrar la línea. \\\hline
CTRL-M & Igual que ENTER. \\\hline
CTRL-O & Igual que ENTER, luego muestra la línea siguiente en el archivo histórico. \\\hline
CTRL-T & Transpone los caracteres a ambos lados del punto. Es como GNU Emacs.\tablefootnote{Esta es una diferencia con \emph{ksh88}, que transpone dos caracteres a la derecha del punto y mueve el punto uno hacia adelante. CTRL-T se comporta de forma ligeramente diferente si pones \texttt{set -o gmacs} (en lugar de \texttt{emacs}) en tu \emph{.profile}. En este caso, transpone los dos caracteres a la izquierda del punto, dejando el punto sin mover. Esta es la única diferencia entre los modos emacs y gmacs; este último se ajusta a la versión de James Gosling del editor Emacs (también conocido como Unipress Emacs, que ya no está disponible).} \\\hline
CTRL-U & Repite el siguiente comando cuatro veces. \\\hline
CTRL-V & Imprime la versión del shell Korn. \\\hline
CTRL-W & Borra (<<wipe>>) todos los caracteres entre punto y <<mark>>. <<Marcar>> se trata más adelante en esta sección. \\\hline
CTRL-X CTRL-E & Invoca un editor -- normalmente el programa emacs -- en el comando actual. \\\hline
CTRL-X CTRL-X & Intercambia punto y marca. \\\hline
CTRL-[ & Igual que ESC (la mayoría de los teclados). \\\hline
CTRL-] x & Busca x hacia adelante en la línea actual, donde x es cualquier carácter. \\\hline
CTRL-@ & Marca el punto. \\\hline
ESC c & Cambia la palabra después del punto a mayúsculas. \\\hline
ESC l & Cambiar la palabra después del punto a todas las letras minúsculas. \\\hline
ESC p & Guardar todos los caracteres entre el punto y la marca como si se hubieran borrado. \\\hline
ESC . & Inserta la última palabra de la línea de comando anterior después del punto. \\\hline
ESC \_ & Igual que la entrada anterior. \\\hline
ESC CTRL-] x & Buscar x hacia atrás, donde x es cualquier carácter. \\\hline
ESC SPACE & Poner marca en el punto. \\\hline
ESC \# & Antepone \# (carácter de comentario) a la línea y la envía al archivo de historial; útil para guardar un comando que se ejecutará más tarde sin tener que volver a escribirlo. Si la línea ya empieza con \#, elimina el \# inicial y cualquier otro carácter de comentario que siga a las nuevas líneas en un comando multilínea. \\\hline
\end{tabular}
\end{table}
Varios de estos comandos pueden entrar en conflicto con las teclas de control de la interfaz de terminal de su sistema. CTRL-U es la tecla por defecto para <<kill line>> en la mayoría de las versiones de Unix. Los sistemas Unix modernos utilizan CTRL-V y CTRL-W como ajustes por defecto para las funciones de interfaz de terminal <<citar siguiente carácter>> y <<borrar palabra>>, respectivamente. CTRL-V es particularmente confuso, ya que está pensado para anular otras teclas de control de la interfaz de terminal pero no tiene efecto sobre los comandos de emacs-mode. Sin embargo, emacs-mode funciona interpretando directamente cada carácter que escribes, por lo que la configuración de \emph{stty} se ignora en gran medida.
Vale la pena discutir algunos comandos misceláneos, aunque no estén entre los comandos más útiles de emacs-mode.
CTRL-O es útil para repetir una secuencia de comandos que ya has introducido. Simplemente vuelve al primer comando de la secuencia y pulsa CTRL-O en lugar de ENTER. Esto ejecuta el comando y muestra el siguiente comando en el archivo de historial. Pulsa CTRL-O de nuevo para ejecutar este comando y que aparezca el siguiente. Repite esto hasta que veas el último comando de la secuencia; entonces simplemente pulsa ENTER.
CTRL-U, si no realiza la función de borrado de línea de la interfaz de terminal de tu sistema, repite el siguiente comando cuatro veces. Si tecleas CTRL-U dos veces, el factor de repetición se convierte en 16; para 3 CTRL-Us es 64; y así sucesivamente. CTRL-U es posiblemente más útil cuando navegas a través de tu archivo histórico. Si quieres recordar un comando que introdujiste hace un rato, puedes teclear CTRL-U CTRL-P para retroceder cuatro líneas a la vez a través del archivo histórico; puedes pensar en esto como un <<rebobinado rápido>> a través de tu historial de comandos.
Otro posible uso de CTRL-U es cuando quieres ir de un extremo a otro de una ruta larga. A diferencia del modo vi, el modo emacs no tiene un concepto de <<palabra>> lo suficientemente flexible como para distinguir entre names de ruta y componentes de names de archivo. Los comandos de movimiento de palabra de emacs-mode (ESC b y ESC f) se mueven a través de un nombre de ruta sólo un componente a la vez, porque emacs-mode trata la barra como un separador de palabras. Puedes usar CTRL-U para evitar esta limitación. Si tienes una línea como esta:
\begin{lstlisting}[language=bash]
$ ls -l /a/very/long/pathname/filename
\end{lstlisting}
y necesitas volver atrás y cambiar <<very>> por <<really>>, puedes teclear CTRL-U ESC b y tu cursor terminará aquí:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ ls -l /a/(*\highlight{v}*)ery/long/pathname/filename
\end{lstlisting}
A continuación, puedes hacer el cambio. Primero, deshazte de <<very>> escribiendo CTRL-U CTRL-D:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ ls -l /a/(*\highlight{/}*)long/pathname/filename
\end{lstlisting}
A continuación, inserta el nuevo texto:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ ls -l /a/really(*\highlight{/}*)long/pathname/filename
\end{lstlisting}
El uso juicioso de CTRL-U puede ahorrarte algunas pulsaciones, pero teniendo en cuenta la pequeña cantidad de información que manipulas cuando editas líneas de comandos, probablemente no sea una función increíblemente vital. A menudo, mantener pulsada una tecla para repetirla es tan eficaz como CTRL-U. Dado que probablemente tendrás que utilizar el comando \emph{stty} para redefinir la tecla de borrado de línea del controlador de terminal antes de poder utilizar CTRL-U, probablemente sea mejor prescindir de ella.
La marca mencionada en la explicación de CTRL-W debería ser familiar para los usuarios de Emacs, pero su función en emacs-mode es un subconjunto de la que tiene en el editor completo. El modo Emacs mantiene un registro del lugar en el que se realizó la última operación de borrado (ya sea un carácter, palabra, línea o lo que sea); este lugar se llama \emph{marca}. Si no se ha borrado nada en la línea actual, la marca por defecto es el principio de la línea. También puede establecer la marca en el lugar donde se encuentra el cursor escribiendo ESC ESPACIO (o, alternativamente, CTRL-@). CTRL-X CTRL-X (CTRL-X pulsado dos veces) hace que el shell Korn intercambie el punto y la marca, es decir, que mueva el cursor a donde está la marca y vuelva a poner la marca donde estaba el cursor antes de teclear CTRL-X CTRL-X.
El concepto de marca no es extremadamente útil debido a la poca <<distancia>> que hay que recorrer en las líneas de comandos. Pero si alguna vez tienes que hacer una serie de cambios en el mismo lugar de una línea, CTRL-X CTRL-X te llevará de vuelta allí. En el ejemplo anterior, si quisieras cambiar <<really>> por <<monumentalily>>, una forma sería escribir CTRL-X CTRL-X para volver al principio de <<really>>:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ ls -l /a/(*\highlight{r}*)eally/long/pathname/filename
\end{lstlisting}
Luego podrías teclear ESC d para borrar <<really>> y hacer el cambio. Por supuesto, podrías hacer esto más rápido tecleando ESC DEL en lugar de CTRL-X CTRL-X y ESC d.
De los comandos de cambio de mayúsculas y minúsculas, ESC l (letra ell) es útil cuando pulsas la tecla BLOQ MAYÚS por accidente y no te das cuenta inmediatamente. Como las palabras en mayúsculas no se usan muy a menudo en el mundo Unix, es posible que no uses ESC c muy a menudo.
Si le parece que hay demasiados sinónimos de ENTER, tenga en cuenta que CTRL-M es en realidad el mismo carácter (ASCII) que ENTER, y que CTRL-J es en realidad lo mismo que nueva línea, que Unix suele aceptar en lugar de ENTER de todos modos.
ESC . y ESC \_ son útiles si desea ejecutar varios comandos en un archivo dado. La convención habitual de Unix es que un nombre de fichero es el último argumento de un comando. Por lo tanto, puede ahorrar tecleando simplemente cada comando seguido de SPACE y luego tecleando ESC . o ESC \_. Por ejemplo, digamos que quieres examinar un archivo usando \emph{more}, así que tecleas:
\begin{lstlisting}[language=bash]
$ more myfilewithaverylongname
\end{lstlisting}
Luego decides que quieres imprimirlo, usando el comando de impresión \emph{lp}. Puede evitar teclear el nombre muy largo escribiendo \emph{lp} seguido de un espacio y luego ESC . o ESC \_; el shell Korn inserta \emph{myfilewithaverylongname} por usted.
Si eres un verdadero experto en Emacs y el modo incorporado no te funciona, usa CTRL-X CTRL-E para invocar el programa editor \emph{emacs} en tu línea de comandos. Cuando salgas del editor, si realmente hiciste cambios en el archivo, el shell ejecutará la línea de comandos final.
\subsection{Expansión de Macros con Alias}
A medida que te acostumbres a usar el modo emacs, puede que te des cuenta de que hay secuencias de comandos que ejecutas una y otra vez. Escribir estos comandos repetidamente es difícil y una pérdida de tiempo. Es mejor definir una \emph{macro} para ellos. Una macro es un nombre corto que, cuando se introduce, se expande en la secuencia completa de comandos.
El shell Korn proporciona una función de macro, utilizando el mecanismo de alias (descrito en el siguiente capítulo), que le permite establecer una secuencia de comandos y luego invocarla con un único comando en modo emacs. Funciona de la siguiente manera: si defines un alias llamado \emph{\_x}, donde x es una letra, entonces cuando tecleas ESC x, emacs-mode expande el alias, y lo lee como entrada. El valor del alias puede contener texto normal, comandos emacs-mode, o ambos.
Por ejemplo, suponga que quiere que un comando ponga en mayúscula la primera letra de la palabra actual. Podrías definir un alias como sigue
\begin{lstlisting}[language=bash]
alias _C='^[b^C' # El valor es ESC b CTRL-C
\end{lstlisting}
Ahora, cada vez que escriba ESC C, el intérprete de comandos se moverá al principio de la palabra actual (ESC b) y, a continuación, pondrá en mayúscula la letra actual (CTRL-C).
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ print here is a word # Type ESC C
$ print here is a W(*\highlight{o}*)rd
\end{lstlisting}
\section{Modo de Edición Vi}
Al igual que emacs-mode, vi-mode crea esencialmente una ventana de edición de una línea en el archivo de historial. El modo vi es popular porque vi es el editor más estándar de Unix. Pero la función para la que fue diseñado vi, escribir programas en C, tiene unos requisitos de edición diferentes a los de los intérpretes de comandos. Como resultado, aunque es posible hacer cosas complejas en vi con relativamente pocas pulsaciones de tecla, las cosas relativamente simples que necesitas hacer en el shell Korn a veces requieren demasiadas pulsaciones de tecla.
Al igual que \emph{vi}, vi-mode tiene dos modos propios: modo de entrada y modo de control. El primero es para teclear comandos (como en el uso normal del shell Korn); el segundo es para moverse por la línea de comandos y el fichero histórico. Cuando estás en modo de entrada, puedes escribir comandos y pulsar ENTER para ejecutarlos. Además, tiene capacidades mínimas de edición a través de caracteres de control, que se resumen en la Tabla \ref{tab2.6}.
\begin{table}[h]
\center
\caption{Comandos de edición en modo de entrada vi}
\label{tab2.6}
\begin{tabular}{m{2cm}|m{13cm}}
\hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
DEL & Borrar el carácter anterior \\
CTRL-W & Borrar palabra anterior (es decir, borrar hasta el espacio en blanco) \\
CTRL-V & <<Citar>> el carácter siguiente \\
ESC & Entrar en modo de control (véase más abajo) \\
\end{tabular}
\end{table}
\textbf{NOTA:} Al menos algunos de estos comandos de edición -- dependiendo de la versión de Unix que tenga -- son los mismos que los proporcionados por los sistemas Unix modernos en la interfaz de terminal. Vi-mode usa su caracter <<erase>> como la tecla <<delete previous character>> (borrar caracter previo); usualmente se establece en DEL o CTRL-H (BACKSPACE). CTRL-V hace que el siguiente carácter que escriba aparezca en la línea de comandos tal cual; es decir, si es un comando de edición (o un carácter especial como CTRL-D), se le quita su significado especial.
En circunstancias normales, sólo tiene que permanecer en el modo de entrada. Pero si quieres volver atrás y hacer cambios en tu línea de comandos, o si quieres recuperar comandos anteriores, necesitas ir al modo de control. Para ello, pulsa ESC.
\subsection{Comandos de Modo de Control Simple}
En el modo de control tienes a tu disposición una amplia gama de comandos de edición de \emph{vi}. Los más sencillos te mueven por la línea de comandos\footnote{Al igual que con el modo emacs, desde ksh93h, puede utilizar secuencias de teclas de flecha estándar ANSI para moverse hacia adelante y hacia atrás en la línea de comandos, y hacia arriba y hacia abajo dentro de la lista del historial.} y se resumen en la Tabla \ref{Tab:2-7}. El modo vi contiene dos conceptos de <<palabra>>. El más simple es cualquier secuencia de caracteres que no sean espacios en blanco; lo llamaremos \emph{palabra no en blanco}. El otro es cualquier secuencia de sólo caracteres alfanuméricos (letras y dígitos) o cualquier secuencia de sólo caracteres no alfanuméricos; llamaremos a esto una \emph{palabra}.\footnote{Ninguna de estas definiciones es la misma que la definición de palabra en modo emacs.}
\begin{table}[h]
\center
\caption{Comandos básicos del modo de control vi}
\label{Tab:2-7}
\begin{tabular}{m{2cm}|m{10cm}}
\hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
h & Mover un carácter a la izquierda. \\
l & Mover un carácter a la derecha. \\
space & Mover a la derecha un caracter. \\
w & Mover a la derecha una palabra. \\
b & Mover una palabra a la izquierda. \\
W & Mover al principio de la siguiente palabra no en blanco. \\
B & Mover al principio de la palabra anterior. \\
e & Mover al final de la palabra actual. \\
E & Mover al final de la palabra actual no en blanco. \\
0 & Va al principio de la línea. \\
\^{} & Va al primer carácter no en blanco de la línea. \\
\$ & Va al final de la línea. \\
\end{tabular}
\end{table}
Todos estos comandos, excepto los tres últimos, pueden ir precedidos de un número que actúa como \emph{contador de repeticiones}. Los dos últimos resultarán familiares a los usuarios de utilidades Unix (como \emph{grep}) que utilizan expresiones regulares, así como a los usuarios de vi.
Veamos algunos ejemplos. Digamos que escribes esta línea y, antes de pulsar ENTER, decides que quieres cambiarla:
\begin{lstlisting}[language=bash]
$ fgrep -l Bob < ~pete/wk/names
\end{lstlisting}
Como se muestra, su cursor está más allá del último carácter de la línea. Primero, teclea ESC para entrar en modo de control; tu cursor retrocede un espacio de modo que se encuentra en la \texttt{s}. Luego, si tecleas h, tu cursor retrocede a la \texttt{e}. Si tecleas \texttt{3h} desde la \texttt{e}, terminas en la \emph{n}.
Ahora veremos la diferencia entre los dos conceptos de <<palabra>>. Vuelve al final de la línea tecleando \$. Si tecleas b, la palabra en cuestión es <<names>>, y el cursor acaba en la \texttt{n}:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pete/wk/(*\highlight{n}*)ames
\end{lstlisting}
Si vuelves a teclear \texttt{b}, la siguiente palabra es la barra (es una <<secuencia>> de caracteres no alfanuméricos), por lo que el cursor acaba sobre ella:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pete/wk(*\highlight{/}*)names
\end{lstlisting}
Sin embargo, si tecleas \texttt{B} en lugar de \texttt{b}, la palabra no en blanco será el nombre completo de la ruta, y el cursor terminará al principio de la misma, es decir, sobre la tilde:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < (*\highlight{\~{}}*)pete/wk/names
\end{lstlisting}
Habría tenido que escribir \texttt{b} cuatro veces -- o simplemente \texttt{4b} -- para obtener el mismo efecto, ya que hay cuatro <<palabras>> en la parte de la ruta a la izquierda de \emph{/names: wk, slash, pete} y la tilde inicial.
En este punto, \texttt{w} y \texttt{W} hacen lo contrario: teclear \texttt{w} te lleva sobre la \texttt{p}, ya que la tilde es una <<palabra>>, mientras que teclear \texttt{W} te lleva al final de la línea. Pero mientras que \texttt{w} y \texttt{W} te llevan al principio de la siguiente palabra, \texttt{e} y \texttt{E} te llevan al final de la palabra actual. Así, si tecleas \texttt{w} con el cursor sobre la tilde, llegas a:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~(*\highlight{p}*)ete/wk/names
\end{lstlisting}
Entonces tecleando \emph{e} te lleva a:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pet(*\highlight{e}*)/wk/names
\end{lstlisting}
Y escribiendo una \emph{w} adicional te lleva a:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pete(*\highlight{/}*)wk/names
\end{lstlisting}
Por otro lado, \texttt{E} te lleva al final de la palabra actual no en blanco -- en este caso, el final de la línea. (Aunque a primera vista los comandos pueden parecer no nemotécnicos, generalmente hay cierto orden en la elección de las letras de los comandos. Cada letra de comando suele ser la primera letra de la palabra inglesa correspondiente a la operación. Las minúsculas sirven para las palabras, mientras que las mayúsculas sirven para las palabras que no están en blanco. Entender esto es sin duda más difícil si el inglés no es tu lengua materna, pero eso también se aplica a los comandos del modo emacs).
\subsection{Introducción y cambio de texto}
Ahora que ya sabes cómo entrar en el modo de control y moverte por la línea de comandos, necesitas saber cómo volver al modo de entrada para poder hacer cambios y escribir comandos adicionales. Una serie de comandos le llevan del modo de control al modo de entrada; se enumeran en la Tabla \ref{tab2.8}. Todos ellos entran en el modo de entrada de forma un poco diferente. Todos ellos entran en el modo de entrada de forma un poco diferente.
\begin{table}[h]
\center
\caption{Comandos para entrar en el modo de entrada de vi}
\label{tab2.8}
\begin{tabular}{m{2cm}|m{10cm}}
\hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
i & Texto insertado antes del carácter actual (insertar) \\
a & Texto insertado después del carácter actual (append) \\
I & Texto insertado al principio de la línea \\
A & Texto insertado al final de la línea \\
r & Sustituir un carácter (no entra en modo de entrada) \\
R & El texto sobrescribe el texto existente (reemplazar) \\
\end{tabular}
\end{table}
Lo más probable es que utilices siempre \texttt{i} o \texttt{a}, y puede que ocasionalmente utilices \texttt{R}. \texttt{I} y \texttt{A} son abreviaturas de \texttt{0i} y \texttt{\$a} respectivamente. Para ilustrar la diferencia entre \texttt{i}, \texttt{a} y \texttt{R}, digamos que empezamos con nuestra línea de ejemplo:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pete/wk(*\highlight{/}*)names
\end{lstlisting}
Si escribes \texttt{i} seguido de end, obtienes:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pete/wkend(*\highlight{/}*)names
\end{lstlisting}
Es decir, el cursor siempre aparece sobre \texttt{/} antes de \emph{names}. Pero si escribes \texttt{a} en lugar de \texttt{i}, verás que el cursor se mueve un espacio a la derecha. Entonces, si escribes \texttt{nick}, obtendrás
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pete/wk/nick(*\highlight{n}*)ames
\end{lstlisting}
Es decir, el cursor está siempre justo después del último carácter que has tecleado, hasta que tecleas ESC para finalizar tu entrada. Por último, si vuelve a la \texttt{n} en \emph{names}, escriba \texttt{R} en su lugar, y luego escriba \texttt{task}, verá:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pete/wk/task(*\highlight{s}*)
\end{lstlisting}
En otras palabras, usted estará \emph{reemplazando} (de ahí \texttt{R}) en lugar de insertar texto.
¿Por qué \texttt{R} mayúscula en lugar de \texttt{r} minúscula? Este último es un comando ligeramente diferente, que sustituye sólo un carácter y no entra en modo de entrada. Con \texttt{r}, el siguiente carácter sobrescribe el carácter situado bajo el cursor. Así que si empezamos con la línea de comandos original y tecleamos \texttt{r} seguido de un punto y coma, obtendremos:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pete/wk(*\highlight{;}*)names
\end{lstlisting}
Si precedes \texttt{r} con un número \emph{N}, te permite reemplazar los siguientes \emph{N} caracteres existentes en la línea -- pero aún no entras en modo de entrada. El modo Vi sustituye los \texttt{N} caracteres de la línea por \texttt{N} copias del carácter que escriba después de la \texttt{r}. La \texttt{r} minúscula es eficaz para corregir letras de opción erróneas, caracteres de redirección de E/S, puntuación, etc.
\subsection{Comandos de eliminación}
Ahora que sabes cómo introducir comandos y moverte por la línea, necesitas saber cómo borrar. El comando básico de borrado en modo vi es \texttt{d} seguido de otra letra. Esta letra determina cuál es la unidad y la dirección de borrado, y corresponde a un comando de movimiento, como los listados previamente en la Tabla \ref{Tab:2-7}. La Tabla \ref{tab2.9} muestra algunos ejemplos de uso común.
\begin{table}[h]
\center
\caption{Algunos comandos de borrado en modo vi}
\label{tab2.9}
\begin{tabular}{m{3cm}|m{10cm}} \hline
\textbf{Commando} & \textbf{Descripción} \\ \hline
dh & Borrar un carácter hacia atrás. \\
dl & Borrar un carácter hacia delante. \\
db & Borrar una palabra hacia atrás. \\
dw & Borrar una palabra hacia delante. \\
dB & Borrar una palabra no en blanco hacia atrás. \\
dW & Borrar una palabra no en blanco hacia delante. \\
d\$ & Borrar hasta el final de línea. \\
d0 & Borrar hasta el principio de línea. \\
\end{tabular}
\end{table}
Estos comandos tienen algunas variaciones y abreviaturas. Si utiliza una \texttt{c} en lugar de \texttt{d}, entrará en el modo de entrada después de que se realice el borrado. Puede proporcionar un número de repetición antes o después de la \texttt{d} (o \texttt{c}). La Tabla \ref{tab2.10} lista las abreviaturas disponibles.
La mayoría de la gente tiende a usar \texttt{D} para borrar hasta el final de la línea, \texttt{dd} para borrar una línea entera, y \texttt{x} (como <<retroceso>>) para borrar caracteres individuales. Si no eres un usuario empedernido de vi, puede que te resulte difícil dominar algunos de los comandos de borrado más esotéricos bajo tus dedos.
\newpage
\begin{table}[h]
\center
\caption{Abreviaturas de los comandos de borrado en modo vi}
\label{tab2.10}
\begin{tabular}{|m{2cm}|m{13cm}|} \hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
D & Equivale a d\$ (borrar hasta el final de la línea) \\\hline
dd & Equivale a 0d\$ (borrar toda la línea) \\\hline
C & Equivale a c\$ (borrar hasta el final de la línea, entrar en modo de entrada) \\\hline
cc & Equivale a 0c\$ (borrar toda la línea, entrar en modo de entrada) \\\hline
s & Equivale a xi (borrar el carácter actual, entrar en modo de entrada) \\\hline
S & Equivale a cc (borrar toda la línea, entrar en modo de entrada) \\\hline
x & Equivale a dl (borrar carácter hacia adelante) \\\hline
X & Equivale a dh (borrar carácter hacia atrás) \\\hline
\end{tabular}
\end{table}
Todo buen editor proporciona comandos <<undelete>> así como comandos de borrado, y vi-mode no es una excepción. Vi-mode mantiene un \emph{buffer de borrado} que almacena todas las modificaciones al texto en la línea actual solamente (note que esto es diferente del editor vi completo). El comando \texttt{u} deshace sólo el último comando de modificación de texto, mientras que \texttt{U} deshace todos los comandos de este tipo en la línea actual. Así que si haces un cambio y quieres deshacerlo, teclea \texttt{u}; si haces muchos cambios y encuentras que el original se acerca más a lo que quieres, puedes deshacerlo todo tecleando \texttt{U}. Un comando relacionado es . (punto), que rehace el último comando de modificación de texto.
También hay una forma de guardar texto en el búfer de borrado sin haberlo borrado en primer lugar: simplemente teclea un comando de borrado pero utiliza \texttt{y} (<<yank>>) en lugar de d. Esto no modifica nada, pero te permite recuperar el texto yankado tantas veces como quieras más adelante. El comando para recuperar el texto arrancado es \texttt{p}, que inserta (<<pone>>) el texto en la línea actual a la derecha del cursor. La versión en mayúsculas, P, coloca el texto a la izquierda del cursor. Los distintos comandos de cortar y pegar se resumen en la Tabla \ref{tab2.11}.
\begin{table}[h]
\center
\caption{Comandos para cortar y pegar en modo Vi}
\label{tab2.11}
\begin{tabular}{|m{2cm}|m{13cm}|} \hline
\textbf{Texto} & \textbf{Descripción} \\ \hline
y & Corta (guarda) texto, no cambia realmente la línea. \\\hline
p & Pone el último texto arrancado o borrado en la línea después del cursor. \\\hline
P & Coloca el último texto arrancado o borrado en la línea anterior al cursor. \\\hline
u & Deshace el cambio más reciente. \\\hline
U & Deshacer todos los cambios de la línea. \\\hline
. (punto) & Rehacer el último cambio en la posición actual del cursor. \\\hline
\end{tabular}
\end{table}
Los comandos \texttt{d} y \texttt{p} son bastante útiles juntos para reorganizar el orden de las opciones o argumentos en una línea de comandos. Por ejemplo, la mayoría de los compiladores C de Unix aceptan una opción \emph{-l} que indica el nombre de una biblioteca a utilizar al enlazar un programa compilado. La opción \emph{-L} especifica un directorio en el que el compilador debe buscar las bibliotecas, además de buscar en los lugares estándar para las bibliotecas del sistema.
\begin{lstlisting}[language=bash]
cc -o myprog myprog.c -Lmylibdir -lmylib
\end{lstlisting}
Este comando busca el archivo de biblioteca \emph{libmylib.a} en el directorio \emph{mylibdir} al compilar y enlazar \emph{myprog.c}. Hasta aquí todo bien. El problema es que normalmente la opción \emph{-L} debe aparecer en la línea de comandos antes de la opción \emph{-l}. Supongamos que accidentalmente las has escrito al revés y, por tanto, la compilación ha fallado. Puedes utilizar los comandos \texttt{d} y \texttt{p} para reordenar las cosas. Empieza por recuperar la línea
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (*\highlight{c}*)c -o myprog myprog.c -lmylib -Lmylibdir
\end{lstlisting}
A continuación, desplázate hasta la última opción con \texttt{5w}. A continuación, retroceda hasta el espacio anterior con \texttt{h}. Su línea de comandos tiene ahora este aspecto:
\begin{lstlisting}[language=bash]
$ cc -o myprog myprog.c -lmylib-Lmylibdir
\end{lstlisting}
Escribe \texttt{D} para borrar el resto de la línea:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ cc -o myprog myprog.c -lmyli(*\highlight{b}*)
\end{lstlisting}
Ahora retrocede hasta el carácter \texttt{c} anterior con \texttt{Bhh}:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ cc -o myprog myprog.(*\highlight{c}*) -lmylib
\end{lstlisting}
Por último, utilice \texttt{p} para insertar la opción desplazada:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ cc -o myprog myprog.c -Lmylibdi(*\highlight{r}*) -lmylib
\end{lstlisting}
Luego pulsa ENTER y ya está. Esto parece mucho tecleo. Pero, como veremos pronto, hay comandos adicionales que te permiten buscar caracteres en la línea de comandos, haciendo mucho más fácil moverse por ella. Y si eres un usuario experimentado de \emph{vi}, estarás como en casa.
\newpage
\subsection{Desplazarse por el archivo histórico}
El siguiente grupo de comandos del modo de control de vi que cubrimos te permite moverte y buscar en tu archivo de historial. Esta es la funcionalidad más importante que te permite volver atrás y corregir un comando erróneo sin tener que volver a escribir toda la línea. Estos comandos se resumen en la Tabla \ref{tab2.12}.
\begin{table}[h]
\center
\caption{Comandos del modo de control Vi para buscar en el fichero histórico}
\label{tab2.12}
\begin{tabular}{|m{2cm}|m{13cm}|}
\hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
k o - & Retroceder una línea. \\\hline
j o + & Avanzar una línea. \\\hline
G & Desplazarse a la línea indicada por la cuenta de repeticiones, o a la primera línea del historial si no hay cuenta de repeticiones. \\\hline
?cadena & Buscar cadena hacia atrás. \\\hline
/cadena & Buscar cadena hacia delante. \\\hline
n & Repite la búsqueda en la misma dirección que la anterior. \\\hline
N & Repite la búsqueda en la dirección opuesta a la anterior. \\\hline
\end{tabular}
\end{table}
Los tres primeros pueden ir precedidos de cuentas de repetición (por ejemplo, 3k o 3- retrocede tres líneas en el archivo histórico).
Si no estás familiarizado con \emph{vi} y su historia cultural, puede que te estés preguntando la sabiduría de elegir mnemónicos tan aparentemente pobres como \texttt{h, j, k,} y \texttt{l} para carácter de retroceso, línea de avance, línea de retroceso y carácter de avance, respectivamente. Bueno, en realidad hay una razón para las opciones - aparte de que están todos juntos en el teclado estándar.
Bill Joy desarrolló originalmente \emph{vi} para ejecutarse en terminales Lear-Siegler ADM-3a, que fueron los primeros modelos populares con cursores direccionables (lo que significa que un programa podía enviar un comando a un ADM-3a para hacer que moviera el cursor a un lugar específico de la pantalla). Las teclas \texttt{h, j, k} y \texttt{l} de la ADM-3a tenían pequeñas flechas, así que Joy decidió usar esas teclas para los comandos apropiados en \emph{vi}.
Otra razón (parcial) para la elección de los comandos es que CTRL-H es la tecla tradicional de retroceso, y CTRL-J denota salto de línea. La razón principal para estas elecciones, sin embargo, es que con estas teclas, nunca es necesario mover las manos de la <<fila de inicio>> del teclado.
Puede que + y - sean mejores mnemotécnicos que \texttt{j} y \texttt{k}, pero estos últimos tienen la ventaja de ser más accesibles para los mecanógrafos táctiles. En cualquier caso, estos comandos son los más básicos para moverse por el historial. Para ver cómo funcionan, tomemos los mismos ejemplos que usamos al hablar del modo emacs.
Introduce el comando de ejemplo (ENTER funciona tanto en modo de entrada como de control, al igual que newline o CTRL-J):
\begin{lstlisting}[language=bash]
$ fgrep -l Bob < ~pete/wk/names
\end{lstlisting}
Pero recibe un mensaje de error diciendo que la letra de su opción era incorrecta. Quieres cambiarla a \emph{-s} sin tener que volver a escribir todo el comando. Asumiendo que estás en modo control (puede que tengas que teclear ESC para ponerte en modo control), tecleas \texttt{k} o \texttt{-} para recuperar el comando. Tu cursor estará al principio de la línea:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (*\highlight{f}*)grep -l Bob < ~pete/wk/names
\end{lstlisting}
Escribe \texttt{w} para llegar al \texttt{-}, luego \emph{l} o espacio para llegar a la \texttt{l}. Ahora puedes reemplazarlo escribiendo \texttt{rs}; presiona ENTER para ejecutar el comando.
Ahora digamos que recibes otro mensaje de error, y finalmente decides mirar la página del manual del comando \texttt{fgrep}. Recuerdas haber hecho esto hoy hace un rato, así que en lugar de teclear el comando \emph{man(1)} entero, buscas el último que usaste. Para ello, teclea ESC para entrar en modo control (si ya estás en modo control, esto no tiene ningún efecto), luego teclea / seguido de \emph{man} o \emph{ma}. Para estar seguro, también puede escribir \texttt{\^{}ma}; el \^{} significa que sólo se buscarán las líneas que empiecen por \texttt{ma}.\footnote{Los aficionados a \emph{vi} y a las utilidades de búsqueda como \emph{grep} deben tener en cuenta que el signo de intercalación (\^{}) para el inicio de línea es el único operador contextual que el modo \emph{vi} proporciona para las cadenas de búsqueda.}
Pero teclear \texttt{/\^{}ma} no te da lo que quieres; en su lugar, el shell te da:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (*\highlight{m}*)ake myprogram
\end{lstlisting}
Para buscar <<man>> de nuevo, puede escribir \texttt{n}, que realiza otra búsqueda hacia atrás utilizando la última cadena de búsqueda. Escribiendo / de nuevo sin un argumento y pulsando ENTER se consigue lo mismo.
El comando \texttt{G} recupera el comando cuyo número es el mismo que el prefijo numérico del argumento que proporcione. \texttt{G} depende del esquema de numeración de comandos descrito en la Sección 3.4.2.3, en el \hyperref[sec:Chapter3]{Capítulo 3}. Sin un argumento de prefijo, va al comando número 1. Esto puede ser útil para los antiguos usuarios del intérprete de comandos C que todavía quieran utilizar números de comando.
\subsection{Comandos de Búsqueda de Caracteres}
Hay algunos comandos de movimiento adicionales en el modo vi. Estos comandos le permiten moverse a la posición de un carácter particular en la línea. Se resumen en la Tabla \ref{tab2.13}, en la que x denota cualquier carácter.
Todos estos comandos pueden ir precedidos de una cuenta de repetición.
\begin{table}[h]
\center
\caption{Comandos de búsqueda de caracteres en modo Vi}
\label{tab2.13}
\begin{tabular}{|m{2cm}|m{13cm}|} \hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
fx & Mover a la derecha a la siguiente ocurrencia de x (buscar). \\\hline
Fx & Mover a la izquierda a la ocurrencia anterior de x (encontrar hacia atrás). \\\hline
tx & Mover a la derecha a la siguiente ocurrencia de x, luego hacia atrás una posición (ir al carácter). \\\hline
Tx & Mover a la izquierda a la ocurrencia anterior de x, luego hacia adelante una posición (ir hacia atrás al carácter). \\\hline
; & Rehacer el último comando de búsqueda de caracteres. \\\hline
, & Rehacer el último comando de búsqueda de caracteres en dirección opuesta. \\\hline
\% & Moverse a la coincidencia (, ), \{, \}, [, o ]. \\\hline
\end{tabular}
\end{table}
Empezando por el ejemplo anterior: supongamos que quieres cambiar \emph{Bob} por \emph{Rob}. Asegúrate de que estás al final de la línea (o, en cualquier caso, a la derecha de la \texttt{B} de \emph{Bob}); entonces, si escribes \texttt{FB}, el cursor se desplaza a la \texttt{B}:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l (*\highlight{B}*)ob < ~pete/wk/names
\end{lstlisting}
En este punto, podrías escribir \texttt{rR} para reemplazar la \texttt{B} por \texttt{R}. Pero digamos que quieres cambiar \emph{Bob} por \emph{Blob}. Necesitarías moverte un espacio a la derecha de la \texttt{B}. Por supuesto, podrías simplemente teclear \texttt{l}. Pero, dado que estás en algún lugar a la derecha de \emph{Bob}, la forma más rápida de moverte a la o sería teclear \texttt{TB} en lugar de \texttt{FB} seguido de \emph{l}.
Como ejemplo de cómo se puede usar la cuenta de repetición con comandos de búsqueda de caracteres, digamos que quieres cambiar el nombre del archivo de \emph{names} a \emph{namfile}. En este caso, asumiendo que tu cursor está todavía en la \texttt{B}, necesitas llegar a la tercera \texttt{e} a la derecha, así que puedes teclear \texttt{3te}, seguido de l para poner el cursor de nuevo en la \texttt{e} de \emph{names}.
Los comandos de búsqueda de caracteres también tienen comandos de borrado asociados. Lee las definiciones de los comandos de la tabla anterior y sustituye mentalmente <<borrar>> por <<mover>>. Obtendrá lo que ocurre cuando precede al comando de búsqueda de caracteres dado con una d. El borrado incluye el carácter dado como argumento. Por ejemplo, suponga que su cursor está bajo la n en \emph{names}:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ fgrep -l Bob < ~pete/wk/(*\highlight{n}*)ames
\end{lstlisting}
Si desea cambiar \emph{names} por \emph{aides}, una posibilidad es escribir \texttt{dfm}. Esto significa <<borrar hasta la siguiente aparición de m>>, es decir, borrar <<nam>>. A continuación, puede escribir \texttt{i} (para entrar en el modo de entrada) y luego <<aid>> para completar el cambio.
Una forma mejor, sin embargo, es utilizar \texttt{cfm}. Esto significa <<cambiar todo desde debajo del cursor hasta la siguiente aparición de m inclusive>>. Esto elimina <<nam>> y entra en el modo de entrada.
El comando \% es muy útil para encontrar el carácter <<pair>> coincidente cuando se utiliza con paréntesis, corchetes y llaves. Todos ellos aparecen con frecuencia en pares coincidentes en las líneas de comandos del shell.
Un último comando completa los comandos del modo de control de \emph{vi} para desplazarse por la línea actual: puede utilizar el carácter de tubería (|) para desplazarse a una columna específica, cuyo número viene dado por un argumento de prefijo numérico. El recuento de columnas comienza en 1; cuente sólo su entrada, no el espacio ocupado por la cadena de texto. La cuenta de repetición por defecto es 1, por supuesto, lo que significa que teclear | por sí mismo equivale a 0 (ver Tabla \ref{Tab:2-7}).
\subsection{Nombre de Archivo y Finalización y Expansión de Variables}
El modo vi proporciona una característica adicional que creemos que utilizarás con bastante frecuencia: el completado de names de archivo. Esta característica no forma parte del editor \emph{vi} real, y sin duda se inspiró en características similares de Emacs y, originalmente, en el sistema operativo TOPS-20 para mainframes DEC.
La lógica detrás del completado de names de fichero es simple: debería tener que teclear sólo lo necesario de un nombre de fichero para distinguirlo de otros names de fichero en el mismo directorio. La barra invertida (\textbackslash{}) es el comando que indica al shell Korn que complete el nombre de fichero en modo vi. Si teclea una palabra, teclea ESC para entrar en modo de control, y luego teclea \textbackslash{}, una de cuatro cosas sucede; son las mismas que para TAB (o ESC ESC) en modo emacs:
\begin{enumerate}
\item{Si no hay ningún fichero cuyo nombre empiece por la palabra, el intérprete de comandos emite un pitido y no ocurre nada más.}
\item{Si hay exactamente una forma de completar el nombre del fichero, y el fichero es un fichero normal, el shell teclea el resto del nombre del fichero, seguido de un espacio por si quieres teclear más argumentos de comando.}
\item{Si hay exactamente una forma de completar el nombre del archivo, y el archivo es un directorio, el shell completa el nombre del archivo, seguido de una barra.}
\item{Si hay más de una forma de completar el nombre de archivo, el shell lo completa con el prefijo común más largo entre las opciones disponibles.}
\end{enumerate}
Como en el modo emacs, a partir de \emph{ksh93h}, puede usar TAB en lugar de ESC \textbackslash{}. Sin embargo, esto sólo funciona si utiliza \emph{set -o viraw} además de \emph{set -o vi}. (La opción \emph{viraw} consume un poco más de CPU -- aunque probablemente no de forma notable -- y es necesaria en algunos sistemas Unix antiguos para que el modo vi funcione). Afortunadamente, a partir de \emph{ksh93n}, la opción \emph{viraw} se activa automáticamente cuando se utiliza el modo vi.
Un comando relacionado es *, que es el mismo que ESC * en modo emacs como se describió anteriormente en este capítulo \footnote{Si contamos la ESC necesaria para salir del modo de entrada, el comando vi-mode es idéntico a emacs-mode.}. Se comporta de forma similar a ESC \textbackslash{}, pero si hay más de una posibilidad de finalización (número cuatro en la lista anterior), enumera todas ellas y le permite seguir escribiendo. Por lo tanto, se asemeja al carácter comodín del shell *.
Por último, el comando = realiza el mismo tipo de expansión de nombre de archivo que el comodín de shell *, pero de forma diferente. En lugar de expandir los names de archivo en la línea de comandos, los imprime en una lista numerada con un nombre de archivo en cada línea. Luego te devuelve el prompt del shell y vuelve a escribir lo que había en tu línea de comandos antes de que teclearas =. Por ejemplo, si los archivos de tu directorio incluyen \emph{program.c} y \emph{problem.c}, y tecleas pro seguido de ESC y luego =, verás esto:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ cc pro # ESC = escrito en este punto
1) problema.c
2) programa.c
$ cc pr(*\highlight{o}*)
\end{lstlisting}
A partir de \emph{ksh93m}, prefijar el comando = con un contador indica la selección de una opción en particular. Volviendo al ejemplo anterior: después de listar tanto \emph{problem.c} como \emph{program.c}, la línea de comandos queda así:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ cc pr(*\highlight{o}*)
\end{lstlisting}
Si desea \emph{program.c}, basta con escribir 2 =, y el intérprete de comandos elige la expansión número 2. La línea de comandos cambia a:
\begin{lstlisting}[language=bash]
$ cc programa.c
\end{lstlisting}
Como en el modo emacs, también puedes completar comandos desde el modo vi. Los comandos *, \textbackslash{} y =, cuando se usan en la primera palabra de la línea de comandos, expanden alias, funciones y comandos. También como en el modo emacs, a partir de \emph{ksh93l}, estas expansiones funcionan cuando has abierto una cadena entrecomillada pero aún no la has cerrado, y para expansiones de variables con \texttt{\$} y \texttt{''\$}.
\subsection{Comandos Misceláneos}
Varios comandos misceláneos completan el modo vi; algunos de ellos son bastante esotéricos. Se enumeran en la Tabla \ref{tab2.14}.
\begin{table}[h]
\center
\caption{Comandos varios del modo vi}
\label{tab2.14}
\begin{tabular}{|m{2cm}|m{12cm}|} \hline
\textbf{Comando} & \textbf{Descripción} \\ \hline
\~{} & Invertir (<<twiddle>>) el caso del carácter(es) actual(es). \\\hline
\_ & Añade la última palabra del comando anterior; entra en modo de entrada. Una cuenta repetida añade la enésima palabra dada, empezando desde el principio del comando. \\\hline
v & Ejecuta el comando hist en la línea actual (en realidad, ejecuta el comando \texttt{hist -e \$\{VISUAL:-\$\{EDITOR:-vi\}\}}); normalmente esto significa ejecutar el vi completo en la línea actual. \\\hline
CTRL-L & Inicia una nueva línea y vuelve a dibujar la línea actual en ella; bueno para cuando tu pantalla se vuelve confusa. \\\hline
CTRL-V & Imprime la versión del shell Korn. \\\hline
\# & Antepone \# (carácter de comentario) a la línea y la envía al fichero de historial;\tablefootnote{La línea también es <<ejecutada>> por el shell. Sin embargo, \# es el carácter de comentario del shell, por lo que ésta lo ignora.} útil para guardar un comando y ejecutarlo más tarde sin tener que volver a escribirlo. Si la línea ya empieza con \#, elimina el \# inicial y cualquier otro carácter de comentario que siga a nuevas líneas en un comando multilínea. \\\hline
@x & Inserta la expansión del alias \_x como entrada en modo comando (ver texto). \\\hline
\end{tabular}
\end{table}
El primero de ellos puede ir precedido de una cuenta de repetición. Una cuenta de repetición de \emph{n} precediendo al \~{} cambia el caso de los \emph{n} caracteres siguientes. \footnote{Esto, en nuestra opinión, es un defecto de diseño en el editor vi que los autores del shell Korn podrían haber corregido. Permitir al usuario añadir un comando de movimiento a ~ y hacer que se comporte de forma análoga a d o y habría sido mucho más útil; de esa forma, se podría cambiar de mayúsculas a minúsculas una palabra con sólo dos pulsaciones de tecla.} El cursor avanza en consecuencia.
Un conteo de repeticiones que precede a \_ hace que la \emph{n} -ésima palabra del comando anterior se inserte en la línea actual; sin el conteo, se usa la última palabra. Omitir el recuento de repeticiones es útil porque un nombre de archivo suele ser lo último en una línea de comandos de Unix y porque los usuarios suelen ejecutar varios comandos seguidos en el mismo archivo. Con esta función, puede escribir todos los comandos (excepto el primero) seguidos de ESC \_ y el shell inserta el nombre del archivo.
\subsection{Expansión de Macros con Alias}
Tal como se describió anteriormente para el modo emacs, puede utilizar la función de alias del shell (descrita en el siguiente capítulo) para crear \emph{macros}, es decir, abreviaturas de una sola letra para secuencias más largas de comandos. Si crea un alias llamado \_x, donde x es una letra, entonces cuando escriba @ x, el modo vi expande el alias y lo lee como entrada del modo comando.
Como antes, suponga que quiere que un comando ponga en mayúscula la primera letra de la palabra actual. Podría definir un alias como sigue:
\begin{lstlisting}[language=bash]
alias _C='B~'
\end{lstlisting}
Ahora, si escribe ESC @ C, el cursor se mueve al principio de la palabra actual (B), y luego pone en mayúscula la letra actual (\~{}).
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ print here is a word # Escribe ESC @ C
$ print here is a w(*\highlight{o}*)rd
\end{lstlisting}
\section{El comando histórico}
\emph{hist} es un comando incorporado en el shell \footnote{En ksh88, este comando se llama \emph{fc}, por <<fix command>>. \emph{ksh93} proporciona un alias incorporado para \emph{fc} a \emph{hist}, para aquellos que están acostumbrados a usar el comando \emph{fc}. Las versiones recientes también tienen \emph{fc} como una orden incorporada que se comporta de forma idéntica a \emph{hist}; esto se debe a que POSIX requiere que esta orden esté incorporada.} que proporciona un superconjunto del mecanismo de historial del shell C. Puedes usarlo para examinar los comandos más recientes que has introducido, para editar uno o más comandos con tu editor <<real>> favorito, y para ejecutar comandos antiguos con cambios sin tener que escribir el comando entero de nuevo. Veremos cada uno de estos usos.
La opción \emph{-l} para \emph{hist} lista los comandos anteriores. Toma argumentos que se refieren a órdenes en el fichero histórico. Los argumentos pueden ser números o cadenas alfanuméricas; los números se refieren a las órdenes del fichero histórico, mientras que las cadenas se refieren a la orden más reciente que empiece por la cadena. \emph{hist} trata los argumentos de una forma bastante compleja:
\begin{itemize}
\item{Si das dos argumentos, sirven como la primera y la última orden a mostrar.}
\item{Si especifica un argumento numérico, sólo se muestra la orden con ese número.}
\item{Con un único argumento de cadena, \emph{hist} busca el comando más reciente que empiece por esa cadena y le muestra todo desde ese comando hasta el comando más reciente.}
\item{Si no especifica ningún argumento, verá los últimos 16 comandos introducidos. Así, \texttt{hist -l} por sí mismo es equivalente al comando \emph{history} del shell C, y de hecho el shell Korn define un alias incorporado history como:
\begin{lstlisting}[language=bash]
alias history='hist -l'
\end{lstlisting}
Como verá en el \hyperref[sec:Chapter3]{Capítulo 3}, esto significa que puede escribir history y el shell Korn ejecutará el comando \texttt{hist -l.}
}
\end{itemize}
Algunos ejemplos le aclararán estas opciones. Supongamos que te conectas e introduces estos comandos:
\begin{lstlisting}[language=bash]
ls -l
more myfile
vi myfile
wc -l myfile
pr myfile | lp -h
\end{lstlisting}
Si escribe \texttt{hist -l} (o \texttt{history}) sin argumentos, verá la lista anterior con números de comando, como en:
\begin{lstlisting}[language=bash]
1 ls -l
2 more myfile
3 vi myfile
4 wc -l myfile
5 pr mifichero | lp -h
\end{lstlisting}
La opción -n suprime los números de línea. Si quiere ver sólo los comandos 2 a 4, escriba \texttt{hist -l 2 4}. Si quiere ver sólo el comando \emph{vi}, escriba \texttt{hist -l 3}. Para ver todo desde el comando \\emph{vi} hasta el presente, escriba \texttt{hist -l v}. Finalmente, si desea ver los comandos entre \emph{more} y \emph{wc}, puede escribir \texttt{hist -l m w}, \texttt{hist -l m 4}, \texttt{hist -l 2 4}, etc.
Los números de historial negativos indican valores relativos al número de comando actual. Por ejemplo, \texttt{hist -l -3} muestra el tercer comando anterior. Una forma menos confusa de hacer esto es con la opción \emph{-N: hist -l -N 3} hace lo mismo. Esto también tiene la ventaja de ajustarse a las convenciones POSIX para opciones y argumentos.
La opción \emph{-l} de \emph{hist} no es particularmente útil, excepto como una forma rápida de recordar qué comandos ha escrito recientemente. Utilice el alias \texttt{history} si es un usuario experimentado del shell C.
La otra opción importante de \emph{hist} es \emph{-e} para <<editar>>. Es útil como <<escotilla de escape>> de los modos vi- y emacs si no estás acostumbrado a ninguno de esos editores. Puede especificar la ruta de su editor favorito y editar comandos desde su archivo de historial; entonces, cuando haya hecho los cambios, el shell ejecutará las nuevas líneas.
Digamos que tu editor favorito es una pequeña joya casera llamada \emph{zed}. Podrías editar tus comandos escribiendo:
\begin{lstlisting}[language=bash]
$ hist -e /usr/local/bin/zed
\end{lstlisting}
se obtiene \emph{zed} al invocar \emph{hist}. HISTEDIT utiliza por defecto el antiguo editor de líneas ed, de modo que el valor general por defecto es también ed.\footnote{El valor por defecto es en realidad un poco complicado en \emph{ksh93}. \texttt{hist -e} ejecuta \texttt{\$\{HISTEDIT:-\$FCEDIT\}} para editar la línea de comandos. Esto preserva la compatibilidad con \emph{ksh88}, donde la variable para el comando \emph{fc} era, no sorprendentemente, \texttt{FCEDIT}. Si no se establece ninguna de las dos variables, se obtiene \emph{/bin/ed}. (La construcción \texttt{\$\{HISTEDIT:-\$FCEDIT\}} se explica en el \hyperref[sec:Chapter4]{Capítulo 4}.) El resultado es utilizar el editor especificado por la variable \texttt{HISTEDIT} si está establecida; de lo contrario, utilice el valor de la variable \texttt{FCEDIT}).}
\emph{hist} se utiliza normalmente para arreglar un comando reciente. Por lo tanto, maneja los argumentos de manera un poco diferente a como lo hace para la variación \texttt{hist -l} anterior:
\begin{itemize}
\item{Sin argumentos, \emph{hist} carga el editor con el comando más reciente.}
\item{Con un argumento numérico, \emph{hist} carga el editor con el comando con ese número.}
\item{Con un argumento de cadena, \emph{hist} carga el comando más reciente empezando por esa cadena.}
\item{Con dos argumentos a \emph{hist}, los argumentos especifican el principio y el final de un rango de comandos, como arriba.}
\end{itemize}
Recuerde que \emph{hist} ejecuta los comandos después de que usted los edite. Por lo tanto, la última opción puede ser peligrosa. El shell Korn intenta ejecutar todos los comandos en el rango que especifique cuando salga de su editor. Si ha escrito alguna construcción multilínea (como las que veremos en el \hyperref[sec:Chapter5]{Capítulo 5}), los resultados pueden ser incluso más peligrosos. Aunque estas pueden parecer formas válidas de generar <<programas de shell instantáneos>>, una estrategia mucho mejor sería dirigir la salida de \texttt{hist -nl} con los mismos argumentos a un archivo; luego edite ese archivo y ejecute los comandos cuando esté satisfecho con ellos:
\begin{lstlisting}[language=bash]
$ hist -nl cp > lastcommands # Listar todos los comandos que empiezan por cp en lastcommands
$ vi lastcommands # Editar lastcommands
$ . lastcommands # Ejecuta los comandos que contiene
\end{lstlisting}
En este caso, ¡el shell no intentará ejecutar el fichero cuando abandones el editor!
Hay un último uso para \emph{hist}. Si especifica la opción \emph{-s} (es decir, \emph{type hist -s}), el shell Korn se saltará la parte de edición y sólo ejecutará el/los comando(s) especificado(s) por el/los argumento(s). ¿Por qué es útil? Por un lado, simplemente tecleando hist \emph{-s} hace que el comando anterior se repita, igual que el comando !! del shell C. El shell Korn proporciona el alias incorporado \emph{r} para esto, de modo que si escribe \texttt{r} y pulsa ENTER, repetirá el último comando.
Esta forma de \emph{hist} permite aún otro tipo de argumento, de la forma \emph{old=new}, que significa <<cambia las ocurrencias de \emph{old} en el comando anterior especificado a \emph{new} y luego ejecútalo>>. (Desafortunadamente, no puede hacer que el shell Korn haga este tipo de sustitución más de una vez; sólo cambia la primera ocurrencia de \emph{old} a \emph{new}). Por ejemplo, suponga que está usando troff y sus preprocesadores para trabajar en un documento.\footnote{Si es así, ¡eres de una raza rara!} Si accidentalmente ejecuta el preprocesador tbl con este comando:
\begin{lstlisting}[language=bash]
tbl ch2.tr | troff -ms -Tps > ch2.ps
\end{lstlisting}
pero necesitabas ejecutar \emph{eqn}, puedes volver a hacerlo escribiendo \texttt{hist -s tbl=eqn}. (También podrías usar el alias, \texttt{r tbl=eqn}.) Este comando se ejecutaría entonces:
\begin{lstlisting}[language=bash]
eqn ch2.tr | troff -ms -Tps > ch2.ps
\end{lstlisting}
El shell Korn imprime el comando modificado antes de ejecutarlo.
\section{Hábitos de los Dedos}
Parafraseando el viejo adagio, los viejos hábitos de los dedos son difíciles de erradicar. De hecho, esa es la razón principal de la elección de vi y Emacs para los modos de edición del shell Korn. Si eres un usuario experimentado de uno de estos editores, utiliza el modo de edición correspondiente del shell Korn. Si eres un mago del vi, probablemente sepas cómo navegar entre dos puntos cualesquiera de una línea en tres pulsaciones de tecla o menos.
Pero si no lo eres, deberías plantearte seriamente adoptar hábitos dactilares en modo emacs. Dado que se basa en las teclas de control, al igual que el soporte de edición mínimo que ya habrás utilizado con el shell Bourne o C, el modo emacs te resultará más fácil de asimilar. Aunque el Emacs completo es un editor extremadamente potente, su estructura de comandos se presta muy bien a pequeños subconjuntos: hay varios editores estilo <<mini-emacs>> flotando por ahí para Unix, MS-DOS y otros sistemas.
No se puede decir lo mismo de \emph{vi}, porque su estructura de comandos está realmente pensada para usarse en un editor a pantalla completa. \emph{vi} es bastante potente también, a su manera, pero su potencia se hace evidente sólo cuando se usa para propósitos similares a aquellos para los que fue diseñado: editar código fuente en C y LISP. Hacer cosas complicadas en \emph{vi} requiere relativamente pocas pulsaciones. Pero hacer cosas sencillas requiere más pulsaciones en modo \emph{vi} que en modo emacs. Por desgracia, la capacidad de hacer cosas sencillas con un número mínimo de pulsaciones es lo más deseado en un intérprete de comandos, especialmente hoy en día, cuando los usuarios pasan más tiempo dentro de las aplicaciones y menos trabajando con el intérprete de comandos.
Ambos modos de edición del intérprete de comandos Korn tienen bastantes comandos; sin duda desarrollarás hábitos de digitación que incluyen sólo algunos de ellos. Si usas el modo emacs y no estás familiarizado con el Emacs completo, aquí tienes un subconjunto que es fácil de aprender pero que te permite hacer casi cualquier cosa:
\begin{itemize}
\item{Para mover el cursor por la línea de comandos, usa CTRL-A y CTRL-E para empezar y terminar la línea, y CTRL-F y CTRL-B para moverte por ella.}
\item{Borra con DEL (o la tecla de borrado que tengas) y CTRL-D; al igual que con CTRL-F y CTRL-B, mantén pulsada la tecla para repetir si es necesario. Usa CTRL-C para borrar toda la línea.}
\item{Utiliza CTRL-P para recuperar el último comando si cometes un error.}
\item{Usa CTRL-R para buscar un comando que necesites ejecutar de nuevo.} \item{Utiliza TAB para completar names de archivo, comandos y variables.}
\end{itemize}
Tras unas horas aprendiendo estos hábitos, te preguntarás cómo has podido vivir sin la edición en línea de comandos.

722
Secciones/Capitulo3.tex Normal file
View File

@ -0,0 +1,722 @@
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.

1365
Secciones/Capitulo4.tex Normal file

File diff suppressed because it is too large Load Diff

1263
Secciones/Capitulo5.tex Normal file

File diff suppressed because it is too large Load Diff

1272
Secciones/Capitulo6.tex Normal file

File diff suppressed because it is too large Load Diff

1603
Secciones/Capitulo7.tex Normal file

File diff suppressed because it is too large Load Diff

933
Secciones/Capitulo8.tex Normal file
View File

@ -0,0 +1,933 @@
El sistema operativo Unix construyó su reputación sobre un pequeño número de conceptos, todos los cuales son simples pero poderosos. Ya hemos visto la mayoría de ellos: entrada/salida estándar, tuberías, utilidades de filtrado de texto, el sistema de archivos estructurado en árbol, y así sucesivamente. Unix también ganó notoriedad como el primer sistema operativo para computadoras pequeñas \footnote{Los sistemas PDP-11 en los que Unix se volvió popular inicialmente se consideraban pequeños para esa época.}
que le daba a cada usuario control sobre más de un proceso. Llamamos a esta \emph{capacidad multitarea controlada} por el usuario.
Si Unix es el único sistema operativo que te resulta familiar, podrías sorprenderte al descubrir que varios otros sistemas operativos importantes han carecido lamentablemente en esta área. Por ejemplo, el MS-DOS de Microsoft, para compatibles IBM PC, no tiene multitarea en absoluto, y mucho menos multitarea controlada por el usuario. El sistema VM/CMS de IBM para mainframes grandes maneja múltiples usuarios pero les otorga solo un proceso cada uno. OpenVMS de Compaq tiene multitarea controlada por el usuario, pero es limitada y difícil de usar. La última generación de sistemas operativos para computadoras pequeñas, como el Macintosh OS X de Apple (que se basa en BSD) y el Windows de Microsoft (Windows 95 y posteriores), finalmente incluyen la multitarea controlada por el usuario a nivel del sistema operativo.
Pero si has llegado tan lejos en este libro, probablemente no pienses que la multitarea sea algo importante. Probablemente estés acostumbrado a la idea de ejecutar un proceso en segundo plano colocando un ampersand (\&) al final de la línea de comandos. También has visto la idea de un subproceso de shell en el \hyperref[sec:Chapter4]{Capítulo 4}, cuando mostramos cómo se ejecutan los scripts de shell.
En este capítulo, cubrimos la mayoría de las características del shell Korn que se relacionan con la multitarea y el manejo de procesos en general. Decimos <<la mayoría>> porque algunas de estas características son, al igual que los descriptores de archivos que vimos en el \hyperref[sec:Chapter7]{Capítulo 7}, de interés solo para programadores de sistemas de bajo nivel.
Comenzamos mirando ciertos primitivos importantes para identificar procesos y controlarlos durante las sesiones de inicio y dentro de los scripts de shell. Luego nos movemos a una perspectiva de nivel superior, examinando formas de hacer que los procesos se comuniquen entre sí. La facilidad de las corutinas del shell Korn es el esquema de comunicación interprocesos más sofisticado que examinaremos; también analizamos en más detalle conceptos que ya hemos visto, como las tuberías y los subprocesos de shell.
No te preocupes por atascarte en detalles técnicos de bajo nivel sobre Unix. Proporcionamos solo la información técnica necesaria para explicar las características de nivel superior, además de algunos otros detalles diseñados para despertar tu curiosidad. Si estás interesado en obtener más información sobre estas áreas, consulta tu Manual del programador de Unix o un libro sobre internos de Unix que sea relevante para tu versión de Unix.
Recomendamos encarecidamente que pruebes los ejemplos en este capítulo. El comportamiento del código que involucra múltiples procesos no es tan fácil de entender en papel como la mayoría de los otros ejemplos en este libro.
\section{ID de proceso y números de trabajo}
Unix asigna números, llamados \emph{identificadores de proceso} (PID, por sus siglas en inglés), a todos los procesos cuando se crean. Notarás que, cuando ejecutas un comando en segundo plano agregándole un ampersand (\&), el shell responde con una línea que se ve así:
\begin{lstlisting}[language=bash]
$ fred &
[1] 2349
\end{lstlisting}
En este ejemplo, 2349 es el PID del proceso \emph{fred}. El \texttt{[1]} es un \emph{número de trabajo} asignado por el shell (no el sistema operativo). ¿Cuál es la diferencia? Los números de trabajo se refieren a procesos en segundo plano que se están ejecutando actualmente bajo tu shell, mientras que los PID se refieren a todos los procesos que se están ejecutando actualmente en todo el sistema, para todos los usuarios. El término \emph{job} básicamente se refiere a una línea de comando que se invocó desde tu shell de inicio de sesión.
Si inicias trabajos adicionales en segundo plano mientras el primero aún se está ejecutando, el shell los numera como 2, 3, etc. Por ejemplo:
\begin{lstlisting}[language=bash]
$ bob &
[2] 2367
$ dave | george &
[3] 2382
\end{lstlisting}
Claramente, 1, 2 y 3 son más fáciles de recordar que 2349, 2367 y 2382.
El shell incluye números de trabajo en mensajes que imprime cuando se completa un trabajo en segundo plano, como este:
\begin{lstlisting}[language=bash]
[1] + Done fred &
\end{lstlisting}
Explicaremos lo que significa el signo más pronto. Si el trabajo sale con un estado distinto de cero (ver \hyperref[sec:Chapter5]{Capítulo 5}), el shell incluye el estado de salida entre paréntesis:
\begin{lstlisting}[language=bash]
[1] + Done(1) fred &
\end{lstlisting}
El shell imprime otros tipos de mensajes cuando suceden ciertas cosas anormales con los trabajos en segundo plano; veremos estos más adelante en este capítulo.
\section{Control de trabajo}
¿Por qué deberías preocuparte por los identificadores de proceso o los números de trabajo? En realidad, podrías manejarte bien en tu vida de Unix sin referirte nunca a los identificadores de proceso (a menos que uses una estación de trabajo con ventanas, como veremos pronto). Sin embargo, los números de trabajo son más importantes: puedes usarlos con los comandos del shell para el \emph{control de trabajos}.
Ya conoces la forma más obvia de controlar un trabajo: puedes crear uno en segundo plano con \&. Una vez que un trabajo se está ejecutando en segundo plano, puedes dejar que se complete, llevarlo al \emph{primer plano} o enviarle un mensaje llamado \emph{signal}.
\subsection{Primer plano y segundo plano}
El comando integrado \emph{fg} lleva un trabajo en segundo plano al primer plano. Normalmente, esto significa que el trabajo tiene control de tu terminal o ventana y, por lo tanto, puede aceptar tu entrada. En otras palabras, el trabajo comienza a actuar como si hubieras escrito su comando sin el \&.
Si solo tienes un trabajo en segundo plano, puedes usar \emph{fg} sin argumentos, y el shell lleva ese trabajo al primer plano. Pero si tienes varios trabajos en segundo plano, el shell elige el que pusiste en segundo plano más recientemente. Si deseas que se lleve al primer plano un trabajo diferente, debes usar el nombre del comando del trabajo, precedido por un signo de porcentaje (\%), o puedes usar su número de trabajo, también precedido por \%, o su identificador de proceso sin un signo de porcentaje. Si no recuerdas qué trabajos se están ejecutando, puedes usar el comando \emph{jobs} para listarlos.
Algunos ejemplos deberían aclarar esto. Digamos que creaste tres trabajos en segundo plano como se muestra arriba. Si escribes \emph{jobs}, verás esto:
\begin{lstlisting}[language=bash]
[1] Running fred &
[2] - Running bob &
[3] + Running dave | george &
\end{lstlisting}
\emph{jobs} tiene algunas opciones interesantes. Además del estado del trabajo, \texttt{jobs -l} también lista los identificadores de grupo de procesos:
\begin{lstlisting}[language=bash]
[1] 2349 Running fred &
[2] - 2367 Running bob &
[3] + 2382 Running dave | george &
\end{lstlisting}
¿Cómo funciona todo esto? Cada vez que ejecutas un trabajo, el o los procesos en el trabajo se colocan en un nuevo \emph{grupo de procesos}. Cada proceso en un grupo de procesos, además de su número único de identificación de proceso, también tiene un \emph{identificador de grupo de procesos(ID)}. El identificador de grupo de procesos es igual al identificador de proceso del \emph{líder} del grupo de procesos, que es uno de los procesos invocados como parte del trabajo. (De hecho, el último en la tubería). Los números que imprime el shell son, de hecho, los identificadores de grupo de procesos. (Observa que para el trabajo 3, hay dos procesos, pero solo un número).
Ahora bien, tu dispositivo terminal, ya sea un puerto serie real o un seudoterminal como el que obtienes en un sistema de ventanas o una \emph{sesión de telnet}, también tiene un número de identificación de grupo de procesos. Los procesos cuyo identificador de grupo de procesos coincide con el del terminal <<poseen>> el terminal, en el sentido de que se les permite leer la entrada desde él. En resumen, el control de trabajos funciona configurando el grupo de procesos del terminal para que sea el mismo que el grupo de procesos del trabajo actual. (Hay muchos más detalles técnicos, incluida la idea de una <<sesión>> introducida por POSIX, pero esos detalles no son necesarios para entender el uso cotidiano del control de trabajos).
La opción \texttt{-p} le dice a \emph{jobs} que liste \emph{solo} los identificadores de grupo de procesos:
\begin{lstlisting}[language=bash]
$ jobs -p
2349
2367
2382
\end{lstlisting}
Esto podría ser útil con la sustitución de comandos; consulta la \hyperref[box:8-1]{Tarea 8-1} más adelante en este capítulo. Finalmente, la opción \texttt{-n} lista solo aquellos trabajos cuyo estado ha cambiado desde la última vez que el shell lo informó, ya sea con un comando jobs o de otra manera.
Si escribes \emph{fg} sin un argumento, el shell coloca a \texttt{dave | george} en primer plano, porque fue colocado en segundo plano más recientemente. Pero si escribes \texttt{fg \%bob} (o \texttt{fg \%2}), bob irá al primer plano.
También puedes referirte al trabajo colocado más recientemente en segundo plano con \texttt{\%+}. De manera similar, \texttt{\%-} se refiere al trabajo en segundo plano invocado a continuación más recientemente (\emph{bob} en este caso). Eso explica los signos más y menos en lo anterior: el signo más muestra el trabajo invocado más recientemente; el signo menos muestra el trabajo invocado a continuación más recientemente. \footnote{Esto es análogo a \~{}+ y \~{}- como referencias al directorio actual y anterior; consulta la nota al pie en el C\hyperref[sec:Chapter7]{apítulo 7}. Además: \%\% es un sinónimo de \%+.}
Si más de un trabajo en segundo plano tiene el mismo comando, entonces \texttt{\%command} desambiguará eligiendo el trabajo invocado más recientemente (como cabría esperar). Si esto no es lo que deseas, debes usar el número del trabajo en lugar del nombre del comando. Sin embargo, si los comandos tienen argumentos diferentes, puedes usar \texttt{\%?string} en lugar de \texttt{\%command. \%?string} se refiere al trabajo cuyo comando contiene la cadena. Por ejemplo, supongamos que iniciaste estos trabajos en segundo plano:
\begin{lstlisting}[language=bash]
$ bob pete &
[1] 189
$ bob ralph &
[2] 190
$
\end{lstlisting}
Luego puedes usar \texttt{\%?pete} y \texttt{\%?ralph} para referirte a cada uno de ellos, aunque en realidad \texttt{\%?pe} y \texttt{\%?ra} son suficientes para desambiguar.
La Tabla \ref{Tab:8-1} enumera todas las formas de referirse a trabajos en segundo plano. Hemos descubierto que, dado lo infrecuentemente que las personas utilizan comandos de control de trabajos, los números de trabajo o los nombres de comando son suficientes, y las demás formas son superfluas.
\begin{longtable}[h]{|m{3cm}|m{12cm}|}
\caption{Formas de referirse a trabajos en segundo plano}
\label{Tab:8-1}\\
\hline
\textbf{Referencia} & \textbf{Trabajo en segundo plano} \\ \hline
\endfirsthead
\hline
\textbf{Referencia} & \textbf{Trabajo en segundo plano} \\ \hline
\endhead
N & Identificación de proceso N \\\hline
-N & Identificación de grupo de procesos N \\\hline
\%N & Número de trabajo N \\\hline
\%\emph{string} & Trabajo cuyo comando comienza con \emph{string} \\\hline
\%?\emph{string} & Trabajo cuyo comando contiene \emph{string} \\\hline
\%+, \%\% & Trabajo en segundo plano invocado más recientemente \\\hline
\%- & Segundo trabajo en segundo plano invocado más recientemente \\\hline
\end{longtable}
\subsection{Suspendiendo un trabajo}
Al igual que puedes llevar trabajos en segundo plano al primer plano con \emph{fg}, también puedes enviar un trabajo en primer plano al segundo plano. Esto implica suspender el trabajo para que el shell recupere el control de tu terminal.
Para suspender un trabajo, escribe CTRL-Z \footnote{Esto asume que la tecla CTRL-Z está configurada como tu tecla de suspensión; al igual que con CTRL-C e interrupciones, esto es convencional pero no obligatorio.}
mientras se está ejecutando. Esto es análogo a escribir CTRL-C (o la tecla de interrupción que tengas configurada), con la diferencia de que puedes reanudar el trabajo después de detenerlo. Cuando escribes CTRL-Z, el shell responde con un mensaje como este:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
[1] + Detenido (* \emph{comando} *)
\end{lstlisting}
Luego te devuelve tu indicador de comando. También coloca el trabajo suspendido en la parte superior de la lista de trabajos, como indica el signo +.
Para reanudar un trabajo suspendido para que continúe ejecutándose en primer plano, simplemente escribe \emph{fg}. Si, por alguna razón, pusiste otros trabajos en segundo plano después de escribir CTRL-Z, usa \emph{fg} con un nombre o número de trabajo. Por ejemplo:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
(* \emph{fred se está ejecutando...} *)
(* \textbf{CTRL-Z} *)
[1] + Detenido (* \emph{fred} *)
(* \textbf{\$ bob \&} *)
[2] bob &
(* \textbf{\$ fg \%fred} *)
(* \emph{fred se reanuda en primer plano...} *)
\end{lstlisting}
La capacidad de suspender trabajos y reanudarlos en primer plano resulta muy útil cuando solo tienes una conexión a tu sistema, \footnote{Como cuando te conectas desde casa a tu oficina, o estás conectado a un sistema remoto a través de Internet mediante \emph{telnet} o \emph{ssh}.}
y estás utilizando un editor de texto como vi en un archivo que necesita ser procesado. Por ejemplo, si estás editando un archivo HTML para tu servidor web, puedes hacer lo siguiente:
\begin{lstlisting}[language=bash]
$ vi myfile.html
Edita el archivo... CTRL-Z
[1] + Detenido vi myfile.html
$ lynx myfile.html Previsualiza los resultados con un navegador de solo texto
Ves que cometiste un error
$ fg
vi vuelve a abrir en el mismo lugar de tu archivo
\end{lstlisting}
Los programadores a menudo utilizan la misma técnica al depurar código fuente.
También es probable que encuentres útil suspender un trabajo y luego reanudarlo en segundo plano en lugar de en primer plano. Puedes iniciar un comando en primer plano (es decir, normalmente) y descubrir que tarda mucho más de lo esperado, por ejemplo, una búsqueda con \emph{grep}, \emph{sort} o una consulta a una base de datos. Necesitas que el comando finalice, pero también te gustaría recuperar el control de tu terminal para poder hacer otras tareas. Si escribes CTRL-Z seguido de \emph{bg}, mueves el trabajo al segundo plano. \footnote{Sin embargo, ten cuidado, ya que no todos los comandos se comportan de manera <<adecuada>> cuando haces esto. Ten especial precaución con los comandos que se ejecutan sobre una red en una máquina remota; podrías <<confundir>> al programa remoto.}
\subsection{Desvinculando un trabajo}
Normalmente, cuando cierras la sesión, el shell envía la señal HUP (consulta la siguiente sección) a cualquier trabajo en segundo plano. Si has iniciado un trabajo de larga duración en segundo plano y deseas que se complete sin importar qué, debes indicárselo al shell mediante el comando \emph{disown} con uno o más números de identificación de trabajo como argumentos. Sin argumentos, se desvinculan \emph{todos} los trabajos en segundo plano.
\section{Señales}
Dijimos anteriormente que escribir CTRL-Z para suspender un trabajo es similar a escribir CTRL-C para detener un trabajo, excepto que puedes reanudar el trabajo más tarde. De hecho, son similares de una manera más profunda: ambos son casos particulares del acto de enviar una \emph{señal} a un proceso.
Una señal es un mensaje que un proceso envía a otro cuando ocurre algún evento anormal o cuando quiere que el otro proceso haga algo. La mayoría de las veces, un proceso envía una señal a un subproceso que creó. Sin duda, ya te sientes cómodo con la idea de que un proceso puede comunicarse con otro a través de una tubería de entrada/salida; piensa en una señal como otra forma para que los procesos se comuniquen entre sí. (De hecho, cualquier libro de sistemas operativos te dirá que ambos son ejemplos del concepto general de \emph{comunicación entre procesos}, o IPC por sus siglas en inglés). \footnote{Las tuberías (pipes) y las señales eran los únicos mecanismos IPC en las primeras versiones de Unix. Las versiones más modernas tienen mecanismos adicionales, como sockets, tuberías con nombre y memoria compartida. Las tuberías con nombre son accesibles para los programadores de shell a través del comando \emph{mkfifo(1)}, lo cual está más allá del alcance de este libro.}
Dependiendo de la versión de Unix, hay entre dos y tres docenas de tipos de señales, incluyendo algunas que un programador puede usar para cualquier propósito. Las señales tienen números (del 1 al número de señales que el sistema admite) y nombres; nosotros usaremos los últimos. Puedes obtener una lista de todas las señales en tu sistema escribiendo \texttt{kill -l}. Ten en cuenta que, al escribir código de shell que involucre señales, los nombres de las señales son más portátiles a otras versiones de Unix que los números de señal.
\subsection{Señales con Control-Key}
Cuando escribes CTRL-C, le estás indicando al shell que envíe la señal INT (de <<interrupción>>) al trabajo actual; CTRL-Z envía la señal TSTP (de <<parada de terminal>>). También puedes enviar al trabajo actual una señal QUIT escribiendo CTRL-\textbackslash{} (control barra invertida); esto es como una versión <<más fuerte>> de CTRL-C. \footnote{CTRL-\textbackslash{} también puede hacer que el programa en ejecución deje un archivo llamado core en el directorio actual del programa. Este archivo contiene una imagen del proceso al cual le enviaste la señal; un programador podría usarlo para ayudar a depurar el programa que se estaba ejecutando. El nombre del archivo es un término (muy) anticuado para la memoria de una computadora. Otras señales también dejan estos <<volcados de núcleo>>; puedes eliminarlos a menos que un programador del sistema te diga lo contrario.}
Normalmente usarías CTRL-\textbackslash{} cuando (y solo cuando) CTRL-C no funciona.
Como veremos pronto, también hay una señal de <<pánico>> llamada KILL que puedes enviar a un proceso cuando ni siquiera CTRL-\textbackslash{} funciona. Pero no está asociada a ninguna tecla de control, lo que significa que no puedes usarla para detener el proceso que se está ejecutando actualmente. INT, TSTP y QUIT son las únicas señales que puedes usar con teclas de control (aunque algunos sistemas tienen señales de teclas de control adicionales).
Puedes personalizar las teclas de control que se utilizan para enviar señales con opciones del comando \emph{stty(1)}. Estas opciones varían de un sistema a otro; consulta la página de manual del comando para obtener información, pero la sintaxis habitual es \texttt{stty signame char}. \emph{signame} es un nombre para la señal que, lamentablemente, a menudo no es el mismo que los nombres que usamos aquí. La Tabla \ref{Tab:1-7} en el \hyperref[sec:Chapter1]{Capítulo 1} enumera los nombres \emph{stty} para señales y las acciones del controlador de terminal que se encuentran en todas las versiones modernas de Unix. \emph{char} es el carácter de control, que puedes dar en la misma notación que usamos. Por ejemplo, para configurar tu tecla INT en CTRL-X en la mayoría de los sistemas, usa:
\begin{lstlisting}[language=bash]
stty intr ^X
\end{lstlisting}
Ahora que te hemos dicho cómo hacer esto, deberíamos agregar que no lo recomendamos. Cambiar las teclas de señal podría causar problemas si alguien más tiene que detener un proceso descontrolado en tu máquina.
La mayoría de las otras señales son utilizadas por el sistema operativo para informar a los procesos sobre condiciones de error, como una instrucción de código de máquina incorrecta, una dirección de memoria incorrecta, división por cero u otros eventos como la disponibilidad de entrada en un descriptor de archivo o el temporizador (<<alarma>> en la terminología de Unix) que se activa. Las señales restantes se utilizan para condiciones de error esotéricas que solo interesan a los programadores de sistemas de bajo nivel; las versiones más nuevas de Unix tienen tipos de señales cada vez más arcanos.
\subsection{kill}
Puedes utilizar el comando incorporado del shell, \emph{kill}, para enviar una señal a cualquier proceso que hayas creado, no solo al trabajo que se está ejecutando actualmente. \emph{kill} toma como argumento el ID del proceso, el número de trabajo o el nombre del comando del proceso al cual deseas enviar la señal. Por defecto, \emph{kill} envía la señal TERM (<<terminar>>), que generalmente tiene el mismo efecto que la señal INT que envías con CTRL-C. Pero puedes especificar una señal diferente utilizando la opción -s y el nombre de la señal, o la opción -n y un número de señal.
\emph{kill} recibe su nombre debido a la naturaleza de la señal TERM predeterminada, pero hay otra razón, que tiene que ver con la forma en que Unix maneja las señales en general. Los detalles completos son demasiado complejos para entrar en ellos aquí, pero la siguiente explicación debería ser suficiente.
La mayoría de las señales hacen que un proceso que las recibe se detenga y muera; por lo tanto, si envías cualquiera de estas señales, <<matas>> el proceso que la recibe. Sin embargo, los programas pueden configurarse para <<atrapar>> señales específicas y tomar alguna otra acción. Por ejemplo, un editor de texto haría bien en guardar el archivo que se está editando antes de terminar cuando recibe una señal como INT, TERM o QUIT. Determinar qué hacer cuando llegan diversas señales es parte de la diversión de la programación de sistemas Unix.
Aquí tienes un ejemplo de \emph{kill}. Supongamos que tienes un proceso \emph{fred} en segundo plano, con un ID de proceso 480 y un número de trabajo 1, que necesita detenerse. Comenzarías con este comando:
\begin{lstlisting}[language=bash]
kill %1
\end{lstlisting}
Si tuviste éxito, verías un mensaje como este:
\begin{lstlisting}[language=bash]
[1] + Terminated fred &
\end{lstlisting}
Si no ves esto, entonces la señal TERM no logró detener el trabajo. El siguiente paso sería intentar con QUIT:
\begin{lstlisting}[language=bash]
kill -s QUIT %1
\end{lstlisting}
Si eso funcionó, verías este mensaje:
\begin{lstlisting}[language=bash]
[1] + Quit(coredump) fred &
\end{lstlisting}
El shell indica la señal que mató al programa (\emph{Quit}) y el hecho de que produjo un archivo de núcleo. Cuando un programa sale de manera normal, el estado de salida que devuelve al shell es un valor entre 0 y 255. Cuando un programa muere al recibir una señal, sale no con un valor de estado de su elección, sino con el estado 256+\emph{N}, donde \emph{N} es el número de la señal que recibió. (Con \emph{ksh88} y la mayoría de las otras shells, los estados de salida normales están entre 0 y 127, y el estado de <<muerte por señal>> es 128+\emph{N}. Caveat emptor.)
Si ni siquiera \texttt{QUIT} funciona, el método de último recurso sería usar \texttt{KILL}:
\begin{lstlisting}[language=bash]
kill -s KILL %1
\end{lstlisting}
(Observa cómo esto tiene el sabor de <<gritarle>> al proceso fuera de control). Esto produce el mensaje:
\begin{lstlisting}[language=bash]
[1] + Killed fred &
\end{lstlisting}
Es imposible que un proceso atrape una señal KILL: el sistema operativo debería terminar el proceso de inmediato e incondicionalmente. Si no lo hace, entonces tu proceso está en uno de los <<estados divertidos>> que veremos más adelante en este capítulo, o (mucho menos probable) hay un error en tu versión de Unix.
En sistemas de control de trabajos, hay una señal adicional e inatrapable: STOP. Esto es similar a TSTP, ya que suspende el trabajo objetivo. Pero a diferencia de TSTP, no se puede capturar ni ignorar. Es una señal más drástica que TSTP, pero menos que QUIT o TERM, ya que un trabajo detenido aún se puede continuar con \emph{fg} o \emph{bg}. El shell Korn proporciona el alias predefinido \texttt{stop='kill -s STOP'} para facilitar la detención de trabajos.
La \hyperref[box:8-1]{Tarea 8-1} es otro ejemplo de cómo usar el comando \emph{kill}.
\begin{mybox}[Tarea 8-1]\label{box:8-1}
Escribe una función llamada `killalljobs` que mate todos los trabajos en segundo plano.\footnote{Para probar tu comprensión de cómo funciona el shell, responde a esta pregunta: ¿por qué esto no se puede hacer como un script separado?}
\end{mybox}
La solución para esta tarea es simple, utilizando \texttt{jobs -p}:
\begin{lstlisting}[language=bash]
function killalljobs {
kill "$@" $(jobs -p)
}
\end{lstlisting}
Es posible que te sientas tentado a usar la señal KILL inmediatamente, en lugar de probar primero TERM (la predeterminada) y QUIT. No lo hagas. TERM y QUIT están diseñadas para darle al proceso la oportunidad de limpiar antes de salir, mientras que KILL detendrá el proceso, sin importar en qué parte de su cálculo se encuentre. \emph{Usa KILL solo como último recurso}.
Puedes usar el comando \emph{kill} con cualquier proceso que crees, no solo trabajos en segundo plano de tu shell actual. Por ejemplo, si usas un sistema de ventanas, es posible que tengas varias ventanas de terminal, cada una de las cuales ejecuta su propia shell. Si una shell está ejecutando un proceso que deseas detener, puedes matarlo desde otra ventana, pero no puedes referirte a él con un número de trabajo porque se está ejecutando bajo una shell diferente. Debes usar su ID de proceso en su lugar.
\subsection{ps}
Esta es probablemente la única situación en la que un usuario casual necesitaría conocer el ID de un proceso. El comando \emph{ps(1)} te brinda esta información; sin embargo, también puede darte mucha información adicional por la cual debes navegar.
\emph{ps} es un comando complejo. Tiene muchas opciones, algunas de las cuales difieren de una versión de Unix a otra. Para agregar a la confusión, es posible que necesites opciones diferentes en diferentes versiones de Unix para obtener la misma información. Usaremos opciones disponibles en los dos principales tipos de sistemas Unix, los derivados de System V (como la mayoría de las versiones para Intel x86, así como Solaris, AIX de IBM y HP-UX de Hewlett-Packard) y BSD (Ultrix de Compaq, SunOS 4.x y también GNU/Linux). Si no estás seguro de qué tipo de versión de Unix tienes, prueba primero con las opciones de System V.
Puedes invocar \emph{ps} en su forma más simple sin ninguna opción. En este caso, imprime una línea de información sobre el shell de inicio de sesión actual y cualquier proceso que se esté ejecutando bajo ella (es decir, trabajos en segundo plano). Por ejemplo, si invocaste tres trabajos en segundo plano, como vimos anteriormente en el capítulo, \emph{ps} en versiones derivadas de System V de Unix produciría una salida que se parece a esto:
\begin{lstlisting}[language=bash]
PID TTY TIME COMD
146 pts/10 0:03 ksh
2349 pts/10 0:03 fred
2367 pts/10 0:17 bob
2387 pts/10 0:06 george
2389 pts/10 0:09 dave
2390 pts/10 0:00 ps
\end{lstlisting}
La salida en sistemas derivados de BSD se vería así:
\begin{lstlisting}[language=bash]
PID TT STAT TIME COMMAND
146 10 S 0:03 /bin/ksh -i
2349 10 R 0:03 fred
2367 10 D 0:17 bob
2387 10 S 0:06 george
2389 10 R 0:09 dave
2390 10 R 0:00 ps
\end{lstlisting}
(Puedes ignorar la columna STAT). Esto es un poco como el comando \emph{jobs}. PID es el ID del proceso; TTY (o TT) es el terminal (o pseudo-terminal, si estás utilizando un sistema de ventanas) desde el cual se invocó el proceso; TIME es la cantidad de tiempo del procesador (no el tiempo real o <<de reloj>>) que el proceso ha utilizado hasta ahora; COMD (o COMMAND) es el comando. Observa que la versión BSD incluye los argumentos del comando, si los hay; también observa que la primera línea informa sobre el proceso del shell principal, y en la última línea, \emph{ps} informa sobre sí mismo.
\emph{ps} sin argumentos lista todos los procesos iniciados desde el terminal o pseudo-terminal actual. Pero como \emph{ps} no es un comando de shell, no correlaciona los ID de procesos con los números de trabajo del shell. Tampoco te ayuda a encontrar el ID del proceso fuera de control en otra ventana del shell.
Para obtener esta información, usa \texttt{ps -a} (para <<todo>>); esto lista información sobre un conjunto diferente de procesos, dependiendo de tu versión de Unix.
\subsubsection{System V}
En lugar de enumerar todos los procesos que se iniciaron bajo un terminal específico, \texttt{ps -a} en sistemas derivados de System V lista todos los procesos asociados con cualquier terminal que no sean líderes de grupo. Para nuestros propósitos, un <<líder de grupo>> es el shell principal de un terminal o ventana. Por lo tanto, si estás utilizando un sistema de ventanas, \texttt{ps -a} lista todos los trabajos iniciados en todas las ventanas (por todos los usuarios), pero no sus shells principales.
Supongamos que, en el ejemplo anterior, tienes solo un terminal o ventana. Entonces, \texttt{ps -a} imprime la misma salida que \emph{ps} simple, excepto por la primera línea, ya que esa es el shell principal. Esto no parece ser muy útil.
Pero considera lo que sucede cuando tienes varias ventanas abiertas. Digamos que tienes tres ventanas, todas ejecutando emuladores de terminal como \emph{xterm} para el Sistema de Ventanas X. Inicias trabajos en segundo plano \emph{fred, dave} y \emph{bob} en ventanas con números de pseudo-terminal 1, 2 y 3, respectivamente. Esta situación se muestra en la Figura \ref{fig:8-1}.
\begin{figure}[h!]
\centering
\includegraphics[scale=0.5]{fig_09}
\caption{\small{Trabajos en segundo plano en múltiples ventanas}}
\label{fig:8-1}
\end{figure}
Supongamos que estás en la ventana superior. Si escribes \emph{ps}, verás algo como esto:
\begin{lstlisting}[language=bash]
PID TTY TIME COMD
146 pts/1 0:03 ksh
2349 pts/1 0:03 fred
2390 pts/1 0:00 ps
\end{lstlisting}
Pero si escribes \emph{ps -a}, verás esto:
\begin{lstlisting}[language=bash]
PID TTY TIME COMD
2349 pts/1 0:03 fred
2367 pts/2 0:17 bob
2389 pts/3 0:09 dave
2390 pts/1 0:00 ps
\end{lstlisting}
Ahora deberías ver cómo \texttt{ps -a} puede ayudarte a rastrear un proceso fuera de control. Si es \emph{dave}, puedes escribir \texttt{kill 2389}. Si eso no funciona, intenta \texttt{kill -s QUIT 2389}, o en el peor caso, \texttt{kill -s KILL 2389}.
\subsubsection{BSD}
En sistemas derivados de BSD, \footnote{En sistemas GNU/Linux, \emph{ps} actúa como la versión de BSD.}
\emph{ps -a} lista todos los trabajos que se iniciaron en cualquier terminal; en otras palabras, es un poco como concatenar los resultados de \emph{ps} simple para cada usuario en el sistema. Dado el escenario anterior, \texttt{ps -a} te mostrará todos los procesos que la versión de System V muestra, además de los líderes de grupo (shells principales).
Desafortunadamente, \texttt{ps -a} (en cualquier versión de Unix) no informará sobre procesos que se encuentren en ciertas condiciones patológicas donde <<olvidan>> cosas como qué shell los invocó y a qué terminal pertenecen. Estos procesos tienen nombres coloridos (zombies, huérfanos) que realmente se utilizan en la literatura técnica de Unix, no solo de manera informal por programadores de sistemas profesionales. Si tienes un problema grave con un proceso fuera de control, es posible que el proceso haya ingresado a uno de estos estados.
No nos preocupemos por el porqué o cómo un proceso llega a este estado. Todo lo que necesitas entender es que el proceso no aparecerá cuando escribas \texttt{ps -a}. Necesitas otra opción para \emph{ps} para verlo: en System V, es \texttt{ps -e} (<<everything>>), mientras que en BSD, es \texttt{ps -ax}.
Estas opciones indican a \emph{ps} que liste procesos que no se iniciaron desde terminales o que <<olvidaron>> desde qué terminal se iniciaron. La primera categoría incluye muchos procesos que probablemente ni siquiera sabías que existían: estos incluyen procesos básicos que ejecutan el sistema y los llamados \emph{daemons} (pronunciado <demonios>>) que manejan servicios del sistema como correo, impresión, sistemas de archivos en red, etc.
De hecho, la salida de \texttt{ps -e} o \texttt{ps -ax} es una excelente fuente de educación sobre los entresijos internos del sistema Unix, si tienes curiosidad por conocerlos. Ejecuta el comando en tu sistema y, para cada línea de la lista que parezca interesante, invoca `man` en el nombre del proceso o búscalo en el Manual del Programador de Unix para tu sistema.
Las shells de usuario y los procesos se enumeran en la parte inferior de la salida de \texttt{ps -e} o \texttt{ps -ax}; aquí es donde debes buscar procesos fuera de control. Observa que muchos procesos en la lista tienen un ? en lugar de un terminal. Estos bien no deberían tener uno (como los daemons básicos) o son procesos fuera de control. Por lo tanto, es probable que si \texttt{ps -a} no encuentra un proceso que estás intentando eliminar, \texttt{ps -e} (o \texttt{ps -ax}) lo mostrará con ? en la columna TTY (o TT). Puedes determinar qué proceso deseas observando la columna COMD (o COMMAND).
\subsection{kill: La historia completa}
El comando \emph{kill} tiene un nombre bastante equívoco. Debería haberse llamado \emph{sendsignal} o algo similar, ya que envía señales a los procesos. (De hecho, el nombre se deriva de la llamada al sistema \emph{kill(2)}, que el comando \emph{kill} utiliza para enviar señales, y que está igualmente mal nombrada).
Como vimos anteriormente, \texttt{kill -l} te proporciona la lista completa de nombres de señales disponibles en tu sistema. El comportamiento de la versión incorporada de \emph{kill} se ha racionalizado considerablemente en \emph{ksh93}. Las opciones y lo que hacen se resumen en la Tabla \ref{Tab:8-2}.
\begin{table}[h]
\center
\caption{Opciones para kill}
\label{Tab:8-2}
\begin{tabular}{|m{4cm}|m{11cm}|} \hline
\textbf{Opción} & \textbf{Significado} \\ \hline
kill \emph{job}... & Envía la señal TERM a cada \emph{trabajo} nombrado. Este es el uso normal. \\\hline
kill -l & Enumera los nombres de todas las señales compatibles. \\\hline
kill -l \emph{signal}... & Si \emph{signal} es un número, imprime su nombre. Si es un nombre, imprime su número. Si \emph{signal} es un número mayor que 256, se trata como un estado de salida. El shell resta 256 e imprime la señal correspondiente. \\\hline
kill -s \emph{signal-name job}... & Envía la señal nombrada por signal-name a cada trabajo dado. \\\hline
kill -n \emph{signal-number job}... & Envía la señal numérica dada por \emph{signal-number} a cada trabajo dado. \\\hline
kill \emph{-signal job}... & Envía la señal especificada por \emph{signal} a cada \emph{trabajo} dado. \emph{signal} puede ser un número o un nombre de señal. Esta forma se considera obsoleta; se proporciona por compatibilidad con \emph{ksh88} y la orden externa \emph{kill(1)}. \\\hline
\end{tabular}
\end{table}
Un lugar para aprovechar la capacidad de \emph{kill} de convertir un número en un nombre es al emitir diagnósticos. Cuando un trabajo muere debido a una señal, el estado de salida es 256 más el número de señal. Por lo tanto, podrías usar un código como este para producir un diagnóstico significativo desde un script:
\begin{lstlisting}[language=bash]
es=$? # guarda el estado de salida
if ((es >= 256)); then
print "el trabajo recibió la señal $(kill -l $((es - 256)))"
fi
\end{lstlisting}
\section{trap}
Hemos estado discutiendo cómo las señales afectan al usuario casual; ahora hablemos un poco sobre cómo los programadores de shell pueden usarlas. No profundizaremos demasiado en esto, porque realmente es el ámbito de los programadores de sistemas.
Mencionamos anteriormente que los programas en general pueden configurarse para <<atrapar>> señales específicas y procesarlas a su manera. El comando integrado \emph{trap} te permite hacer esto desde un script de shell. \emph{trap} es crucial para <<blindar>> programas de shell extensos para que reaccionen adecuadamente a eventos anómalos, al igual que los programas en cualquier lenguaje deberían protegerse contra una entrada no válida. También es importante para ciertas tareas de programación de sistemas, como veremos en el próximo capítulo.
La sintaxis de trap es:
\begin{lstlisting}[language=bash]
trap cmd sig1 sig2 ...
\end{lstlisting}
Es decir, cuando se recibe cualquiera de \emph{sig1, sig2}, etc., ejecuta \emph{cmd} y luego continúa la ejecución. Después de que \emph{cmd} finaliza, el script continúa la ejecución justo después del comando que fue interrumpido. \footnote{Esto es lo que \emph{suele} suceder. A veces, el comando en ejecución actual se aborta (\emph{sleep} actúa así, como veremos pronto); otras veces, termina su ejecución. Los detalles adicionales están más allá del alcance de este libro.}
Por supuesto, \emph{cmd} puede ser un script o una función. Las señales pueden especificarse por nombre o por número. También puedes invocar \emph{trap} sin argumentos, en cuyo caso el shell imprime una lista de cualquier trampa que se haya establecido, utilizando nombres simbólicos para las señales. Si usas \texttt{trap -p}, el shell imprime la configuración de la trampa de una manera que se puede guardar y volver a leer más tarde mediante una invocación diferente del shell.
El shell escanea el texto de \emph{cmd} dos veces. La primera vez es mientras se prepara para ejecutar el comando \emph{trap}; todas las sustituciones como se describen en el C\hyperref[sec:Chapter7]{apítulo 7} se realizan antes de ejecutar el comando \emph{trap}. La segunda vez es cuando el shell ejecuta realmente la trampa. Por esta razón, es mejor usar comillas simples alrededor de \emph{cmd} en el texto del programa de shell. Cuando el shell ejecuta el comando de la trampa, \texttt{\$?} es siempre el estado de salida del último comando ejecutado antes de que comenzara la trampa. Esto es importante para diagnósticos.
Aquí tienes un ejemplo simple que muestra cómo funciona \emph{trap}. Supongamos que tenemos un script de shell llamado loop con este código:
\begin{lstlisting}[language=bash]
while true; do
sleep 60
done
\end{lstlisting}
Esto simplemente pausa durante 60 segundos (el comando \emph{sleep(1)}) y se repite indefinidamente. \emph{true} es un comando <<sin hacer nada>> cuyo estado de salida siempre es 0. Por eficiencia, está integrado en el shell. (El comando \emph{false} es un comando similar <<sin hacer nada>> cuyo estado de salida siempre es 1. También está integrado en el shell.) Como sucede, \emph{sleep} también está integrado en el shell. Intenta escribir este script. Invócalo, déjalo correr un rato y luego escribe CTRL-C (asumiendo que esa es tu tecla de interrupción). Debería detenerse y deberías recuperar tu indicador de shell.
Ahora inserta la siguiente línea al principio del script:
\begin{lstlisting}[language=bash]
trap 'print "¡Presionaste control-C!"' INT
\end{lstlisting}
Invoca el script de nuevo. Ahora presiona CTRL-C. Las probabilidades son abrumadoras de que estés interrumpiendo el comando \emph{sleep} (en lugar de \emph{true}). Deberías ver el mensaje <<¡Presionaste control-C!>>, y el script no se detendrá; en cambio, el comando \emph{sleep} se abortará y volverá a empezar otro \emph{sleep}. Presiona CTRL-\textbackslash{} para detenerlo. Escribe \texttt{rm core} para deshacerte del archivo de volcado de núcleo resultante.
A continuación, ejecuta el script en segundo plano escribiendo \texttt{loop \&}. Escribe \texttt{kill \%loop} (es decir, envíale la señal TERM); el script se detendrá. Agrega TERM al comando de trampa, de modo que se vea así:
\begin{lstlisting}[language=bash]
trap 'print "¡Presionaste control-C!"' INT TERM
\end{lstlisting}
Ahora repite el proceso: ejecútalo en segundo plano y escribe \texttt{kill \%loop}. Como antes, verás el mensaje y el proceso seguirá ejecutándose. Escribe \texttt{kill -KILL \%loop} para detenerlo.
Observa que el mensaje no es realmente apropiado cuando usas kill. Cambiaremos el script para que imprima un mensaje mejor en el caso de \emph{kill}:
\begin{lstlisting}[language=bash]
trap 'print "¡Presionaste control-C!"' INT
trap 'print "¡Intentaste matarme!"' TERM
while true; do
sleep 60
done
\end{lstlisting}
Ahora pruébalo de ambas maneras: en primer plano con CTRL-C y en segundo plano con \emph{kill}. Verás mensajes diferentes.
\subsection{Trampasy funciones}
La relación entre las trampas y las funciones del shell es directa, pero tiene ciertos matices que vale la pena discutir. Lo más importante que debes entender es que las funciones del shell Korn (aquellas creadas con la palabra clave \texttt{function}; consulta el C\hyperref[sec:Chapter4]{apítulo 4}) tienen sus propias trampas locales; estas no son conocidas fuera de la función. Las funciones de estilo antiguo de POSIX (aquellas creadas con la sintaxis \texttt{nombre()}) comparten trampas con el script principal.
Comencemos con las funciones de estilo \texttt{function}, donde las trampas son locales. En particular, el script circundante no las conoce. Considera este código:
\begin{lstlisting}[language=bash]
function settrap {
trap 'print "You hit control-C!"' INT
}
settrap
while true; do
sleep 60
done
\end{lstlisting}
Si invocas este script y presionas tu tecla de interrupción, simplemente se cierra. La trampa en INT en la función solo es conocida dentro de esa función. Por otro lado:
\begin{lstlisting}[language=bash]
function loop {
trap 'print "How dare you!"' INT
while true; do
sleep 60
done
}
trap 'print "You hit control-C!"' INT
loop
\end{lstlisting}
Cuando ejecutas este script y presionas tu tecla de interrupción, imprime <<¡Cómo te atreves!>> Pero, ¿qué tal esto?
\begin{lstlisting}[language=bash]
function loop {
while true; do
sleep 60
done
}
trap 'print "You hit control-C!"' INT
loop
print 'exiting ...'
\end{lstlisting}
Esta vez, el código de bucle está dentro de una función, y la trampa se establece en el script circundante. Si presionas tu tecla de interrupción, imprime el mensaje y luego imprime <<saliendo...>>. No repite el bucle como antes.
¿Por qué? Recuerda que cuando la señal entra, el shell aborta el comando actual, que en este caso es una llamada a una función. Toda la función se aborta, y la ejecución se reanuda en la siguiente declaración después de la llamada a la función.
La ventaja de las trampas locales a las funciones es que te permiten controlar el comportamiento de una función por separado del código circundante.
Sin embargo, es posible que desees definir trampas globales dentro de funciones. Existe una forma bastante chapucera de hacerlo; depende de una función que presentamos en el \hyperref[sec:Chapter9]{Capítulo 9}, que llamamos <<falsa señal>>. Aquí hay una manera de configurar trapcode como una trampa global para la señal SIG dentro de una función:
\begin{lstlisting}[language=bash]
trap "trap trapcode sig" EXIT
\end{lstlisting}
Esto configura el comando \texttt{trap trapcode SIG} para ejecutarse justo después de que la función salga, momento en el cual el script del shell circundante está en el ámbito (es decir, está <<a cargo>>). Cuando se ejecuta ese comando, \emph{trapcode} se configura para manejar la señal SIG.
Por ejemplo, es posible que desees restablecer la trampa en la señal que acabas de recibir, así:
\begin{lstlisting}[language=bash]
function trap_handler {
trap "trap second_handler INT" EXIT
print 'Interrupt: one more to abort.'
}
function second_handler {
print 'Aborted.' exit
}
trap trap_handler INT
\end{lstlisting}
Este código actúa como la utilidad de correo Unix: cuando estás escribiendo un mensaje, debes presionar tu tecla de interrupción dos veces para abortar el proceso.
Hay una forma menos chapucera de hacer esto, aprovechando el hecho de que las funciones de estilo POSIX comparten trampas con el script principal:
\begin{lstlisting}[language=bash]
# POSIX style function, trap is global
trap_handler () {
trap second_handler INT
print 'Interrupt: one more to abort.'
}
function second_handler {
print 'Aborted.' exit
}
trap trap_handler INT
while true ; do
sleep 60
done
\end{lstlisting}
Si escribes esto y lo ejecutas, obtendrás los mismos resultados que en el ejemplo anterior, sin la artimaña adicional de usar la falsa señal EXIT.
Hablando de \emph{mail}, en la \hyperref[box:8-2]{Tarea 8-2} mostraremos un ejemplo más práctico de trampas.
\begin{mybox}[Tarea 8-2]\label{box:8-2}
Como parte de un sistema de correo electrónico, escribe el código de shell que permite a un usuario componer un mensaje.
\end{mybox}
La idea básica es usar \emph{cat} para crear el mensaje en un archivo temporal y luego pasar el nombre del archivo a un programa que realmente envía el mensaje a su destino. El código para crear el archivo es muy simple:
\begin{lstlisting}[language=bash]
msgfile=/tmp/msg$$
cat > $msgfile
\end{lstlisting}
Dado que \emph{cat} sin un argumento lee desde la entrada estándar, esto simplemente espera a que el usuario escriba un mensaje y lo termine con el carácter de fin de archivo CTRL-D
\subsection{Variables de ID de proceso y ficheros temporales}
Lo único nuevo aquí es \$\$ en la expresión del nombre de archivo. Esta es una variable especial del shell cuyo valor es el ID de proceso del shell actual.
Para ver cómo funciona \$\$, escribe \texttt{ps} y toma nota del ID de proceso de tu proceso de shell (\emph{ksh}). Luego, escribe \texttt{print "\$\$"}; el shell responderá con ese mismo número. Ahora escribe \texttt{ksh} para iniciar un subproceso del shell y, cuando obtengas un indicador, repite el proceso. Deberías ver un número diferente, probablemente ligeramente superior al último.
Puedes examinar la relación entre padres e hijos con más detalle utilizando la variable \texttt{PPID} (ID de proceso padre). \emph{ksh} establece esto como el ID de proceso del proceso padre. Cada vez que inicias una nueva instancia de \emph{ksh}, si escribes \texttt{print \$PPID}, deberías ver un número que es igual a \$\$ del shell anterior.
Otra variable del shell relacionada es \emph{!} (es decir, su valor es \texttt{\$!}), que contiene el ID de proceso del trabajo en segundo plano más recientemente invocado. Para ver cómo funciona esto, inicia cualquier trabajo en segundo plano y toma nota del ID de proceso impreso por el shell junto a \texttt{[1]}. Luego escribe \texttt{print "\$!"}; deberías ver el mismo número.
Volviendo a nuestro ejemplo de correo: dado que todos los procesos en el sistema deben tener ID de procesos únicos, \$\$ es excelente para construir nombres de archivos temporales. Vimos un ejemplo de esto en el \hyperref[sec:Chapter7]{Capítulo 7}, cuando discutíamos los pasos de evaluación de la línea de comandos, y también hay ejemplos en el \hyperref[sec:Chapter9]{Capítulo 9}. \footnote{En la práctica, los nombres de archivos temporales basados solo en \$\$ pueden llevar a sistemas inseguros. Si tienes el programa \emph{mktemp(1)} en tu sistema, deberías usarlo en tus aplicaciones para generar nombres únicos para tus archivos temporales.}
El directorio \texttt{/tmp} se utiliza convencionalmente para archivos temporales. Por lo general, los archivos en este directorio se borran cada vez que se reinicia la computadora.
Sin embargo, un programa debería limpiar esos archivos antes de salir para evitar ocupar espacio en disco innecesario. Podríamos hacer esto en nuestro código muy fácilmente agregando la línea \texttt{rm \$msgfile} después del código que realmente envía el mensaje. Pero, ¿qué pasa si el programa recibe una señal durante la ejecución? Por ejemplo, ¿qué pasa si un usuario cambia de opinión sobre el envío del mensaje y presiona CTRL-C para detener el proceso? Necesitaríamos limpiar antes de salir. Emularemos el sistema Unix real guardando el mensaje que se está escribiendo en un archivo llamado \emph{dead.letter} en el directorio actual. Podemos hacer esto usando \emph{trap} con una cadena de comandos que incluye un comando de salida:
\begin{lstlisting}[language=bash]
trap 'mv $msgfile dead.letter; exit' INT TERM
msgfile=/tmp/msg$$
cat > $msgfile
# send the contents of $msgfile to the specified mail address ...
rm $msgfile
\end{lstlisting}
Cuando el script recibe una señal INT o TERM, guarda el archivo temporal y luego sale. Ten en cuenta que la cadena de comandos no se evalúa hasta que necesita ejecutarse, por lo que \texttt{\$msgfile} contendrá el valor correcto; es por eso que rodeamos la cadena con comillas simples.
Pero, ¿qué pasa si el script recibe una señal antes de que se cree \texttt{msgfile} - aunque sea poco probable? En ese caso, \emph{mv} intentará cambiar el nombre de un archivo que no existe. Para solucionar esto, necesitamos verificar la existencia del archivo \texttt{\$msgfile} antes de intentar guardarlo. El código para esto es un poco incómodo para ponerlo en una sola cadena de comandos, así que usaremos una función en su lugar:
\begin{lstlisting}[language=bash]
function cleanup {
if [[ -e $msgfile ]]; then
mv $msgfile dead.letter
fi
exit
}
trap cleanup INT TERM
msgfile=/tmp/msg$$
cat > $msgfile
# send the contents of $msgfile to the specified mail address ...
rm $msgfile
\end{lstlisting}
\subsection{Ignorando señales}
A veces, llega una señal que no deseas manejar. Si proporcionas la cadena nula (\texttt{""} o \texttt{''}) como argumento de comando para \emph{trap}, el shell efectivamente ignora esa señal. El ejemplo clásico de una señal que es posible que desees ignorar es HUP (hangup), la señal que reciben todos tus procesos en segundo plano cuando cierras sesión. (Si tu conexión realmente se cae, Unix envía la señal HUP al shell. El shell reenvía la señal a todos tus procesos en segundo plano o la envía por iniciativa propia si cierras sesión de forma normal).
HUP tiene el comportamiento predeterminado habitual: mata al proceso que la recibe. Pero seguramente habrá momentos en los que no querrás que un trabajo en segundo plano finalice cuando cierras sesión. Por ejemplo, podrías iniciar una compilación larga o un trabajo de formato de texto; quieres cerrar sesión y volver más tarde cuando esperas que el trabajo esté terminado. En circunstancias normales, tu trabajo en segundo plano terminaría cuando cierras sesión. Pero si lo ejecutas en un entorno de shell donde la señal HUP se ignora, el trabajo se completa.
Para hacer esto, podrías escribir una función simple que se ve así:
\begin{lstlisting}[language=bash]
function ignorehup {
trap "" HUP
eval "$@"
}
\end{lstlisting}
Escribimos esto como una función en lugar de un script por razones que se harán más claras cuando examinemos detenidamente los subprocesos al final de este capítulo.
En realidad, hay un comando Unix llamado \emph{nohup} que hace precisamente esto. La función `start` del último capítulo podría incluir \emph{nohup}:
\begin{lstlisting}[language=bash]
function start {
eval nohup "$@" > logfile 2>&1 &
}
\end{lstlisting}
Esto evita que HUP termine tu comando y guarda su salida estándar y de error en un archivo. De hecho, lo siguiente es igualmente bueno:
\begin{lstlisting}[language=bash]
function start {
nohup "$@" > logfile 2>&1 &
}
\end{lstlisting}
Si comprendes por qué \emph{eval} es prácticamente redundante cuando usas \emph{nohup} en este caso, entonces tienes un sólido entendimiento del material en el \hyperref[sec:Chapter7]{Capítulo 7}.
\subsection{Restablecimiento de trampas}
Otro <<caso especial>> del comando \emph{trap} ocurre cuando proporcionas un guion (-) como argumento de comando. Esto restablece la acción tomada cuando se recibe la señal al valor predeterminado, que generalmente es la terminación del proceso.
Como ejemplo de esto, volvamos a la \hyperref[box:8-2]{Tarea 8-2}, nuestro programa de correo. Después de que el usuario ha terminado de enviar el mensaje, el archivo temporal se borra. En ese momento, ya que ya no hay necesidad de <<limpiar>>, podemos restablecer la trampa de señal a su estado predeterminado. El código para esto, aparte de las definiciones de funciones, es:
\begin{lstlisting}[language=bash]
trap cleanup INT TERM
msgfile=/tmp/msg$$
cat > $msgfile
# enviar el contenido de $msgfile a la dirección de correo especificada ...
rm $msgfile
trap - INT TERM
\end{lstlisting}
La última línea de este código restablece los manejadores para las señales INT y TERM.
En este punto, es posible que estés pensando que uno podría involucrarse seriamente con el manejo de señales en un script de shell. Es cierto que los programas de alta resistencia dedican cantidades considerable de código al manejo de señales. Pero estos programas son casi siempre lo suficientemente grandes como para que el código de manejo de señales sea una fracción muy pequeña del conjunto. Por ejemplo, puedes apostar a que el sistema de correo real de Unix es bastante resistente.
Sin embargo, es probable que nunca escribas un script de shell lo suficientemente complejo y que necesite ser lo suficientemente robusto como para justificar mucho manejo de señales. Puedes escribir un prototipo para un programa tan grande como el correo en código de shell, pero los prototipos, por definición, no necesitan ser a prueba de balas.
Por lo tanto, no deberías preocuparte por poner código de manejo de señales en cada script de shell de 20 líneas que escribas. Nuestro consejo es determinar si hay situaciones en las que una señal podría hacer que tu programa haga algo seriamente malo y agregar código para manejar esas contingencias. ¿Qué es <<seriamente malo>>? Bueno, con respecto a los ejemplos anteriores, diríamos que el caso en el que HUP hace que tu trabajo termine al cerrar la sesión es seriamente malo, mientras que la situación del archivo temporal en nuestro programa de correo no lo es.
El shell Korn tiene varias opciones nuevas para \emph{trap} (con respecto al mismo comando en la mayoría de las conchas Bourne) que lo hacen útil como ayuda para depurar scripts de shell. Las cubrimos en el \hyperref[sec:Chapter9]{Capítulo 9}.
\section{Corrutinas}
Hemos dedicado las últimas páginas a detalles casi microscópicos del comportamiento de los procesos. En lugar de continuar nuestra inmersión en las profundidades turbias, volveremos a una vista de más alto nivel de los procesos.
Anteriormente en este capítulo, cubrimos formas de controlar múltiples trabajos simultáneos dentro de una sesión interactiva de inicio de sesión; ahora consideramos el control de múltiples procesos dentro de programas de shell. Cuando dos (o más) procesos están programados explícitamente para ejecutarse simultáneamente y posiblemente comunicarse entre sí, los llamamos corotinas.
Esto no es realmente nuevo: una \emph{tubería} es un ejemplo de corotinas. El constructo de una tubería del shell encapsula un conjunto bastante sofisticado de reglas sobre cómo interactúan los procesos entre sí. Si observamos más de cerca estas reglas, podremos entender mejor otras formas de manejar corotinas, la mayoría de las cuales resultan ser más simples que las tuberías.
Cuando invocas una tubería simple, por ejemplo, \texttt{ls | more}, el shell invoca una serie de operaciones primitivas de Unix, también conocidas como \emph{llamadas al sistema}. En efecto, el shell le indica a Unix que haga lo siguiente; en caso de que estés interesado, incluimos entre paréntesis la llamada al sistema real utilizada en cada paso:
\begin{enumerate}
\item Crear la tubería que manejará la entrada/salida entre los procesos (llamada al sistema \emph{pipe}).
\item Crear dos subprocesos, que llamaremos P1 y P2 (\emph{fork}).
\item Configurar la entrada/salida entre los procesos para que la salida estándar de P1 alimente la entrada estándar de P2 (\emph{dup}, \emph{close}).
\item Iniciar \texttt{/bin/ls} en el proceso P1 (\emph{exec}).
\item Iniciar \texttt{/bin/more} en el proceso P2 (\emph{exec}).
\item Esperar a que ambos procesos terminen (\emph{wait}).
\end{enumerate}
Probablemente puedas imaginar cómo cambian los pasos anteriores cuando la tubería involucra más de dos procesos.
Ahora simplifiquemos las cosas. Veremos cómo hacer que varios procesos se ejecuten al mismo tiempo si los procesos no necesitan comunicarse. Por ejemplo, queremos que los procesos \emph{dave} y \emph{bob} se ejecuten como corotinas, sin comunicación, en un script de shell. Ambos deben ejecutarse por completo antes de que el script finalice. Nuestra solución inicial sería esta:
\begin{lstlisting}[language=bash]
dave &
bob
\end{lstlisting}
Supongamos por un momento que \emph{bob} es el último comando en el script. Lo anterior funciona, pero solo si \emph{dave} termina primero. Si \emph{dave} aún se está ejecutando cuando el script termina, se convierte en un proceso \emph{huérfano}, es decir, entra en uno de los <<estados extraños>> que mencionamos anteriormente en este capítulo. No importa los detalles de ser un huérfano; simplemente cree que no quieres que esto suceda, y si ocurre, es posible que necesites usar el método de <<proceso fugitivo>> para detenerlo, discutido anteriormente en este capítulo. (Por ejemplo, considera el caso en el que \emph{dave} se lanza a un consumo excesivo de recursos, ralentizando tu sistema, y es mucho más difícil detenerlo si el script principal ya ha salido).
\subsection{wait}
Hay una manera de asegurarse de que el script no termine antes que \emph{dave}: el comando integrado \emph{wait}. Sin argumentos, \emph{wait} simplemente espera hasta que todos los trabajos en segundo plano hayan terminado. Entonces, para asegurarse de que el código anterior se comporte correctamente, agregaríamos \emph{wait} de la siguiente manera:
\begin{lstlisting}[language=bash]
dave &
bob
wait
\end{lstlisting}
Aquí, si \emph{bob} termina primero, el shell principal espera a que \emph{dave} termine antes de finalizar.
Si tu script tiene más de un trabajo en segundo plano y necesitas esperar a que terminen trabajos específicos, puedes darle a \emph{wait} el mismo tipo de argumento de trabajo (con un signo de porcentaje) que usarías con \emph{kill, fg} o \emph{bg}.
Sin embargo, es probable que encuentres que \emph{wait} sin argumentos es suficiente para todas las corotinas que programarás. Las situaciones en las que necesitarías esperar trabajos en segundo plano específicos son bastante complejas y están fuera del alcance de este libro.
\subsection{Ventajas y desventajas de las corrutinas}
De hecho, puede que te preguntes por qué necesitarías programar corotinas que no se comunican entre sí. Por ejemplo, ¿por qué no simplemente ejecutar \emph{bob} después de \emph{dave} de la manera habitual? ¿Qué ventaja hay en ejecutar los dos trabajos simultáneamente?
Si estás ejecutando un equipo con un solo procesador (CPU), hay una ventaja de rendimiento, pero solo si tienes desactivada la opción \emph{bgnice} (ver \hyperref[sec:Chapter3]{Capítulo 3}), y aún así solo en ciertas situaciones.
Hablando en términos generales, puedes caracterizar un proceso en función de cómo utiliza los recursos del sistema de tres maneras: si es \emph{intensivo en CPU} (por ejemplo, realiza muchos cálculos numéricos), \emph{intensivo en E/S} (realiza mucha lectura o escritura en el disco) o \emph{interactivo} (requiere intervención del usuario).
Ya sabemos desde el \hyperref[sec:Chapter1]{Capítulo 1} que no tiene sentido ejecutar un trabajo interactivo en segundo plano. Pero aparte de eso, cuanto más difieran dos o más procesos en estos tres criterios, mejor es ejecutarlos simultáneamente. Por ejemplo, un cálculo estadístico intensivo en números funcionaría bien al mismo tiempo que una larga y intensiva consulta de base de datos.
Por otro lado, si dos procesos utilizan los recursos de manera similar, puede ser menos eficiente ejecutarlos al mismo tiempo que ejecutarlos secuencialmente. ¿Por qué? Básicamente, porque en tales circunstancias, el sistema operativo a menudo tiene que <<dividir el tiempo>> de los recursos en disputa.
Por ejemplo, si ambos procesos consumen muchos recursos del disco, el sistema operativo puede entrar en un modo en el que controla constantemente el interruptor del disco entre los dos procesos competidores; el sistema termina gastando al menos tanto tiempo haciendo el cambio como en los propios procesos. Este fenómeno se conoce como \emph{thrashing}; en su forma más severa, puede hacer que un sistema prácticamente se paralice. El thrashing es un problema común; tanto los administradores del sistema como los diseñadores de sistemas operativos pasan mucho tiempo tratando de minimizarlo.
\subsection{Paralelización}
Pero si tienes una computadora con múltiples CPUs \footnote{Los sistemas multiprocesador solían encontrarse solo en servidores a gran escala ubicados en salas de máquinas especiales con control climático. Hoy en día, los sistemas multiprocesador de escritorio están disponibles y se están volviendo cada vez más comunes, aunque los sistemas con más de alrededor de 4 CPUs aún tienden a estar principalmente en salas de máquinas.},
deberías preocuparte menos por el thrashing. Además, las corotinas pueden proporcionar aumentos dramáticos de velocidad en este tipo de máquina, que a menudo se llama una computadora \emph{paralela}; de manera análoga, descomponer un proceso en corotinas a veces se denomina \emph{paralelizar} el trabajo.
Normalmente, cuando inicias un trabajo en segundo plano en una máquina con múltiples CPUs, la computadora lo asigna al siguiente procesador disponible. Esto significa que los dos trabajos están realmente, no solo metafóricamente, ejecutándose al mismo tiempo.
En este caso, el tiempo de ejecución de las corotinas es esencialmente igual al del trabajo de mayor duración más un poco de sobrecarga, en lugar de la suma de los tiempos de ejecución de todos los procesos (aunque si todas las CPUs comparten una unidad de disco común, aún existe la posibilidad de thrashing relacionado con la E/S). En el mejor caso, con todos los trabajos teniendo el mismo tiempo de ejecución y sin contención de E/S, obtienes un factor de aceleración igual al número de CPUs.
Paralelizar un programa a menudo no es fácil; hay varios problemas sutiles involucrados y hay mucho margen para el error. Sin embargo, vale la pena saber cómo paralelizar un script de shell, ya sea que tengas o no una máquina paralela, especialmente porque estas máquinas son cada vez más comunes, incluso en el escritorio.
Mostraremos cómo hacer esto mediante la \hyperref[box:8-3]{Tarea 8-3}, una tarea sencilla cuya solución es propicia para la paralelización.
\begin{mybox}[Tarea 8-3]\label{box:8-3}
Aumenta el script del frente del compilador de C para compilar cada archivo fuente en paralelo.
\end{mybox}
Si tienes múltiples CPUs, hay un potencial considerable para acelerar considerablemente la compilación de múltiples archivos fuente en paralelo. Cada archivo es independiente del siguiente, y por lo tanto, crear múltiples archivos objeto simultáneamente realiza más trabajo, más rápido.
Los cambios son relativamente sencillos: inicia la tubería de compilación en segundo plano y luego agrega una declaración \emph{wait} antes de realizar el paso final de enlace:
\begin{lstlisting}[language=bash]
# initialize option-related variables
do_link=true
debug=""
link_libs=""
clib="-lc"
exefile=""
# initialize pipeline components
compile=" | ccom"
assemble=" | as"
optimize=""
# process command-line options
while getopts "cgl:[lib]o:[outfile]O files ..." opt; do
case $opt in
c )
do_link=false ;;
g )
debug="-g" ;;
l )
link_libs+=" -l $OPTARG" ;;
o )
exefile="-o $OPTARG" ;;
O )
optimize=" | optimize" ;;
esac
done
shift $(($OPTIND - 1))
# process the input files
for filename in "$@"; do
case $filename in
*.c )
objname=${filename%.c}.o ;;
*.s )
objname=${filename%.s}.o compile="" ;;
*.o )
objname=$filename # just link it directly with the rest
compile="" assemble="" ;;
* )
print "error: $filename is not a source or object file."
exit 1 ;;
esac
# run a pipeline for each input file; parallelize by backgrounding
eval cat \$filename $compile $assemble $optimize \> \$objname &
objfiles+=" $objname"
compile=" | ccom" assemble=" | as"
done
wait # wait for all compiles to finish before linking
if [[ $do_link == true ]]; then
ld $exefile $objfiles $link_libs $clib
fi
\end{lstlisting}
Este es un ejemplo directo de paralelización, siendo la única <<trampa>> asegurarse de que todas las compilaciones se realicen antes de realizar el paso final de enlace. De hecho, muchas versiones de \emph{make} tienen una bandera de <<ejecutar tantos trabajos en paralelo>> precisamente para obtener la aceleración de la compilación simultánea de archivos independientes.
Pero no toda la vida es tan simple; a veces, simplemente iniciar más trabajos en segundo plano no hará el truco. Por ejemplo, considera múltiples cambios en la misma base de datos: el software de la base de datos (o algo, en algún lugar) tiene que asegurarse de que dos procesos diferentes no estén intentando actualizar el mismo registro al mismo tiempo.
Las cosas se complican aún más al trabajar a un nivel más bajo, con múltiples hilos de control dentro de un solo proceso (algo que afortunadamente no es visible a nivel de shell). Dichos problemas, conocidos como problemas de \emph{control de concurrencia}, se vuelven mucho más difíciles a medida que aumenta la complejidad de la aplicación. Los programas concurrentes complejos a menudo tienen mucho más código para manejar los casos especiales que para el trabajo real que se supone deben hacer.
Por lo tanto, no debería sorprenderte que se haya hecho y se esté haciendo mucha investigación sobre la paralelización, siendo el objetivo final idear una herramienta que paralelice el código automáticamente. (Tales herramientas existen; generalmente trabajan dentro de algún subconjunto estrecho del problema). Incluso si no tienes acceso a una máquina con múltiples CPUs, paralelizar un script de shell es un ejercicio interesante que debería familiarizarte con algunos de los problemas que rodean a las corotinas.
\subsection{Corrutinas con tuberías bidireccionales}
Ahora que hemos visto cómo programar corotinas que no se comunican entre sí, construiremos sobre esa base y discutiremos cómo hacer que se comuniquen, de una manera más sofisticada que con una canalización. El shell Korn tiene un conjunto de características que permiten a los programadores establecer una comunicación bidireccional entre corotinas. Estas características no están incluidas en la mayoría de los shells Bourne.
Si inicias un proceso en segundo plano agregando |\& a un comando en lugar de \&, el shell Korn configura una tubería bidireccional especial entre el shell principal y el nuevo proceso en segundo plano. \texttt{read -p} en el shell principal lee una línea de la salida estándar del proceso en segundo plano; de manera similar, \texttt{print -p} en el shell principal alimenta la entrada estándar del proceso en segundo plano. La Figura \ref{fig:8-2} muestra cómo funciona esto.
\begin{figure}[h!]
\centering
\includegraphics[scale=0.6]{fig_10}
\caption{\small{E/S de las corrutinas}}
\label{fig:8-2}
\end{figure}
Este esquema tiene algunas posibilidades intrigantes. Observa lo siguiente: primero, el shell principal se comunica con el proceso en segundo plano de manera independiente de su propia entrada y salida estándar. En segundo lugar, el proceso en segundo plano no necesita tener idea de que un script de shell se está comunicando con él de esta manera. Esto significa que el proceso en segundo plano puede ser casi cualquier programa preexistente que use su entrada y salida estándar de manera normal.\footnote{Observa que \emph{sort(1)} no encaja del todo aquí. sort debe leer toda su entrada antes de poder generar cualquier salida. Todavía puedes usar sort en un coproceso, pero tendrías que cerrar el descriptor de archivo utilizado para escribir en el coproceso primero. La forma de hacer esto es mover el descriptor de archivo de entrada del coproceso a un descriptor de archivo numerado y luego cerrarlo. Ambas cosas involucran el comando \emph{exec}, que se trata en el próximo capítulo.}
La \hyperref[box:8-4]{Tarea 8-4} es una tarea que muestra un ejemplo sencillo.
\begin{mybox}[Tarea 8-4]\label{box:8-4}
Quieres tener una calculadora en línea, pero la utilidad Unix existente \emph{dc(1)} utiliza la Notación Polaca Inversa (RPN), al estilo de las calculadoras Hewlett-Packard. Preferirías tener una que funcione como el modelo de \$3.95 que recibiste con esa suscripción a una revista. Escribe un programa de calculadora que acepte notación algebraica estándar.
\end{mybox}
El objetivo aquí es escribir el programa sin volver a implementar el motor de cálculo que ya tiene \emph{dc}, en otras palabras, escribir un programa que traduzca la notación algebraica a RPN y pase la línea traducida a \emph{dc} para hacer el cálculo real. (La utilidad \emph{bc(1)} proporciona funcionalidad similar).
Supondremos que la función \emph{alg2rpn}, que realiza la traducción, ya existe: dada una línea de notación algebraica como argumento, imprime el equivalente en RPN en la salida estándar. Si tenemos esto, entonces el programa de la calculadora (que llamaremos \emph{adc}) es muy simple:
\begin{lstlisting}[language=bash]
dc |&
while read line'?adc> '; do
print -p "$(alg2rpn $line)"
read -p answer
print " = $answer"
done
\end{lstlisting}
La primera línea de este código inicia \emph{dc} como una corutina con una tubería bidireccional. Luego, el bucle \texttt{while} solicita al usuario una línea y la lee hasta que el usuario escribe CTRL-D para el final de la entrada. El cuerpo del bucle convierte la línea a RPN, la pasa a dc a través de la tubería, lee la respuesta de \emph{dc} e imprime después un signo igual. Por ejemplo:
\begin{lstlisting}[language=bash]
$ adc
adc> 2 + 3
= 5
adc> (7 * 8) + 54
= 110
adc> ^D
$
\end{lstlisting}
En realidad, como habrás notado, no es del todo necesario tener una tubería bidireccional con \emph{dc}. Podrías hacerlo con una tubería estándar y dejar que \emph{dc} haga su propia salida, así:
\begin{lstlisting}[language=bash]
{ while read line'?adc> '; do
print "$(alg2rpn $line)"
done
} | dc
\end{lstlisting}
La única diferencia con lo anterior es la falta del signo igual antes de imprimir cada respuesta.
¿Pero qué pasa si quisieras hacer una interfaz gráfica de usuario elegante (GUI), como el programa \emph{xcalc} que viene con muchas instalaciones del sistema X Window? Entonces, claramente, la salida propia de \emph{dc} no sería satisfactoria y necesitarías un control total de tu propia salida estándar en el proceso principal. La interfaz de usuario tendría que capturar la salida de \emph{dc} y mostrarla en la ventana de la calculadora de manera adecuada. La tubería bidireccional es una excelente solución a este problema: simplemente imagina que, en lugar de \texttt{print "\hspace{0.2cm} = \$answer"}, hay una llamada a una rutina que muestra la respuesta en la sección de <<lectura>> de la ventana de la calculadora.
Todo esto sugiere que el esquema de tubería bidireccional es excelente para escribir scripts de shell que interponen una capa de software entre el usuario (o algún otro programa) y un programa existente que utiliza entrada y salida estándar. En particular, es excelente para escribir nuevas interfaces para programas Unix estándar antiguos que esperan entrada y salida de usuario basada en caracteres, línea por línea. Las nuevas interfaces podrían ser GUI o podrían ser programas de interfaz de red que se comuniquen con usuarios a través de enlaces a máquinas remotas. En otras palabras, ¡la construcción de tuberías bidireccionales del shell Korn está diseñada para ayudar a desarrollar software muy actualizado!
\subsection{Tuberías Bidireccionales versus Tuberías Estándar}
Antes de abandonar el tema de las corotinas, completaremos el círculo mostrando cómo se compara la construcción de tuberías bidireccionales con las canalizaciones regulares. Como probablemente hayas podido deducir hasta ahora, es posible programar una canalización estándar usando |\& con \texttt{print -p}.
Esto tiene la ventaja de reservar la salida estándar del shell principal para otro uso. La desventaja es que la salida estándar del proceso secundario se dirige a la tubería bidireccional: si el proceso principal no la lee con \texttt{read -p}, se pierde efectivamente.
\section{Subprocesos Shell y Subshell}
Las corrutinas representan claramente la relación más compleja entre procesos que define el shell Korn. Para concluir este capítulo, veremos un tipo de relación entre procesos mucho más simple: la de un subproceso de shell con su shell padre. Vimos en el \hyperref[sec:Chapter3]{Capítulo 3} que cada vez que ejecuta un script de shell, en realidad invoca otra copia del shell que es un subproceso del proceso de shell principal o principal . Ahora veámoslos con más detalle.
\subsection{Herencia de subprocesos de shell}
Las cosas más importantes que debe saber sobre los subprocesos de shell son las características que obtienen o heredan de sus padres. Estos son los siguientes:
\begin{itemize}
\item El directorio actual
\item Variables de entorno
\item Entrada, salida y error estándar más cualquier otro descriptor de archivo abierto
\item Cualquier característica definida en el archivo de entorno (ver \hyperref[sec:Chapter3]{Capítulo 3}). Tenga en cuenta que solo los shells interactivos ejecutan el archivo de entorno
\item Señales que se ignoran
\end{itemize}
Las tres primeras características son heredadas por todos los subprocesos, mientras que las dos últimas son exclusivas de los subprocesos de shell. Igual de importantes son las cosas que un subproceso de shell no hereda de su padre:
\begin{itemize}
\item Variables de shell, excepto las variables de entorno y las definidas en el archivo de entorno
\item Manejo de señales que no se ignoran
\end{itemize}
Cubrimos algo de esto anteriormente (en el \hyperref[sec:Chapter3]{Capítulo 3}), pero estos puntos son fuentes comunes de confusión, por lo que vale la pena repetirlos.
\subsection{Subshells}
Un tipo especial de subproceso del shell es el subshell. El subshell se inicia dentro del mismo script (o función) que el padre. Lo haces de manera muy similar a los bloques de código que vimos en el último capítulo. Simplemente rodea un código de shell con paréntesis (en lugar de llaves), y ese código se ejecuta en un subshell.
Por ejemplo, aquí está el programa de la calculadora, de arriba, con un subshell en lugar de un bloque de código:
\begin{lstlisting}[language=bash]
( while read line'?adc> '; do
print "$(alg2rpn $line)"
done
) | dc
\end{lstlisting}
El código dentro de los paréntesis se ejecuta como un proceso separado.\footnote{Por razones de rendimiento, el shell Korn hace todo lo posible para evitar crear realmente un proceso separado para ejecutar el código entre paréntesis y dentro de \texttt{\$(...)}. Pero los resultados siempre deben ser los mismos que \emph{si} el código se ejecutara en un proceso separado.}
Esto suele ser menos eficiente que un bloque de código. Las diferencias en funcionalidad entre subshell y bloques de código son muy pocas; se refieren principalmente a problemas de ámbito, es decir, a los dominios en los que se conocen las definiciones de cosas como variables de shell y trampas de señales. En primer lugar, el código dentro de un subshell obedece a las reglas de la herencia del subproceso del shell mencionadas anteriormente, excepto que conoce las variables definidas en el shell circundante; en cambio, piensa en los bloques como unidades de código que heredan \emph{todo} del shell exterior. En segundo lugar, las variables y trampas definidas dentro de un bloque de código son conocidas por el código de shell después del bloque, mientras que las definidas en un subshell no lo son.
Por ejemplo, considera este código:
\begin{lstlisting}[language=bash]
{
fred=bob
trap 'print "You hit CTRL-C!"' INT
}
while true; do
print "\$fred is $fred"
sleep 60
done
\end{lstlisting}
Si ejecutas este código, verás el mensaje \texttt{\$fred is bob} cada 60 segundos, y si escribes CTRL-C, verás el mensaje, \texttt{You hit CTRL-C!}. Deberás escribir CTRL-\ para detenerlo (no olvides eliminar el archivo core). Ahora cambiémoslo a un subshell:
\begin{lstlisting}[language=bash]
(
fred=bob
trap 'print "You hit CTRL-C!"' INT
)
while true; do
print "\$fred is $fred"
sleep 60
done
\end{lstlisting}
Si ejecutas esto, verás el mensaje \texttt{\$fred is}; el shell exterior no conoce la definición de \emph{fred} del subshell y, por lo tanto, piensa que es nulo. Además, el shell exterior no conoce la trampa de la señal INT del subshell, así que si presionas CTRL-C, el script se termina.
Si un lenguaje admite el anidamiento de código, las definiciones dentro de una unidad anidada deberían tener un ámbito limitado a esa unidad anidada. En otras palabras, los subshell te brindan un mejor control que los bloques de código sobre el ámbito de las variables y las trampas de señales. Por lo tanto, creemos que debes usar subshell en lugar de bloques de código si van a contener definiciones de variables o trampas de señales, a menos que la eficiencia sea una preocupación.
Este ha sido un capítulo largo y ha cubierto mucho terreno. Aquí tienes algunos ejercicios que deberían ayudarte a asegurarte de que tienes un firme entendimiento del material. El último ejercicio es especialmente difícil para aquellos sin antecedentes en compiladores, teoría del análisis sintáctico o teoría formal de lenguajes.
\begin{enumerate}
\item Escribe una función llamada \emph{pinfo} que combine los comandos \emph{jobs} y \emph{ps} imprimiendo una lista de trabajos con sus números de trabajo, identificadores de proceso correspondientes, tiempos de ejecución y comandos completos. Puntuación extra: describe por qué esto debe ser una función y no un script.
\item Toma la última versión de nuestro script de shell del compilador C, o algún otro script de shell no trivial, y <<a prueba de balas>> con trampas de señales.
\item Vuelve a hacer el programa \emph{findterms} en el último capítulo usando un subshell en lugar de un bloque de código.
\item Lo siguiente no tiene mucho que ver con el material de este capítulo en sí, pero es un ejercicio de programación clásico y te dará práctica si lo haces:
\begin{enumerate}
\item Escribe la función \emph{alg2rpn} utilizada en \emph{adc}. Así es como debes hacerlo: las expresiones aritméticas en notación algebraica tienen la forma \emph{expr op expr}, donde cada \emph{expr} es o bien un número o bien otra expresión (quizás entre paréntesis), y \emph{op} es +, -, x, / o \% (resto). En RPN, las expresiones tienen la forma \emph{expr expr op}. Por ejemplo: la expresión algebraica $2+3$ es $2 3 +$ en RPN; el equivalente en RPN de $(2+3) x (9-5)$ es $2 3 + 9 5 - x$. La principal ventaja de RPN es que elimina la necesidad de paréntesis y reglas de precedencia de operadores (por ejemplo, la regla de que \emph{x} se evalúa antes que +). El programa \emph{dc} acepta RPN estándar, pero cada expresión debe tener <<p>> al final: esto le dice a \emph{dc} que imprima su resultado, por ejemplo, el primer ejemplo anterior debería darse a \emph{dc} como $2 3 + p$.
\item Necesitas escribir una rutina que convierta la notación algebraica en RPN. Esto debería ser (o incluir) una función que se llame a sí misma (conocida como una función \emph{recursiva}) cada vez que encuentra una subexpresión. Es especialmente importante que esta función lleve un registro de dónde está en la cadena de entrada y cuánto de la cadena consume durante su procesamiento. (Pista: utiliza los operadores de coincidencia de patrones discutidos en el \hyperref[sec:Chapter4]{Capítulo 4} para facilitar la tarea de analizar cadenas de entrada). \\
Para facilitarte la vida, no te preocupes por la precedencia de operadores por ahora; simplemente convierte a RPN de izquierda a derecha. Por ejemplo, trata $3+4x5$ como $(3+4)x5$ y $3x4+5$ como $(3x4)+5$. Esto hace posible que conviertas la cadena de entrada sobre la marcha, es decir, sin tener que leerla completa antes de hacer cualquier procesamiento.
\item Mejora tu solución al ejercicio anterior para que admita la precedencia de operadores en el orden usual: x, /, \% (resto) +, -. Por ejemplo, trata $3+4x5$ como $3+(4x5)$ y $3x4+5$ como $(3x4)+5$.
\end{enumerate}
\end{enumerate}

1067
Secciones/Capitulo9.tex Normal file

File diff suppressed because it is too large Load Diff

11
Secciones/Copyright.tex Normal file
View File

@ -0,0 +1,11 @@
Copyright © 2002, 1993 O'Reilly \& Associates, Inc. Todos los derechos reservados.
Impreso en los Estados Unidos de América.
Publicado por O'Reilly \& Associates, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
Los libros de O'Reilly \& Associates pueden adquirirse para uso educativo, comercial o promocional. También están disponibles ediciones en línea para la mayoría de los títulos (safari.oreilly.com). Para más información, póngase en contacto con nuestro departamento de ventas corporativas/institucionales: (800) 998-9938 o \emph{corporate@oreilly.com}.
Nutshell Handbook, el logotipo de Nutshell Handbook y el logotipo de O'Reilly son marcas registradas de O'Reilly \& Associates, Inc. Muchas de las denominaciones utilizadas por los fabricantes y vendedores para distinguir sus productos son reclamadas como marcas comerciales. En los casos en que esas denominaciones aparecen en este libro, y O'Reilly \& Associates, Inc. tenía conocimiento de una reclamación de marca, las denominaciones se han impreso en mayúsculas o con iniciales. La asociación entre la imagen de una tortuga de carey y el aprendizaje del caparazón de Korn es una marca comercial de O'Reilly \& Associates, Inc.
Aunque se han tomado todas las precauciones en la preparación de este libro, el editor y los autores no asumen ninguna responsabilidad por los errores u omisiones, ni por los daños resultantes del uso de la información aquí contenida.

View File

@ -0,0 +1,7 @@
\emph{A Margot Lorraine Rosenblatt, nacida el 16 de abril de 2001.}
--Bill Rosenblatt
\emph{A mi esposa, Miriam, por su amor y apoyo. A mis hijos, Chana, Rivka, Nachum y Malka.}
--Arnold Robbins

28
Secciones/Portada.tex Normal file
View File

@ -0,0 +1,28 @@
\begin{titlepage}
\AddToShipoutPictureBG*{%
\AtPageLowerLeft{%
\includegraphics[width=\paperwidth,height=\paperheight]{Portada}%
}%
}
\vspace{10cm}
\centering{
{\fontsize{50}{58}\selectfont
\textbf{Aprendiendo Korn Shell}
}
}\\
\vspace{10mm}
\centering{
\huge \textbf{Segunda Edición}
}
\vspace{15cm}
\begin{center}
\begin{tabular}{ccc}
\Huge \textbf{Autores} & \hspace{1.5cm} & \Huge \textbf{Traducción} \\
\LARGE \textbf{Arnold Robbins} & \hspace{1.5cm} & \LARGE \textbf{Omar Sánchez Enríquez} \\
\LARGE \textbf{Bill Rosenblatt} & &
\end{tabular}
\end{center}
\vfill
\huge \textbf{Diciembre \textcolor{white}{2023}}
\end{titlepage}

228
Secciones/Prefacio.tex Normal file
View File

@ -0,0 +1,228 @@
La larga y tortuosa historia del sistema operativo Unix ha dado lugar a sistemas con todo tipo de permutaciones y combinaciones de características. Esto significa que cada vez que te acercas a un sistema Unix desconocido, necesitas averiguar ciertas cosas sobre él para poder utilizarlo correctamente. E incluso en un sistema dado, puedes tener un número de opciones que puedes hacer sobre las características que quieres usar.
La decisión más importante, si es que la tienes que tomar, es qué shell usar. <<Shell>> es la jerga de Unix para el programa que le permite comunicarse con el ordenador introduciendo comandos y obteniendo respuestas. El shell está completamente separado del sistema operativo Unix per se; es sólo un programa que se ejecuta en Unix. En otros sistemas como MS-DOS, Microsoft Windows, Macintosh,\footnote{Al menos hasta MacOS X. MacOS X tiene un sistema similar a Unix en su núcleo: Darwin, que deriva de Mach y 4.4-BSD-Lite.} OpenVMS y VM/CMS, el intérprete de comandos o la interfaz de usuario es una parte integral del sistema operativo.
Hoy en día hay docenas de shells diferentes flotando por ahí, que van desde el estándar original, el shell de Bourne, hasta interfaces basadas en menús y gráficas. Los shells más importantes han sido el shell de Bourne, el shell C y el shell de Korn, el tema de este libro.
\subsection*{Versiones de Korn Shell}
En concreto, este libro describe la versión de 1993 del shell de Korn. La versión de 1993 se distribuye con las tres principales versiones comerciales de Unix: Solaris, HP-UX y AIX, aunque como parte del Entorno Común de Escritorio (CDE), en \texttt{/usr/dt/bin/dtksh}. La versión de 1988 también está disponible en los sistemas Unix modernos, normalmente como \texttt{/usr/bin/ksh}. Existen otras versiones, variaciones e implementaciones en otros sistemas operativos; éstas, junto con las diferencias entre las versiones de 1988 y 1993 del shell de Korn se describen en \hyperref[sec:ApendiceA]{Apéndice A}.
La versión de 1993 está ahora disponible tanto en forma de código fuente como de ejecutables precompilados para muchos sistemas comunes. En \hyperref[sec:ApendiceC]{Apéndice C} se describe la descarga y construcción de la misma. La última versión descargable de \emph{ksh} tiene una serie de características que no estaban en las versiones anteriores. También cubrimos todas esas características. Hemos hecho un esfuerzo para señalar cuando algo puede no estar en una versión anterior, pero caveat emptor; si necesita una característica y la versión de su sistema del shell de Korn 1993 no la tiene, puede necesitar descargar un ejecutable precompilado o descargar el código fuente y construir su propio ejecutable.
Para saber qué versión tienes, escribe el comando \emph{set -o emacs}, y luego pulsa CTRL-V. Deberías ver una fecha seguida de la letra de la versión (la letra no suele ser importante). Si es así, tienes una de las versiones oficiales, la de 1993, la de 1988 o una más antigua. Pero si no es así, tiene una versión no estándar, como pdksh, el shell de Korn de dominio público que se discute en el \hyperref[sec:ApendiceA]{Apéndice A}.
\subsection*{Resumen de las características del Shell de Korn}
El shell de Korn es el más avanzado de los shells que se distribuyen <<oficialmente>> con los sistemas Unix. Se trata de un sucesor evolutivo del shell Bourne, compatible con las versiones anteriores, que incluye la mayoría de las principales ventajas del shell C, así como una serie de nuevas características propias.
Entre las características apropiadas del intérprete de comandos C se incluyen:
\begin{description}
\item[Control de trabajo:] La capacidad de detener trabajos con CTRL-Z y moverlos al primer plano o al fondo con los comandos \emph{fg} y \emph{bg}.
\item[Alias:] La capacidad de definir nombres abreviados para comandos o líneas de comando.
\item[Funciones:] La capacidad de almacenar su propio código de shell en la memoria en lugar de archivos. Las funciones aumentan la programabilidad y la eficiencia. (Las funciones han sido comunes en el shell Bourne durante muchos años).
\item[Historial de comandos:] La capacidad de recordar comandos introducidos previamente. \\
\end{description}
Las principales características nuevas del Shell de Korn incluyen:
\begin{description}
\item[Edición de línea de comandos:] Esta característica le permite usar comandos de edición de estilo \emph{vi} o \emph{Emacs} en sus líneas de comando.
\item[Funciones de programación integradas:] La funcionalidad de varios comandos externos de Unix, incluidos \emph{test , expr , getopt} y \emph{echo}, se ha integrado en el propio shell, lo que permite que las tareas de programación comunes se realicen de manera más limpia y sin crear procesos adicionales.
\item[Estructuras de Control:] Las estructuras adicionales de control de flujo, especialmente la construcción de selección , permiten una fácil generación de menús.
\item[Primitivos de depuración:] Estas características hacen posible escribir herramientas que ayuden a los programadores a depurar su código de shell.
\item[Expresiones regulares:] Bien conocidas por los usuarios de utilidades de Unix como \emph{grep} y \emph{awk}, las expresiones regulares (aunque con una sintaxis diferente) se han agregado al conjunto estándar de comodines de nombre de archivo y a la función de variable de shell.
\item[Funciones de E/S avanzadas:] Varias instalaciones nuevas para el control de E/S de procesos, incluida la capacidad de realizar una comunicación bidireccional con procesos concurrentes (corrutinas) y conectarse a servicios de red.
\item[Nuevas opciones y variables:] Estas opciones y variables le brindan más formas de personalizar su entorno que los shells estándar de Unix.
\item[Mayor velocidad:] El shell de Korn a menudo ejecuta el mismo programa de shell considerablemente más rápido que el shell de Bourne.
\item[Características de seguridad:] Funciones diseñadas para ayudar a proteger contra <<caballos de Troya>> y otros tipos de esquemas de robo. \\
\end{description}
Las principales características nuevas de la versión de 1993 incluyen:
\begin{description}
\item[Conformidad con POSIX:] El cumplimiento de POSIX, un estándar internacional para la programación de shell portátil, hace posible escribir y utilizar scripts de shell portátiles.
\item[Aritmética para bucles:] Esta nueva estructura de control le permite programar de forma más natural cuando se repite un número fijo de veces.
\item[Aritmética de punto flotante:] La capacidad de usar números de coma flotante y las nuevas funciones aritméticas integradas enriquecen el shell como lenguaje de programación.
\item[Nombres de variables estructuradas:] La nueva sintaxis para los nombres de variables proporciona funciones similares a las estructuras C y los registros Ada para agrupar elementos relacionados en una variable.
\item[Referencias a variables indirectas:] Esta facilidad facilita la programación de funciones de shell para manipular variables globales.
\item[matrices asociativas:] Una potente función de gestión de datos que es similar a las de \emph{awk} o \emph{perl}.
\item[Facilidades adicionales de manipulación de texto:] Incluso hay más formas de hacer coincidir patrones y sustituir variables.
\item[Más comandos integrados:] Los comandos adicionales mejoran la eficiencia y aumentan la portabilidad de los scripts.
\end{description}
\subsection*{Público Objetivo}
Este libro está diseñado para atraer a los usuarios ocasionales de Unix que están por encima del nivel de <<principiante>>. Deberías estar familiarizado con el proceso de iniciar sesión, introducir comandos y hacer cosas sencillas con los archivos. Aunque el \hyperref[sec:Chapter1]{Capítulo 1} repasa conceptos como el esquema de archivos y directorios en forma de árbol, puede que te parezca que avanza demasiado rápido si eres un completo neófito. En ese caso, le recomendamos el libro \emph{Learning the Unix Operating System}, de Jerry Peek, Grace Todino y John Strang, publicado por O'Reilly \& Associates, Inc.
Si eres un usuario experimentado, puedes saltarte el \hyperref[sec:Chapter1]{Capítulo 1}. Pero si su experiencia es con el shell C, puede encontrar que el \hyperref[sec:Chapter1]{Capítulo 1} revela algunas diferencias sutiles entre los shells Korn y C.
No importa cuál sea su nivel de experiencia, indudablemente aprenderá muchas cosas en este libro que le harán un usuario más productivo del shell de Korn -- desde las características más importantes hasta los detalles a nivel de <<novato>> de los que no era consciente.
Si está interesado en la programación del shell (escribiendo scripts y funciones del shell que automatizan las tareas cotidianas o sirven como utilidades del sistema), también debería encontrar este libro útil. Sin embargo, hemos evitado deliberadamente hacer una fuerte distinción entre el uso interactivo del shell (introducir comandos durante una sesión de inicio de sesión) y la programación del shell. Consideramos que la programación del shell es una consecuencia natural e inevitable del aumento de la experiencia como usuario.
En consecuencia, cada capítulo depende de los anteriores, y aunque los tres primeros capítulos están orientados únicamente al uso interactivo, los siguientes describen características interactivas orientadas al usuario además de conceptos de programación.
De hecho, si este libro tiene un mensaje primordial, es éste: <<El shell de Korn es un entorno de programación Unix increíblemente potente y muy infravalorado. Usted -sí, usted- puede escribir programas de shell útiles, incluso si acaba de aprender a conectarse la semana pasada y nunca ha programado antes>>.
Con este fin, hemos decidido no dedicar mucho tiempo a las características que interesan exclusivamente a los programadores de sistemas de bajo nivel. Conceptos como descriptores de archivos, tipos de archivos especiales, etc., sólo pueden confundir al usuario casual, y de todos modos, pensamos que aquellos de ustedes que entienden esas cosas son lo suficientemente inteligentes como para extrapolar la información necesaria de nuestras discusiones superficiales.
\subsection*{Ejemplos de Código}
Este libro está lleno de ejemplos de comandos y programas del shell que están diseñados para ser útiles en tu vida diaria como usuario, no sólo para ilustrar la función que se explica. A partir del \hyperref[sec:Chapter4]{Capítulo 4}, incluimos varios problemas de programación, que llamamos tareas, que ilustran conceptos particulares de programación del shell. Algunas tareas tienen soluciones que se perfeccionan en capítulos posteriores. Los capítulos posteriores también incluyen ejercicios de programación, muchos de los cuales se basan en las tareas del capítulo.
Puedes usar cualquier código que veas en este libro y pasarlo a tus amigos y colegas. Le animamos especialmente a que lo modifique y mejore usted mismo. El código está disponible en el sitio web de este libro: \url {http://www.oreilly.com/catalog/korn2/}.
Si desea probar ejemplos pero no usa el shell de Korn como su shell de inicio de sesión, debe colocar la siguiente línea en la parte superior de cada script de shell:
\begin{lstlisting}[language=bash]
#!/bin/ksh
\end{lstlisting}
Si su shell de Korn no está instalado como el archivo \emph{/bin/ksh}, o si \emph{/bin/ksh} es la versión de 1988, sustituya el nombre de ruta completo por su versión de \emph{ksh93} en lo anterior.
\subsection*{Resumen del Capítulo}
Si desea investigar temas específicos en lugar de leer todo el libro, aquí hay un resumen capítulo por capítulo:
\begin{enumerate}
\item \hyperref[sec:Chapter1]{Capítulo 1} \\
Presenta el shell de Korn y le indica cómo instalarlo como su shell de inicio de sesión. Luego presenta los conceptos básicos del uso de shell interactivo, incluidas las descripciones generales del esquema de archivos y directorios de Unix, E/S estándar y trabajos en segundo plano.
\item \hyperref[sec:Chapter2]{Capítulo 2} \\
Describe el mecanismo de historial de comandos del shell, incluidos los modos de edición de emacs y vi y el comando \emph{hist} history.
\item \hyperref[sec:Chapter3]{Capítulo 3} \\
Cubre formas de personalizar su entorno de shell sin programación, mediante el uso de los archivos \emph{.profile} y de entorno. Los alias, las opciones y las variables de shell son las técnicas de personalización discutidas.
\item \hyperref[sec:Chapter4]{Capítulo 4} \\
Introduce la programación de shell. Este capítulo explica los conceptos básicos de las funciones y los scripts de shell, y analiza varias funciones de programación importantes: operadores de manipulación de cadenas, expresiones regulares, argumentos de línea de comandos (parámetros posicionales) y sustitución de comandos.
\item \hyperref[sec:Chapter5]{Capítulo 5} \\
Continúa la discusión de la programación del shell describiendo el estado de salida del comando, las expresiones condicionales y las estructuras de control de flujo del shell: \emph{if , for , case , select , while} y \emph{till}.
\item \hyperref[sec:Chapter6]{Capítulo 6} \\
Profundiza en los parámetros posicionales y el procesamiento de opciones de la línea de comandos, luego analiza los tipos especiales y las propiedades de las variables, como la aritmética de enteros y de coma flotante, la versión aritmética del bucle \emph{for}, las matrices indexadas y asociativas y el comando \emph{typeset}.
\item \hyperref[sec:Chapter7]{Capítulo 7} \\
Proporciona una descripción detallada de la E/S del shell de Korn, completando la información omitida en el Capítulo 1 . Todos los redirectores de E/S del shell están cubiertos, junto con la capacidad del shell para realizar conexiones de socket TCP/IP y los comandos de E/S de línea a la vez \emph{read , print} e \emph{printf}. Luego, el capítulo analiza el mecanismo de procesamiento de la línea de comandos del shell y el comando \emph{eval}.
\item \hyperref[sec:Chapter8]{Capítulo 8} \\
Cubre los problemas relacionados con el proceso en detalle. Comienza con una discusión sobre el control del trabajo y luego pasa a información de bajo nivel sobre los procesos, incluidos los ID de procesos, las señales y las trampas. Luego, el capítulo pasa a un nivel más alto de abstracción para analizar corrutinas, conductos bidireccionales y subcapas.
\item \hyperref[sec:Chapter9]{Capítulo 9} \\
Describe varias técnicas de depuración, comenzando con las simples como los modos de rastreo y detallado y las trampas de <<señal falsa>>. A continuación, este capítulo describe las funciones de disciplina. Finalmente, presenta \emph{kshdb}, una herramienta de depuración de shell de Korn que puede usar para depurar su propio código.
\item \hyperref[sec:Chapter10]{Capítulo 10} \\
Brinda información para administradores de sistemas, incluidas técnicas para implementar la personalización de shell en todo el sistema, personalizar los editores integrados y funciones relacionadas con la seguridad del sistema.
\item \hyperref[sec:ApendiceA]{Apéndice A} \\
Compara el shell de Korn de 1993 con varios shells similares, incluido el shell SVR4 Bourne estándar, el shell de Korn de 1988, el shell estándar IEEE 1003.2 POSIX, el shell CDE Desk Top Korn (\emph{dtksh}), \emph{tksh} (que combina Tcl/Tk con \emph{ksh} ), el shell de Korn de dominio público (|emph{pdksh}), el |emph{bash} de la Free Software Foundation , el shell Z (|emph{zsh}) y varios shells de estilo Bourne (realmente entornos de emulación de Unix) para Microsoft Windows.
\item \hyperref[sec:ApendiceB]{Apéndice B} \\
Contiene listas de opciones de invocación de shell, comandos integrados, alias predefinidos, variables integradas, operadores de prueba condicional, opciones de \emph{comando de configuración, opciones de comando de composición tipográfica} y comandos de modo de edición de emacs y vi. Este apéndice también cubre los detalles completos para usar el comando integrado \emph{getopts}.
\item \hyperref[sec:ApendiceC]{Apéndice C} \\
Describe cómo descargar el código fuente de \emph{ksh93} y crear un ejecutable que funcione. Este apéndice también cubre la descarga de ejecutables preconstruidos para varios sistemas diferentes.
\item \hyperref[sec:ApendiceD]{Apéndice D} \\
Presenta los términos de licencia para el código fuente \emph{ksh93}.
\end{enumerate}
\subsection*{Convenciones Utilizadas en este Manual}
Dejamos entendido que, cuando se introduce un comando del shell, se pulsa ENTER al final. ENTER está etiquetado como RETURN en algunos teclados.
Los caracteres llamados CTRL-X, donde X es cualquier letra, se introducen manteniendo pulsada la tecla CTRL (o CTL, o CONTROL) y pulsando esa letra. Aunque demos la letra en mayúscula, se puede pulsar la letra sin la tecla SHIFT.
Otros caracteres especiales son la nueva línea (que es lo mismo que CTRL-J), RETROCESO (lo mismo que CTRL-H), ESC, TAB y DEL (a veces etiquetado como DELETE o RUBOUT).
Este libro utiliza las siguientes convenciones tipográficas:
\begin{itemize}
\item \emph{Cursiva} \\
Se utiliza cuando se habla de nombres de archivos Unix, comandos externos e incorporados, nombres de alias, opciones de comandos, opciones del shell y funciones del shell. La cursiva también se utiliza en el texto cuando se discuten parámetros ficticios que deben ser reemplazados por un valor real, para distinguir los programas vi y emacs de sus modos Korn-shell, y para resaltar términos especiales la primera vez que se definen.
\item \texttt{Anchura constante} \\
Se utiliza para los nombres de las variables y las palabras clave del shell, los sufijos de los nombres de archivo y en los ejemplos para mostrar el contenido de los archivos o la salida de los comandos, así como para las líneas de comando cuando están dentro de un texto normal.
\item \textbf{\texttt{Negrita de ancho constante}} \\
Se utiliza en los ejemplos para mostrar la interacción entre el usuario y el shell; cualquier texto que el usuario escriba se muestra en \textbf{negrita de ancho constante}. Por ejemplo:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (* \textbf{pwd} *)
/home/billr/ora/kb
$
\end{lstlisting}
\item \emph{\texttt{Cursiva de ancho constante}} \\
Se utiliza en el texto y en las líneas de comando de ejemplo para los parámetros ficticios que deben sustituirse por un valor real. Por ejemplo:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
$ (* \textbf{cd} \emph{directorio} *)
\end{lstlisting}
\item \emph{Vídeo inverso} \\
Se utiliza en el \hyperref[sec:Chapter2]{Capítulo 2} para mostrar la posición del cursor en la línea de comandos que se está editando. Por ejemplo:
\begin{lstlisting}[language=bash, escapeinside={(*}{*)}]]
grep -l Bob < ~pete/wk/(*\highlight{n}*)ombres
\end{lstlisting}
\item \textbf{NOTA} Indica un consejo, sugerencia o nota general.
\item \textbf{ADVERTENCIA} Indica una advertencia o precaución. \\
\end{itemize}
Los comandos de utilidad estándar de Unix se mencionan a veces con un número entre paréntesis (normalmente 1) después del nombre del comando. El número se refiere a la sección del Manual del Usuario de Unix en la que encontrará la documentación de referencia (también conocida como <<la página man>>) sobre la utilidad en cuestión. Por ejemplo, \emph{grep}(1) significa la página del manual para \emph{grep} en la Sección 1.
Cuando hay una diferencia importante entre las versiones de 1988 y 1993 del shell de Korn, nos referimos a ellas como \emph{ksh88} y \emph{ksh93} respectivamente. La mayor parte de este libro se aplica a todas las versiones del shell de Korn de 1993. Cuando necesitemos distinguir entre diferentes versiones del shell de Korn de 1993, añadiremos la versión menor al nombre, como \emph{ksh93h}, o \emph{ksh93l+}.
\subsection*{Acerca de la Segunda Edición}
La primera edición de este libro cubría la versión de 1988 del shell de Korn. Poco después de su publicación, David Korn lanzó la versión de 1993, que incluía compatibilidad con el estándar de shell POSIX 1003.2, así como muchas nuevas características.
Aunque \emph{ksh93} ha tardado en difundirse en el mundo comercial de Unix, el código fuente está ahora disponible, por lo que cualquiera que quiera una copia de la última y mejor versión de \emph{ksh93} no tiene más que descargar el código fuente y compilarlo. Teniendo esto en cuenta, hemos hecho de \emph{ksh93} el centro de la segunda edición, con un resumen de las diferencias disponible en un apéndice. Esta edición cubre la versión más reciente de \emph{ksh93} disponible en el momento de escribir este artículo, que incluye algunas características significativas que no se encuentran en las versiones anteriores.
La estructura básica y el flujo del libro siguen siendo los mismos, aunque hemos corregido varios errores y erratas de la primera edición y hemos actualizado kshdb, el depurador de Korn Shell, para que funcione con \emph{ksh93}. El \hyperref[sec:ApendiceA]{Apéndice A} incluye ahora más información sobre los programas similares al shell de Korn, tanto para sistemas Unix como Windows.
También se incluye en esta edición una tarjeta de referencia que cubre muchas de las características de \emph{ksh93} descritas en este libro. Esta tarjeta tiene el copyright de Specialized Systems Consultants, Inc. (SSC), y se reimprime con permiso. SSC vende una versión de la tarjeta en cuatro colores, 26 paneles, de 3,5 por 8,5 pulgadas, que cubre tanto \emph{ksh88} como \emph{ksh93} con mucho más detalle. Para más información, consulte \url{http://www.ssc.com}.
\subsection*{Nos Gustaría Saber de Usted}
Por favor, dirija sus comentarios y preguntas sobre este libro al editor:
\begin{spacing}{0}
\begin{scriptsize}
O'Reilly \& Associates, Inc. \\
1005 Gravenstein Highway North \\
Sebastopol, CA 95472 \\
(800) 998-9938 (en Estados Unidos o Canadá) \\
(707) 829-0515 (internacional o local) \\
(707) 829-0104 (fax) \\
\end{scriptsize}
\end{spacing}
\vspace{0.3cm}
Disponemos de una página web para este libro, en la que aparecen ejemplos, erratas o cualquier información adicional. Puede acceder a esta página en:
\url{http://www.oreilly.com/catalog/korn2/}
Para comentar o hacer preguntas técnicas sobre este libro, envíe un correo electrónico a:
bookquestions@oreilly.com
Para obtener más información sobre nuestros libros, conferencias, centros de recursos y la red O'Reilly, consulte nuestro sitio web en:
\url{http://www.oreilly.com}
\subsection*{Agradecimientos}
Escribir un libro desde cero no es fácil. Actualizar un libro es aún más difícil; el truco consiste en hacer imposible (o al menos difícil) que el lector sepa qué autor escribió cada parte. Espero haberlo conseguido. Quiero dar las gracias a Bill Rosenblatt por haber escrito la primera edición y haberme proporcionado un excelente material con el que trabajar. Es uno de los mejores libros de O'Reilly que he leído, y ha sido un placer trabajar con él.
Me gustaría dar las gracias (en orden alfabético) a Nelson A. Beebe (Departamento de Matemáticas de la Universidad de Utah), al Dr. David G. Korn (AT\&T Research), a Chet Ramey (mantenedor de bash), a Bill Rosenblatt (GiantSteps/Media Technology Strategies) y al Dr. Eugene H. Spafford (Departamento de Ciencias de la Computación de la Universidad de Purdue) por haber revisado el libro y haber aportado muchos comentarios útiles. Mike Loukides, el editor del libro, fue muy paciente conmigo durante varios retrasos en la actualización. David Chu, del equipo editorial de O'Reilly, hizo un gran trabajo asegurándose de que muchas de las partes <<tuercas y tornillos>> del proyecto se hicieran, por lo que estoy agradecido.
David Korn, ahora de AT\&T Research Laboratories, y autor del shell de Korn, respondió a varias preguntas y proporcionó acceso temprano a la documentación de ksh93l, lo que ayudó considerablemente, así como acceso previo a la liberación de ksh93n. Glenn Fowler, también de AT\&T Research, me ayudó con problemas de compilación bajo GNU/Linux, así como a entender algunos de los puntos más finos del uso de ksh. Steve Alston aportó algunas mejoras al depurador kshdb en el \hyperref[sec:Chapter9]{Capítulo 9}. George Kraft IV proporcionó información útil sobre dtksh para el \hyperref[sec:ApendiceA]{Apéndice A}. Glenn Barry, de Sun Microsystems, proporcionó información sobre zsh para el \hyperref[sec:ApendiceA]{Apéndice A}.
Gracias a Phil Hughes, presidente de SSC, por el permiso para reimprimir partes de su tarjeta de referencia de ksh.
Otros miembros del personal de O'Reilly también han contribuido al libro: Leanne Soylemez fue la editora de producción y correctora; Mary Brady y Jane Ellin proporcionaron un control de calidad adicional; Brenda Miller escribió el índice.
Por último, gracias a mi maravillosa esposa Miriam, por no reclamar la atención que le correspondía en demasiadas noches mientras trabajaba en este libro. Sin ella, no valdría la pena hacer nada.
\newpage
\begin{spacing}{0}
\begin{scriptsize}
Arnold Robbins \\
Nof Ayalon \\
ISRAEL \\
\end{scriptsize}
\end{spacing}
\vspace{0.3cm}
\subsection*{Agradecimientos de la Primera Edición}
Muchas personas han contribuido a este libro de muchas maneras. Me gustaría dar las gracias a las siguientes personas por su asesoramiento y asistencia técnica: por la ayuda en la administración del sistema, John van Vlaanderen y Alexis Rosen. Por la información sobre shells alternativas, John (de nuevo), Sean Wilson (de MKS), Ed Ravin, Mel Rappaport y Chet Ramey. Por identificar la necesidad de un depurador de shell, la experiencia en SunOS y la seguridad del sistema y, de hecho, una parte importante de mi carrera, Hal Stern. Por las sugerencias de depuración, Tan Bronson. Por la ayuda humanitaria, Jessica Lustig. Y muchas gracias a David Korn por todo tipo de información de <<boca de caballo>> - y, por supuesto, por el propio shell de Korn.
Gracias a nuestros revisores técnicos: Jim Baumbach, Jim Falk, David Korn, Ed Miner, Eric Pearce y Ed Ravin. Aprecio especialmente la cooperación de Ed y Ed (en ese orden) durante mi fase de <<¡¿Qué quieres decir con que no funciona?!>>.
Varias personas de O'Reilly \& Associates contribuyeron a este esfuerzo: Gigi Estabrook y Clairemarie Fisher O'Leary corrigieron múltiples borradores del manuscrito, Kismet McDonough y Donna Woonteiler corrigieron el manuscrito, Len Muellner implementó el macropaquete de diseño del libro, Jennifer Niederst diseñó la portada y el formato del libro, y Chris Reilley creó las figuras. Por último, un océano de gratitud para Mike Loukides: editor, motivador, facilitador, constructivo y constante voz de la razón. Él y el resto de la gente de O'Reilly \& Associates son algunas de las personas más innovadoras, interesantes y motivadas con las que he tenido el privilegio de trabajar.
--Bill Rosenblatt