Autor(es) original(es) | Bill Joy |
---|---|
Lanzamiento inicial | 1978 ( 1978 ) |
Versión estable | 6.20.00 / 24 de noviembre de 2016 ( 24 de noviembre de 2016 ) [1] |
Repositorio |
|
Escrito en | do |
Sistema operativo | BSD , UNIX , UNOS , Linux , macOS |
Tipo | Concha de Unix |
Licencia | Licencia BSD |
El C shell ( csh o la versión mejorada, tcsh ) es un shell de Unix creado por Bill Joy mientras era estudiante de posgrado en la Universidad de California, Berkeley a fines de la década de 1970. Se ha distribuido ampliamente, comenzando con la versión 2BSD de Berkeley Software Distribution (BSD) que Joy distribuyó por primera vez en 1978. [2] [3] Otros contribuyentes tempranos a las ideas o al código fueron Michael Ubell, Eric Allman , Mike O'Brien y Jim Kulp. [4]
El C shell es un procesador de comandos que normalmente se ejecuta en una ventana de texto, lo que permite al usuario escribir y ejecutar comandos. El C shell también puede leer comandos de un archivo, llamado script . Como todos los shells de Unix, admite comodines en los nombres de archivo , canalización , documentos aquí , sustitución de comandos , variables y estructuras de control para pruebas de condición e iteración . Lo que diferenciaba al C shell de otros, especialmente en la década de 1980, eran sus características interactivas y su estilo general. Sus nuevas características lo hicieron más fácil y rápido de usar. El estilo general del lenguaje se parecía más a C y se consideraba más legible.
En muchos sistemas, como macOS y Red Hat Linux , csh es en realidad tcsh , una versión mejorada de csh. A menudo, uno de los dos archivos es un enlace físico o un enlace simbólico al otro, de modo que cualquiera de los dos nombres hace referencia a la misma versión mejorada del shell C. El código fuente y el binario originales de csh son parte de NetBSD .
En Debian y algunos derivados (incluido Ubuntu ), existen dos paquetes diferentes: csh y tcsh. El primero se basa en la versión BSD original de csh [5] [6] y el segundo es la versión mejorada de tcsh. [7] [8]
tcsh agregó nombres de archivo, finalización de comandos y conceptos de edición de línea de comandos tomados del sistema Tenex , que es la fuente de la "t". [9] Debido a que solo agregó funcionalidad y no cambió lo que ya existía, tcsh siguió siendo compatible con versiones anteriores [10] con el C shell original. Aunque comenzó como una rama lateral del árbol de código fuente original que había creado Joy, tcsh es ahora la rama principal para el desarrollo continuo. tcsh es muy estable, pero continúan apareciendo nuevas versiones aproximadamente una vez al año, que consisten principalmente en correcciones de errores menores. [11]
Los principales objetivos de diseño para el C shell eran que se pareciera más al lenguaje de programación C y que fuera mejor para el uso interactivo.
El sistema Unix había sido escrito casi exclusivamente en C, por lo que el primer objetivo del shell C era crear un lenguaje de comandos que fuera más coherente estilísticamente con el resto del sistema. Las palabras clave, el uso de paréntesis, la gramática de expresiones integrada del shell C y el soporte para matrices estaban fuertemente influenciados por C.
Según los estándares actuales, C shell puede no parecer mucho más parecido a C que muchos otros lenguajes de programación populares. Pero durante los años 80 y 90, la diferencia se veía como sorprendente, en particular cuando se comparaba con Bourne shell (también conocido como sh ), el shell dominante en ese momento escrito por Stephen Bourne en Bell Labs . Este ejemplo ilustra los operadores de expresión y la sintaxis más convencionales de C shell .
Concha Bourne
#!/bin/sh if [ $days -gt 365 ] then echo Esto es más de un año. fi
C-shell (concha C)
#!/bin/csh if ( $days > 365 ) then echo Esto es más de un año. endif
El sh de Bourne carecía de una gramática de expresión . La condición entre corchetes debía evaluarse mediante el medio más lento de ejecutar el programa de prueba externo . El comando de sh if
tomaba sus palabras de argumento como un nuevo comando que se ejecutaba como un proceso secundario . Si el proceso secundario salía con un código de retorno cero , sh buscaría una then
cláusula (una declaración separada, pero a menudo escrita unida en la misma línea con un punto y coma) y ejecutaría ese bloque anidado. De lo contrario, ejecutaría el else. La vinculación rígida del programa de prueba como " test
" y " [
" dio la ventaja de notación de los corchetes y la apariencia de que la funcionalidad de test era parte del lenguaje sh. El uso de sh de una palabra clave invertida para marcar el final de un bloque de control era un estilo tomado de ALGOL 68. [ 12]
En cambio, csh podía evaluar la expresión directamente, lo que la hacía más rápida. También afirmaba que era más legible: sus expresiones usaban una gramática y un conjunto de operadores copiados en su mayoría de C, ninguna de sus palabras clave estaba invertida y el estilo general también era más parecido al de C.
A continuación se muestra un segundo ejemplo, comparando scripts que calculan las primeras 10 potencias de 2.
Concha Bourne
#!/bin/sh i = 2 j = 1 mientras [ $j -le 10 ] hacer echo '2 **' $j = $i i = ` expr $i '*' 2 ` j = ` expr $j + 1 ` hecho
C-shell (concha C)
#!/bin/csh establecer i = 2 establecer j = 1 mientras ( $j < = 10 ) echo '2 **' $j = $i @ i * = 2 @j++fin
Nuevamente, debido a la falta de una gramática de expresión, el script sh utiliza la sustitución de comandos y el comando expr . ( El shell POSIX moderno tiene una gramática de este tipo: la declaración podría escribirse i=$((i * 2))
o : "$((i *= 2))"
.)
Finalmente, aquí hay un tercer ejemplo, que muestra los diferentes estilos para una declaración switch .
Concha Bourne
#!/bin/sh para i en d* hacer caso $i en d? ) echo $i es corto ;; * ) echo $i es largo ;; esac hecho
C-shell (concha C)
#!/bin/csh foreach i ( d* ) switch ( $i ) case d?: echo $i es corto breaksw predeterminado : echo $i es largo endsw fin
En el script sh, " ;;
" marca el final de cada caso porque en caso contrario sh no permite declaraciones nulas.
El segundo objetivo era mejorar el uso interactivo del C shell. Introdujo numerosas características nuevas que lo hicieron más fácil, rápido y amigable de usar al escribir comandos en una terminal. Los usuarios podían hacer cosas con muchas menos pulsaciones de teclas y funcionaba más rápido. Las más significativas de estas nuevas características eran los mecanismos de historial y edición, los alias, las pilas de directorios, la notación de tilde, cdpath, el control de trabajos y el hash de rutas. Estas nuevas características resultaron muy populares y muchas de ellas han sido copiadas desde entonces por otros shells de Unix.
El historial permite a los usuarios recordar comandos anteriores y volver a ejecutarlos con solo pulsar unas pocas teclas. Por ejemplo, si se pulsan dos signos de exclamación (" !!
") [13] como comando, se ejecuta el comando inmediatamente anterior. Otras combinaciones de teclas cortas, por ejemplo, " !$
" (que significa "el argumento final del comando anterior"), permiten pegar fragmentos de comandos anteriores y editarlos para formar un nuevo comando.
La edición se puede realizar no solo en el texto de un comando anterior, sino también en sustituciones de variables. Los operadores varían desde la simple búsqueda y reemplazo de cadenas hasta el análisis de una ruta para extraer un segmento específico.
Los alias permiten al usuario escribir el nombre de un alias y hacer que el shell C lo expanda internamente en cualquier conjunto de palabras que el usuario haya definido. En muchas situaciones simples, los alias se ejecutan más rápido y son más convenientes que los scripts.
La pila de directorios permite al usuario insertar o extraer el directorio de trabajo actual , lo que hace más fácil saltar de un lugar a otro del sistema de archivos.
La notación tilde ofrece una forma abreviada de especificar rutas relativas al directorio de inicio utilizando el ~
carácter " ".
La tecla Escape se puede utilizar de forma interactiva para mostrar posibles finalizaciones de un nombre de archivo al final de la línea de comando actual.
Cdpath extiende la noción de ruta de búsqueda al cd
comando (cambiar directorio): si el directorio especificado no está en el directorio actual , csh intentará encontrarlo en los directorios cdpath.
Hasta bien entrada la década de 1980, la mayoría de los usuarios sólo tenían terminales simples en modo carácter que impedían la presencia de varias ventanas, por lo que sólo podían trabajar en una tarea a la vez. El control de tareas del shell C permitía al usuario suspender la actividad actual y crear una nueva instancia del shell C, llamada tarea, escribiendo ^Z
. El usuario podía entonces alternar entre tareas utilizando el fgcomando . Se decía que la tarea activa estaba en primer plano. Se decía que otras tareas estaban suspendidas (detenidas) o ejecutándose en segundo plano .
El hash de rutas acelera la búsqueda de archivos ejecutables por parte del shell C. En lugar de realizar una llamada al sistema de archivos en cada directorio de ruta, uno a la vez, hasta que encuentra el archivo o se queda sin posibilidades, el shell C consulta una tabla hash interna creada mediante el escaneo de los directorios de ruta. Esa tabla generalmente puede indicarle al shell C dónde encontrar el archivo (si existe) sin tener que buscar y se puede actualizar con el rehash
comando.
El shell C opera con una línea a la vez. Cada línea se convierte en un conjunto de palabras separadas por espacios u otros caracteres con un significado especial, incluidos paréntesis, operadores de canalización y de redirección de entrada/salida, punto y coma y símbolos &.
Una sentencia básica es aquella que simplemente ejecuta un comando. La primera palabra se toma como nombre del comando que se va a ejecutar y puede ser un comando interno, por ejemplo, echo
o un comando externo. El resto de las palabras se pasan como argumentos al comando.
A nivel de enunciado básico, aquí se presentan algunas de las características de la gramática:
El shell C, como todos los shells de Unix, trata cualquier argumento de línea de comandos que contenga caracteres comodín como un patrón y lo reemplaza con la lista de todos los nombres de archivo que coinciden (ver globbing ).
*
coincide con cualquier número de caracteres.?
coincide con cualquier caracter individual.[
... ]
coincide con cualquiera de los caracteres dentro de los corchetes. Se permiten rangos, utilizando el guion.[^
... ]
coincide con cualquier carácter que no esté en el conjunto.El shell C también introdujo varias conveniencias de notación (a veces conocidas como globbing extendido ), que luego fueron copiadas por otros shells de Unix.
abc{def,ghi}
es alternancia (también conocida como expansión de llaves ) y se expande a abcdef abcghi .~
significa el directorio de inicio del usuario actual.~user
significa el directorio de inicio del usuario .Se admiten varios comodines a nivel de directorio, por ejemplo, " */*.c
".
Desde la versión 6.17.01, el uso de comodines recursivos como zsh (por ejemplo, " **/*.c
" o " ***/*.html
") también se admite con la globstar
opción.
Darle al shell la responsabilidad de interpretar los comodines fue una decisión importante en Unix. Significaba que los comodines funcionarían con cada comando, y siempre de la misma manera. Sin embargo, la decisión dependía de la capacidad de Unix de pasar largas listas de argumentos de manera eficiente a través de la llamada al sistema exec que csh usa para ejecutar comandos. Por el contrario, en Windows , la interpretación de comodines la realiza convencionalmente cada aplicación. Esto es un legado de MS-DOS, que solo permitía pasar una línea de comando de 128 bytes a una aplicación, lo que hacía que el uso de comodines por parte del símbolo del sistema de DOS fuera poco práctico. Aunque el Windows moderno puede pasar líneas de comando de hasta aproximadamente 32K caracteres Unicode , la carga de la interpretación de comodines recae en la aplicación.
De manera predeterminada, cuando csh ejecuta un comando, el comando hereda los controladores de archivo stdio de csh para stdin , stdout y stderr , que normalmente apuntan a la ventana de la consola donde se ejecuta el shell C. Los operadores de redirección de E/S permiten que el comando utilice un archivo en su lugar para la entrada o la salida.
>
archivo significa que la salida estándar se escribirá en el archivo , sobrescribiéndolo si existe y creándolo si no existe. Los errores siguen apareciendo en la ventana del shell.>&
archivo significa que tanto stdout como stderr se escribirán en el archivo , sobrescribiéndolo si existe y creándolo si no existe.>>
archivo significa que stdout se agregará al final del archivo .>>&
archivo significa que tanto stdout como stderr se agregarán al final del archivo .<
archivo significa que la entrada estándar se leerá desde el archivo .<<
string es un documento here . Stdin leerá las siguientes líneas hasta la que coincida con string .Redirigir stderr por sí solo no es posible sin la ayuda de un sub-shell.
establecer filtro = "$home" '/filter'
mkfifo ” $filter " cat " $filter " & ( ( ls /root/ || echo Sin acceso. ) > " $filter " ) >& /dev/null
Los sistemas que admiten descriptores de archivos como archivos pueden utilizar la siguiente solución alternativa.
( ( ( echo ok ; '' ) > /dev/fd/0 ) >& /dev/null < /dev/fd/1 ) | ( echo "$<" adiós )
Los comandos se pueden unir en la misma línea.
;
significa ejecutar el primer comando y luego el siguiente.&&
significa ejecutar el primer comando y, si tiene éxito con un código de retorno 0 , ejecutar el siguiente.||
significa ejecutar el primer comando y, si falla con un código de retorno distinto de cero, ejecutar el siguiente.Los comandos se pueden conectar mediante una tubería, lo que hace que la salida de un comando se transfiera a la entrada del siguiente. Ambos comandos se ejecutan simultáneamente .
|
Significa conectar la salida estándar a la entrada estándar del siguiente comando. Aún aparecen errores en la ventana del shell.|&
significa conectar tanto stdout como stderr a stdin del siguiente comando.Ejecutar simultáneamente significa "en paralelo". En un sistema de varios núcleos (varios procesadores), los comandos canalizados pueden ejecutarse literalmente al mismo tiempo, de lo contrario, el programador del sistema operativo divide el tiempo entre ellos.
Dado un comando, p. ej., " a | b
", el shell crea una tubería , luego inicia ambos a
y b
con stdio para los dos comandos redirigidos de modo que a
escribe su stdout en la entrada de la tubería mientras b
lee stdin de la salida de la tubería. Las tuberías son implementadas por el sistema operativo con una cierta cantidad de almacenamiento en búfer de modo que a
se pueda escribir durante un tiempo antes de que la tubería se llene, pero una vez que la tubería se llena, cualquier nueva escritura se bloqueará dentro del SO hasta que b
se lean lo suficiente para desbloquear nuevas escrituras. Si b
intenta leer más datos de los disponibles, se bloqueará hasta que a
haya escrito más datos o hasta que la tubería se cierre, p. ej., si a
sale.
Si una palabra contiene un signo de dólar, " $
", los siguientes caracteres se toman como el nombre de una variable y la referencia se reemplaza por el valor de esa variable. Varios operadores de edición, escritos como sufijos a la referencia, permiten la edición de la ruta de acceso (por ejemplo, " :e
" para extraer solo la extensión) y otras operaciones.
Los mecanismos de comillas permiten que caracteres que de otro modo serían especiales, como espacios en blanco, comodines, paréntesis y signos de dólar, se tomen como texto literal .
\
significa tomar el siguiente carácter como un carácter literal ordinario."
La cadena"
es una comilla débil. Los espacios en blanco y los comodines se toman como literales, pero se siguen realizando sustituciones de variables y comandos.'
La cadena'
es una comilla fuerte. La cadena completa que la encierra se toma como literal.Las comillas dobles dentro de comillas dobles deben ir precedidas de "\""
. Lo mismo se aplica al símbolo del dólar, para evitar la expansión de variables "\$"
. En el caso de las comillas invertidas, para evitar la anidación de sustituciones de comandos, se requieren comillas simples "'\`'"
.
La sustitución de comandos permite que la salida de un comando se utilice como argumento de otro.
`
comando`
significa tomar la salida del comando , analizarla en palabras y pegarlas nuevamente en la línea de comando.El siguiente es un ejemplo de sustituciones de comandos anidados.
echo "`echo " \"\` "echo " \"\\\"\\\`\" "echo " \"\\\"\\\\\\\"\\\\\\\`\ \\"\" "echo " \"\\\"\\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\\\\\\ \\\`\\\\\\\"\\\"\" "echo " \"\\\"\\\\\\\"\\\\\\\\\\\\\\\ "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\`\\\\\\\\\\\\\\\"\ \\\\\\"\\\"\" "contraseña" \"\\\"\\\\\\\"\\\\\\\\\\\\\\\"\\\\\\\\\\\\\\\\\\\\ \\\\\\\\\\\`\\\\\\\\\\\\\\\"\\\\\\\\\\\\\\\`\\\\\\ \\\\\\\\\"\\\\\\\"\\\\\\\`\\\\\\"\\\"\\\`\\\"\"\` \" "`"
Normalmente, cuando el shell C inicia un comando, espera a que el comando finalice antes de darle al usuario otro mensaje indicando que se puede escribir un nuevo comando.
&
significa iniciar un comando en segundo plano y solicitar inmediatamente un nuevo comando.Un subshell es una copia secundaria separada del shell que hereda el estado actual pero luego puede realizar cambios, por ejemplo, en el directorio actual, sin afectar al padre.
(
comandos )
significa ejecutar comandos en un subshell.El shell C proporciona estructuras de control tanto para las pruebas de condición como para la iteración . Las estructuras de control de las pruebas de condición son las sentencias if y switch. Las estructuras de control de la iteración son las sentencias while, foreach y repeat.
Existen dos formas de la instrucción if . La forma corta se escribe en una sola línea, pero solo puede especificar un único comando si la expresión es verdadera.
comando if ( expresión )
La forma larga utiliza las palabras clave then, else y endif para permitir que se aniden bloques de comandos dentro de la condición.
si ( expresión1 ) entonces comandos de lo contrario si ( expresión2 ) entonces comandos ...los demás comandos terminan si
Si las palabras clave else y if aparecen en la misma línea, csh las encadena, en lugar de anidarlas, y el bloque termina con un único endif.
La sentencia switch compara una cadena con una lista de patrones, que puede contener caracteres comodín. Si no hay coincidencias, se toma la acción predeterminada, si la hay.
switch ( cadena ) patrón de caso 1: Comandos Patrón de caso breaksw2 : Comandos romper ... por defecto : Comandos rompe y termina
La instrucción while evalúa una expresión. Si es verdadera, el shell ejecuta los comandos anidados y luego repite el proceso mientras la expresión siga siendo verdadera.
mientras ( expresión ) Comandosfin
La declaración foreach toma una lista de valores, generalmente una lista de nombres de archivos producidos mediante comodines, y luego, para cada uno, establece la variable de bucle en ese valor y ejecuta los comandos anidados.
variable de bucle foreach ( lista de valores ) Comandosfin
La declaración de repetición repite un solo comando un número entero de veces.
Comando de repetición de números enteros
El shell C implementa tanto variables de shell como de entorno . [14] Las variables de entorno, creadas mediante la setenv
declaración, son siempre cadenas simples, pasadas a cualquier proceso secundario , que recupera estas variables a través del envp[]
argumento a main()
.
Las variables de shell, creadas mediante las instrucciones set
or @
, son internas de C shell. No se pasan a los procesos secundarios. Las variables de shell pueden ser cadenas simples o matrices de cadenas. Algunas de las variables de shell están predefinidas y se utilizan para controlar varias opciones internas de C shell, por ejemplo, qué debería suceder si un comodín no coincide con nada.
En las versiones actuales de csh, las cadenas pueden tener una longitud arbitraria, incluso millones de caracteres.
Las variables se pueden ampliar según sea necesario. Sin embargo, si se desea trabajar con un tamaño fijo, se prefiere la siguiente sintaxis.
# Crea una variable lo suficientemente grande para contener 1024 elementos. set fixed = { , }{ , } { , } { , }{ , }{ , } { , }{ , }{ , }
El shell C implementa una gramática de expresiones enteras de 32 bits con operadores tomados de C pero con algunos operadores adicionales para comparaciones de cadenas y pruebas del sistema de archivos, por ejemplo, para comprobar la existencia de un archivo. Los operadores deben estar separados de sus operandos por espacios en blanco. Las variables se referencian como $
name .
La precedencia de operadores también se tomó prestada de C, pero con diferentes reglas de asociatividad de operadores para resolver la ambigüedad de qué viene primero en una secuencia de operadores con la misma precedencia. En C, la asociatividad es de izquierda a derecha para la mayoría de los operadores; en C shell, es de derecha a izquierda. Por ejemplo,
// C agrupa desde la izquierda int i = 10 / 5 * 2 ; printf ( "%d \n " , i ); // imprime 4 i = 7 - 4 + 2 ; printf ( "%d \n " , i ); // imprime 5 i = 2 >> 1 << 4 ; printf ( "%d \n " , i ); // imprime 16
# Grupos de shell C desde la derecha
@ i = 10 / 5 * 2 echo $i # imprime 1
@ i = 7 - 4 + 2 echo $i # imprime 1
@ i = ( 2 >> 1 << 4 ) echo $i # imprime 0
Los paréntesis en el ejemplo de C shell se utilizan para evitar que los operadores de desplazamiento de bits se confundan con operadores de redirección de E/S. En ambos lenguajes, los paréntesis siempre se pueden utilizar para especificar explícitamente el orden de evaluación deseado, aunque sea solo por claridad.
Los valores de retorno están limitados a 8 bits. En el caso de exit
las expresiones, se puede utilizar el operador de negación unario para la evaluación de 32 bits.
salir !! 256 # Devuelve 1.
Aunque el propio Stephen Bourne reconoció que csh era superior a su shell para uso interactivo, [15] nunca ha sido tan popular para scripts.
En 1983, tanto csh como Bourne shell estaban disponibles para el sistema operativo UNOS de Charles River Data Systems, entre otras herramientas UNIX, bajo la licencia de Bell Laboratories . [16]
Inicialmente, y durante la década de 1980, no se podía garantizar que csh estuviera presente en todos los sistemas Unix y similares a Unix, pero sh sí, lo que lo convirtió en una mejor opción para cualquier script que pudiera tener que ejecutarse en otras máquinas. A mediados de la década de 1990, csh estaba ampliamente disponible, pero el uso de csh para scripting se enfrentó a nuevas críticas por parte del comité POSIX , [17] que especificó que solo debería haber un shell preferido, KornShell , tanto para fines interactivos como de scripting. El C shell también se enfrentó a críticas de otros [18] [19] sobre los supuestos defectos del C shell en la sintaxis, las características faltantes y la mala implementación.
set
, setenv
y alias
hacían básicamente lo mismo, es decir, asociaban un nombre con una cadena o un conjunto de palabras. Pero los tres tenían diferencias leves pero innecesarias. Se requería un signo igual para a set
pero no para setenv
o alias
; se requerían paréntesis alrededor de una lista de palabras para a set
pero no para setenv
o alias
, etc. De manera similar, las construcciones de bucle if
, switch
y usan palabras clave innecesariamente diferentes ( endif
, endsw
y end
) para terminar los bloques anidados.Funcionó para la mayoría de los comandos escritos de forma interactiva, pero para los comandos más complejos que un usuario podría escribir en un script, podría fallar fácilmente, produciendo solo un mensaje de error críptico o un resultado no deseado. Por ejemplo, el shell C no podía soportar la canalización entre estructuras de control. Intentar canalizar la salida de un foreach
comando grep
simplemente no funcionaba. (La solución alternativa, que funciona para muchas de las quejas relacionadas con el analizador, es dividir el código en scripts separados. Si foreach
se mueve a un script separado, la canalización funciona porque los scripts se ejecutan bifurcando una nueva copia de csh que hereda los manejadores stdio correctos. También es posible romper códigos en un solo archivo. A continuación se ofrece un ejemplo sobre cómo romper códigos en un solo archivo).
Otro ejemplo es el comportamiento no deseado de los siguientes fragmentos. Ambos parecen significar: "Si 'myfile' no existe, créelo escribiendo 'mytext' en él". Pero la versión de la derecha siempre crea un archivo vacío porque el orden de evaluación del shell C es buscar y evaluar operadores de redirección de E/S en cada línea de comando a medida que la lee, antes de examinar el resto de la línea para ver si contiene una estructura de control.
# Funciona como se esperaba si ( ! -e myfile ) entonces echo mytext > myfile fin si
# Siempre crea un archivo vacío si ( ! -e myfile ) echo mytext > myfile
# Solución alternativa (solo para tcsh) if ( ! -e myfile ) eval "echo mytext > myfile"
# Segunda solución alternativa (para csh y tcsh) ( exit ( -e myfile ) && ( ( echo mytext > myfile ) >& /dev/null || echo No se puede crear el archivo. ) || echo El archivo existe.
La implementación también es criticada por sus mensajes de error notoriamente pobres, por ejemplo, "0: Evento no encontrado", que no brinda información útil sobre el problema.
Sin embargo, con la práctica es posible superar esas deficiencias (lo que permite al programador adoptar enfoques mejores y más seguros al implementar un script).
El error "0: Evento no encontrado" implica que no hay comandos guardados en el historial. Es posible que el historial no funcione correctamente en los scripts, pero tener un conjunto predefinido de comandos en una variable sirve como solución alternativa.
#!/bin/csh -f set cmdlist = ( 'date # 1' \ 'uname # 2' \ 'tty # 3' \ 'id # 4' ) echo -n 'Ingrese un número para ejecutar un comando del historial: ' set cmdexec = "$<" ( exit ( ! ( "$cmdexec" > 0 && \ "$cmdexec" < = "$#cmdlist" ) ) ) >& /dev/null if ( "$status" ) then echo 'Número de evento no válido.' foreach cmd ( $cmdlist :q ) echo "$cmd" end exit -1 endif eval "$cmdlist[$cmdexec]"
Es preferible romper códigos recurriendo el script como solución alternativa para las funciones.
#!/bin/csh -f if ( ! "$?main" ) then if ( ! "$?0" ) then echo 'Debe ejecutar este script llamando explícitamente a su archivo.' exit -1 endif alias function 'set argv = ( \!* ) ; source "$main"' set main = "$0" set ret = "`function myfunc`" echo "$ret" exit endif ir a "$1" ; cambiarmifunción:función myfunc2echo "Una función." salirmifunc2:echo "Otra función." salir
El shell C tuvo un gran éxito al introducir una gran cantidad de innovaciones, entre ellas el mecanismo de historial, los alias, la notación de tilde, la finalización interactiva de nombres de archivo, una gramática de expresiones incorporada al shell y más, que desde entonces han sido copiadas por otros shells de Unix. Pero a diferencia de sh , que ha generado una gran cantidad de clones desarrollados independientemente, incluidos ksh y bash , solo se conocen dos clones de csh. (Dado que tcsh se basó en el código csh escrito originalmente por Bill Joy, no se lo considera un clon).
En 1986, Allen Holub escribió On Command: Writing a Unix-Like Shell for MS-DOS [22], un libro que describía un programa que había escrito llamado "SH" pero que, de hecho, copiaba el diseño del lenguaje y las características de csh, no de sh. Los disquetes complementarios que contenían el código fuente completo de SH y de un conjunto básico de utilidades similares a Unix (cat, cp, grep, etc.) estaban disponibles por 25 y 30 dólares, respectivamente, a través del editor. Las estructuras de control, la gramática de expresiones, el mecanismo de historial y otras características del SH de Holub eran idénticas a las del shell C.
En 1988, Hamilton Laboratories comenzó a distribuir Hamilton C shell para OS/2 . [23] Incluía tanto un clon de csh como un conjunto de utilidades similares a Unix. En 1992, Hamilton C shell fue lanzado para Windows NT . [24] La versión de Windows continúa siendo compatible activamente, pero la versión de OS/2 se suspendió en 2003. [24] Una referencia rápida de principios de 1990 [25] describió la intención como "plena conformidad con todo el lenguaje C shell (excepto el control de tareas)" pero con mejoras en el diseño del lenguaje y adaptación a las diferencias entre Unix y una PC. La mejora más importante fue un analizador de arriba hacia abajo que permitía que las estructuras de control se anidaran o canalizaran, algo que el C shell original no podía soportar, dado su analizador ad hoc. Hamilton también agregó nuevas características del lenguaje, incluidos procedimientos integrados y definidos por el usuario, variables locales estructuradas en bloques y aritmética de punto flotante. La adaptación a una PC incluyó soporte para el nombre de archivo y otras convenciones en una PC y el uso de subprocesos en lugar de bifurcaciones (que no estaban disponibles ni en OS/2 ni en Windows) para lograr paralelismo , por ejemplo, al configurar una tubería.