¿Por qué se requiere un espacio en blanco entre “[[” y “-e xxx” en ksh?


9

Por ejemplo, el siguiente comando no funciona:

if [[-e xyz]]; then echo File exists;fi

ksh da el siguiente error

[[-e: command not found

¿Es porque "[[-" es ambiguo?


2
[[es una palabra clave - presumiblemente árbol de análisis sintáctico de la cáscara requiere que las palabras clave deben ser delineadas por espacios en blanco
steeldriver

Otra forma de verlo es probablemente que podría escribir una función [[-, ¿cómo sabría el shell analizar "hacer la bandera e en [[" en lugar de "hacer la función [[- en e".
pbhj

Respuestas:


15

La explicación más simple sería, ya que en el manual se presenta como [[ expression ]]lo que tiene que haber espacio entre [[y expressiony cierre ]]. Pero, por supuesto, podemos intentar verlo más en profundidad. Los shells tienen una gramática algo compleja y dependen en gran medida del concepto de "división de palabras". En particular, el manual ksh (1) establece:

El shell comienza a analizar su entrada dividiéndola en palabras. Las palabras, que son secuencias de caracteres, están delimitadas por espacios en blanco sin comillas (espacio, tabulación y nueva línea) o metacaracteres (<,>, |,;, &, (y)). Además de delimitar palabras, los espacios y las pestañas se ignoran, mientras que las líneas nuevas suelen delimitar comandos.

Entonces, como se indica en el manual, la secuencia de caracteres [[-ese considera una palabra shell. kshbuscaría dicho comando en la lista de incorporados y operadores especiales (como foro while), luego buscaría un comando externo, y voilà, ninguno encontrado, por lo tanto, hay un mensaje de error sobre el comando no encontrado.

En este caso [[tampoco son metacaracteres especiales. Si lo fueran, la -eparte en [[-ese consideraría una palabra shell, no un argumento en [[sí mismo. Sin embargo, [[se describe como comando compuesto, y el manual dice lo siguiente:

Los comandos compuestos se crean usando las siguientes palabras reservadas: estas palabras solo se reconocen si no se citan y si se usan como la primera palabra de un comando (es decir, no pueden ir precedidas de asignaciones de parámetros o redirecciones):

Por lo tanto, para [[ser reconocido como un comando compuesto, debe ser la primera palabra de un comando o lista de comandos, lo que por definición previa de una "palabra" implica que debe estar separado por espacios, tabulaciones o líneas nuevas de otros palabras / argumentos.


Has preguntado en los comentarios : "¿Por qué el analizador no puede detenerse tan pronto como encuentra el patrón" [["y lo trata como el inicio de un comando condicional?" La respuesta corta probablemente se deba a que 1) influye en la [sintaxis, ya que originalmente era un comando externo, y para el cumplimiento estándar POSIX existe como comando externo incluso hoy, y 2) porque el analizador de shell está construido de esta manera. El analizador de shell puede reconocer otros caracteres especiales no delimitados por espacios: echo $((2+2))y (echo foobar)funciona perfectamente bien. Tal vez en el futuro cuando se kshreanude el desarrollo o parezca haber una bifurcación o clon (como mksho pdksh) alguien implementará una sintaxis sin espacio [[-e.

Ver también:


3
Creo que la cita manual de ksh realmente está en el lugar. Es similar a cualquier otro lenguaje de programación: en C, chares una palabra clave, pero aún no puede escribir charsomeletter = 'A';y esperar que el analizador se detenga después de ver char.
Peter - Restablece a Monica el

Creo que la principal diferencia entre su ejemplo de "char" y el símbolo "[[" en la pregunta es que, un identificador alfanumérico, en general, necesita un delimitador de espacio para separarse de los demás; Los símbolos especiales como tokens, por otro lado, no necesitan delimitadores de espacio.
AcBap

Exactamente. No estoy seguro de que la comparación con C sea útil. En C se puede decir i--<=++j, y el compilador no tiene problemas para analizar eso como i -- <= ++ j.
Scott

@ Scott Creo que la comparación de C es útil (y que es algo secundario que los identificadores de C solo permitan alfanuméricos y _). Ksh tiene (como operador y (echo hello)analiza como ( echo hello ). Ha if, {, [[, y otras palabras clave , y ifx, {xy [[xson cada uno un token - como la forma charsomeletteres una ficha C. En contraste, un sin comillas (xen ksh son dos tokens, como cómo i--son dos tokens en C. Lo que conceptualmente significa ser un operador difiere entre los shells de estilo Bourne (como ksh) y C. Pero Peter A. Schneider señaló un similitud léxica real .
Eliah Kagan

2

Es importante tener en cuenta que [es un comando, y la palabra clave de shell [[en bash y ksh se basa en [.

[[se usa de manera similar a [. A veces incluso se puede sustituir [ ]con [[ ]]sin cambio en el comportamiento. Con [, como cualquier comando, un espacio es obligatorio entre el comando y sus argumentos. Lo mismo se aplica a [[.

Solíamos tener solo /usr/bin/[. Ahora la mayoría de los shells se han [incorporado para la eficiencia, pero la sintaxis es la misma. En los depósitos que proporcionan [[, funciona como una alternativa más versátil para [.

Aquí está la descripción de [en bash:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

Entonces [es equivalente a test(aparte de esperar un ]argumento al final). help testdará aún más detalles al respecto. Puedes comparar esto con help [[.

También hay una página de manual para el comando externo [( man \[).

En el caso de

if [[-e
  • Ni [tampoco [[es un comando, allí. La palabra completa [[-ees.
  • Esto lo if [[-econvierte en una prueba de verdadero / falso. Entonces , ¿ existe un comando [[-e?

¿Es porque "[[-" es ambiguo?

Si. O no. [[-ees lo que es: nada que el shell entienda, por lo que supone que es su propio comando. ;-)


"% help [[" dice que "[[" es un comando condicional. ¿Por qué el analizador no puede detenerse tan pronto como encuentra el patrón "[[" y lo trata como el inicio de un comando condicional?
AcBap

@Myloti [[-ees un nombre de comando potencialmente válido (si se ve raro).
Gordon Davisson

2

El espacio es un deliminador y es obligatorio. Como puedes ver en shellcheck:

$ shellcheck gmail-browse-msgs-algorithm.sh

In gmail-browse-msgs-algorithm.sh line 847:
        [[$Today != "${DaysArr[ i + DAY_DELETED_ON_NDX ]}" ]] && continue
          ^-- SC1035: You need a space after the [[ and before the ]].

(Tanto ksh como bash admiten [[y no funcionan sin el espacio. Shellcheck proporciona exactamente esa salida con scripts ksh y bash similares que contienen esa línea con errores).

Por qué se necesitan delimitadores tiene que ver con tokens y léxicos .


Tenga en cuenta que OP preguntó sobre kshshell en la pregunta. Bash toma prestado [[operador kshy de esta tela de juicio su comportamiento es idéntico, pero me gustaría tener cuidado de supuestos ya que hay algunas diferencias significativas entre kshy bashel comportamiento y el funcionamiento interno. Eso es solo porque shellcheck funciona en script bash, puede que no sea necesariamente una herramienta apropiada para scripts ksh. Solo algo a tener en cuenta al acercarse a diferentes proyectiles
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy Estaba siendo oportunista usando shellcheck para resaltar las [[reglas integradas. De ninguna manera deduje que kshera un caparazón en el que shellcheckpodría encontrar errores. Espero que otros lo aprecien kshy bashsean dos intérpretes diferentes. Gracias por mencionar eso. También vale la pena señalar que [[es un bash incorporado, mientras que [es un comando externo.
WinEunuuchs2Unix

En realidad, [también es una función integrada en bash :) manpages.ubuntu.com/manpages/bionic/man7/bash-builtins.7.html Pero no está lejos, [o testdebe ser un comando externo. En los días de Bourne Shell original [era, de hecho, un comando externo, y todavía existe hoy en día /usr/bin/[porque POSIX requiere que sea un comando externo pubs.opengroup.org/onlinepubs/009695399/utilities/test.html POSIX requiere muy pocos ser incorporado pubs.opengroup.org/onlinepubs/009695399/idx/sbi.html Builtin [es para la eficiencia
Sergiy Kolodyazhnyy

2
Shellcheck lo sabe ksh; usar un hashbang o -s ksh. Btw, ksh y bash se han [["incorporado", pero es una palabra clave , a diferencia de [, que es un shell incorporado . (ksh :; whence -v [ [[bash type [ [[:) Los comandos internos y externos son sintácticamente similares. Una forma diferente [[de esto [es que [[suprime algunas expansiones en sus argumentos, lo que no podría ser como un componente integrado, al igual {que no podría agruparse si fuera un elemento integrado. Esta puede ser la razón por la cual {x(o [[x) ser una ficha se siente extraño. Creo que es correcto decir que las palabras clave y las palabras clave son léxicas pero no sintácticamente similares. @SergiyKolodyazhnyy
Kagan

1
@EliahKagan En realidad, publiqué una pregunta en U&L para obtener una respuesta precisa sobre lo que POSIX piensa sobre esta situación unix.stackexchange.com/questions/526574/… El lenguaje Shell es lo suficientemente complejo, por lo que espero que alguien pueda explicarlo en términos más formales.
Sergiy Kolodyazhnyy

1

[[puede ser un comando externo! Es decir, puede ser un programa y no una sintaxis compatible con su shell directamente. Sería posible soportar una sintaxis sin espacio, kshpero fallaría en sistemas con sistema externo, [[por lo que por razones de compatibilidad es mejor mantener el espacio requerido.

BusyBox se proporciona [[como un comando externo , por ejemplo.


0

Dado que [[-epuede participar en la expansión de shell (se puede usar como

echo [[-e]*

para enumerar todos los archivos que comienzan con una letra entre [e einclusive), sería un completo desastre si [y si ]los caracteres especiales no participaran en la división normal de palabras gobernada por espacios en blanco.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.