Shell POSIX: ¿`$` pierde su significado especial si es el último carácter de una palabra?

17

En ash, dash y bash, cuando corro

$ echo ab$

vuelve

ab$

¿POSIX especifica este comportamiento o es solo una convención común en shells compatibles con POSIX? No pude encontrar nada en la página POSIX Shell Command Language que mencione este comportamiento.

Harold Fischer
fuente
44
La mejor pregunta es "¿ $ Adquiere un significado especial si es el último carácter de una palabra?" No hay un significado especial único asignado a $; se usa para introducir expansiones múltiples, pero distintas, como expansión de parámetros ${...}, sustitución de comandos $(...)y expresiones aritméticas $((...)). Algunos shells introducen contextos adicionales, como kshla variante de sustitución de comandos x=${ echo foo; echo bar;}(que difiere del estándar $(...)al no ejecutar los comandos en un subshell).
chepner
@chepner ¿Le gustaría evaluar la diferencia de opinión entre Issac y Michael Homer? Sus respuestas se contradicen explícitamente entre sí
Harold Fischer el
1
Estoy de acuerdo con la interpretación de Michael Homer; el shell ni siquiera comienza a preocuparse por las expansiones hasta que se completa el análisis, por lo que, en una sola palabra ab$, no hay caracteres a continuación $, ya sea "seguido" por un carácter nulo en la cadena de entrada original o un espacio en un caso como echo ab$ foo; el espacio en blanco original sin comillas ha sido reconocido y descartado después del análisis.
chepner

Respuestas:

10

$no tiene un significado especial por sí mismo (try echo $), solo cuando se combina con otro personaje después de que y formando una expansión, por ejemplo, $var(o ${var}), $(util), $((1+2)).

El $obtiene su "especial", que significa que la definición de una expansión en el estándar POSIX en la sección de reconocimiento simbólico :

Si el carácter actual no está entre comillas $o `, el shell identificará el inicio de cualquier candidato para la expansión de parámetros, sustitución de comandos o expansión aritmética a partir de sus secuencias introductorias de caracteres sin comillas: $o ${, $(o `, y $((, respectivamente. El shell leerá suficiente información para determinar el final de la unidad a expandir ( como se explica en las secciones citadas) Mientras procesa los caracteres, si se encuentran instancias de expansiones o citas anidadas dentro de la sustitución, el shell las procesará recursivamente de la manera especificada para la construcción que se encuentra. Los caracteres encontrados desde el comienzo de la sustitución hasta su final, permitiendo cualquier recursión necesaria para reconocer construcciones incrustadas, se incluirán sin modificar en el token de resultado, incluidos los operadores o citas de sustitución incrustados o cerrados. El token no se delimitará al final de la sustitución.

Entonces, si $no forma una expansión, entran en vigencia otras reglas de análisis:

Si el carácter anterior era parte de una palabra, el carácter actual se agregará a esa palabra.

Eso cubre tu ab$cuerda.

En el caso de un solitario $(la "nueva palabra" sería la $sola):

El carácter actual se usa como el comienzo de una nueva palabra.

El significado de la palabra generada que contiene una $que no es una expansión estándar se define explícitamente como no especificado por POSIX.

También tenga en cuenta que $es el último carácter en $$, pero que también es la variable que contiene el PID del shell actual. En bash, !$puede invocar una expansión del historial (el último argumento del comando anterior). Entonces, en general, no, $no está exento de significado al final de una palabra sin comillas, pero al final de una palabra al menos no denota una expansión estándar.

Kusalananda
fuente
7

Dependiendo de la situación exacta, esto no está explícitamente especificado (por lo que las implementaciones pueden hacer lo que quieran) o se requiere que ocurra como usted observó. En su escenario exacto echo ab$, POSIX ordena la salida "ab $" que observó y no está sin especificar . Un resumen rápido de todos los diferentes casos está al final.

Hay dos elementos: primero la tokenización en palabras, y luego la interpretación de esas palabras.


Tokenización

La tokenización POSIX requiere que un $que no sea el comienzo de una expansión de parámetro válida , sustitución de comando o sustitución aritmética se considere una parte literal del WORDtoken que se está construyendo. Esto se debe a la regla 5 ("Si el carácter actual no está entre comillas $o `, el shell identificará el inicio de cualquier candidato para la expansión de parámetros, sustitución de comandos o expansión aritmética de sus secuencias de caracteres introductorias sin comillas: $o ${, $(o `, y $((, respectivamente" ) no se aplica, ya que ninguna de esas expansiones es viable allí. La expansión de parámetros requiere un nombre válido para aparecer allí, y un nombre vacío no es válido.

Como esta regla no se aplica, continuamos siguiendo hasta encontrar una que sí lo haga. Los dos candidatos son el # 8 ("Si el carácter anterior era parte de una palabra, el carácter actual se agregará a esa palabra") y el # 10 ("El carácter actual se usa como el comienzo de una nueva palabra"). , que se aplican a echo a$y echo $respectivamente.

También hay un tercer caso de la forma echo a$+bque cae en la misma grieta, ya +que no es el nombre de un parámetro especial. A este volveremos más adelante, ya que activa diferentes partes de las reglas.

Por lo tanto, la especificación requiere que $se considere una parte de la palabra sintácticamente, y luego se pueda procesar más adelante.


Expansión de palabras

Una vez que la entrada se ha analizado de esta manera, con la $palabra incluida, las expansiones de palabras se aplican a cada una de las palabras que se han leído. Cada palabra se procesa individualmente .

Se especifica que :

Si a un '$' sin comillas le sigue un carácter que no es uno de los siguientes:

  • Un caracter numerico
  • El nombre de uno de los parámetros especiales (ver Parámetros especiales )
  • Un primer carácter válido de un nombre de variable
  • A <left-curly-bracket>('{')
  • UN <left-parenthesis>

El resultado no está especificado.

"No especificado" es un término particular aquí que significa que

  1. Un shell conforme puede elegir cualquier comportamiento en este caso
  2. Una aplicación conforme no puede confiar en ningún comportamiento particular

En su ejemplo, echo ab$el $ no va seguido de cualquier carácter , por lo que esta regla no se aplica y el resultado no especificado, no se invoca. Simplemente no hay expansión incitada por el $, por lo que está literalmente presente e impreso.

Donde podría aplicar es en nuestro tercer caso desde arriba: echo a$+b. Aquí $es seguido por +, que no es un número, parámetro especial ( @, *, #, ?, -, $, !, o 0), inicio de una variable de nombre (o un guión bajo alfabético en la conjunto de caracteres portátil ), o uno de los soportes. En este caso, el comportamiento no está especificado: un shell conforme puede inventar un parámetro especial llamado +expandir , y una aplicación conforme no debe asumir que el shell no lo hace . El shell también podría hacer cualquier otra cosa que quisiera, incluso informar un error.

Por ejemplo, zsh, incluso en su modo POSIX, interpreta $+bcomo "es un bconjunto variable " y sustituye 1 o 0 en su lugar. Del mismo modo tiene extensiones para ~y =. Este es un comportamiento conforme.

Otro lugar donde esto podría suceder es echo "a$ b". Una vez más, el shell puede hacer lo que desee, y usted, como autor del script, debe escapar de $si desea una salida literal. Si no lo hace, puede funcionar, pero no puede confiar en él. Esta es la letra absoluta de la especificación, pero no creo que este tipo de granularidad haya sido pensado o considerado.


En resumen

  • echo ab$: salida literal, completamente especificada
  • echo a$ b: salida literal, completamente especificada
  • echo a$ b$: salida literal, completamente especificada
  • echo a$b: expansión del parámetro b, completamente especificada
  • echo a$-b: expansión de parámetro especial -, completamente especificado
  • echo a$+b: comportamiento no especificado
  • echo "a$ b": comportamiento no especificado

Para un $al final de una palabra, se le permite confiar en el comportamiento y debe tratarse literalmente y transmitirse al echocomando como parte de su argumento. Ese es un requisito de conformidad en el shell.

Michael Homer
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
terdon
@MichaelHomer ¿ echo $También sería literal y completamente especificado?
Harold Fischer
@HaroldFischer Sí
Michael Homer
¿Dónde hacer echo "$"y echo "a b$"caer?
Harold Fischer