Me encontré con ss64.com que proporciona una buena ayuda sobre cómo escribir scripts por lotes que ejecutará el intérprete de comandos de Windows.
Sin embargo, no he podido encontrar una buena explicación de la gramática de los scripts por lotes, cómo las cosas se expanden o no, y cómo escapar de las cosas.
Aquí hay ejemplos de preguntas que no he podido resolver:
- ¿Cómo se gestiona el sistema de cotizaciones? Hice un script TinyPerl
(foreach $i (@ARGV) { print '*' . $i ; }
), lo compilé y lo llamé de esta manera:my_script.exe "a ""b"" c"
→ la salida es*a "b*c
my_script.exe """a b c"""
→ salida*"a*b*c"
- ¿Cómo funciona el
echo
comando interno ? ¿Qué se expande dentro de ese comando? - ¿Por qué tengo que usar
for [...] %%I
en scripts de archivo, perofor [...] %I
en sesiones interactivas? - ¿Cuáles son los personajes de escape y en qué contexto? ¿Cómo escapar de un signo de porcentaje? Por ejemplo, ¿cómo puedo hacer eco
%PROCESSOR_ARCHITECTURE%
literalmente? Encontré queecho.exe %""PROCESSOR_ARCHITECTURE%
funciona, ¿hay una mejor solución? - ¿Cómo se
%
combinan los pares ? Ejemplo:set b=a
,echo %a %b% c%
→%a a c%
set a =b
,echo %a %b% c%
→bb c%
- ¿Cómo me aseguro de que una variable pase a un comando como argumento único si alguna vez esta variable contiene comillas dobles?
- ¿Cómo se almacenan las variables cuando se usa el
set
comando? Por ejemplo, si lo hagoset a=a" b
y luegoecho.%a%
obtengoa" b
. Sin embargo, si usoecho.exe
de UnxUtils, obtengoa b
. ¿Cómo se%a%
expande de una manera diferente?
Gracias por tus luces.
Respuestas:
Realizamos experimentos para investigar la gramática de los scripts por lotes. También investigamos las diferencias entre el modo de línea de comandos y el lote.
Analizador de línea de lote:
Aquí hay una breve descripción de las fases en el analizador de línea de archivo por lotes:
Fase 0) Leer línea:
Fase 1) Porcentaje de expansión:
Fase 2) Procesar caracteres especiales, tokenizar y construir un bloque de comandos en caché: este es un proceso complejo que se ve afectado por cosas como comillas, caracteres especiales, delimitadores de tokens y escapes de caret.
Fase 3) Haga eco de los comandos analizados solo si el bloque de comandos no comenzó
@
y ECHO estaba ENCENDIDO al comienzo del paso anterior.Fase 4)
%X
Expansión variable FOR : solo si un comando FOR está activo y los comandos después de DO están siendo procesados.Fase 5) Expansión retrasada: solo si la expansión retrasada está habilitada
Fase 5.3) Procesamiento de tubería: solo si los comandos están a ambos lados de una tubería
Fase 5.5) Ejecutar redireccionamiento:
Fase 6) Procesamiento de LLAMADAS / Duplicación de Caret: solo si el token de comando es LLAMADA
Fase 7) Ejecutar: el comando se ejecuta
Aquí hay detalles para cada fase:
Tenga en cuenta que las fases que se describen a continuación son solo un modelo de cómo funciona el analizador por lotes. Las partes internas reales de cmd.exe pueden no reflejar estas fases. Pero este modelo es efectivo para predecir el comportamiento de los scripts por lotes.
Fase 0) Leer línea: lea la línea de entrada hasta el primero
<LF>
.<Ctrl-Z>
(0x1A) se lee como<LF>
(LineFeed 0x0A)<Ctrl-Z>
, se trata como en sí - se no se convirtió al<LF>
Fase 1) Porcentaje de expansión:
%%
es reemplazado por un sencillo%
%*
,%1
,%2
, etc.)%var%
, si var no existe, reemplácelo con nada<LF>
no dentro de la%var%
expansiónFase 2) Procesar caracteres especiales, tokenizar y construir un bloque de comandos en caché: este es un proceso complejo que se ve afectado por cosas como comillas, caracteres especiales, delimitadores de tokens y escapes de caret. Lo que sigue es una aproximación de este proceso.
Hay conceptos que son importantes a lo largo de esta fase.
<space>
<tab>
;
,
=
<0x0B>
<0x0C>
y los<0xFF>
delimitadores de token consecutivos se tratan como uno: no hay tokens vacíos entre los delimitadores de token
Los siguientes caracteres pueden tener un significado especial en esta fase, según el contexto:
<CR>
^
(
@
&
|
<
>
<LF>
<space>
<tab>
;
,
=
<0x0B>
<0x0C>
<0xFF>
Mira cada personaje de izquierda a derecha:
<CR>
luego, elimínelo, como si nunca hubiera estado allí (excepto por un extraño comportamiento de redireccionamiento )^
), se escapa el siguiente carácter y se elimina el símbolo de interferencia de escape. Los personajes escapados pierden todo significado especial (excepto<LF>
)."
), alterna el indicador de cita. Si la bandera de cotización está activo, entonces solamente"
y<LF>
son especiales. Todos los demás personajes pierden su significado especial hasta que la próxima cita desactiva la bandera de la cita. No es posible escapar de la cita de cierre. Todos los caracteres entre comillas siempre están dentro del mismo token.<LF>
siempre apaga la bandera de cotización. Otros comportamientos varían según el contexto, pero las citas nunca alteran el comportamiento de<LF>
.<LF>
<LF>
es despojado<LF>
, entonces se trata como un literal, lo que significa que este proceso no es recursivo.<LF>
no entre paréntesis<LF>
se elimina y se termina el análisis de la línea actual.<LF>
dentro de un bloque entre paréntesis FOR IN<LF>
se convierte en un<space>
<LF>
dentro de un bloque de comandos entre paréntesis<LF>
se convierte<LF><space>
y<space>
se trata como parte de la siguiente línea del bloque de comandos.&
|
<
o>
, divida la línea en este punto para manejar tuberías, concatenación de comandos y redirección.|
), cada lado es un comando separado (o bloque de comandos) que recibe un manejo especial en la fase 5.3&
,&&
o||
concatenación de comando, cada lado de la concatenación se trata como un comando separado.<
,<<
,>
, o>>
redirección, la cláusula de redirección se analiza, elimina temporalmente, y luego añade al final de la consigna de corriente. Una cláusula de redirección consta de un dígito de identificador de archivo opcional, el operador de redirección y el token de destino de redirección.@
, entonces@
tiene un significado especial. (@
no es especial en ningún otro contexto)@
se elimina.@
está antes de una apertura(
, se excluye todo el bloque entre paréntesis del eco de fase 3.(
no es especial.(
, inicie una nueva declaración compuesta e incremente el contador de paréntesis)
termina la declaración compuesta y disminuye el contador de paréntesis.)
funciona de manera similar a unaREM
declaración siempre que sea seguido inmediatamente por un delimitador de token, un carácter especial, una nueva línea o el final del archivo^
(es posible la concatenación de líneas)@
se hayan eliminado y la redirección se haya movido al final).(
funciona como un delimitador de token de comando, además de los delimitadores de token estándar<LF>
como<space>
. Después de analizar la cláusula IN, todos los tokens se concatenan juntos para formar un solo token.^
que termina la línea, entonces el token de argumento se descarta y la línea posterior se analiza y se agrega al REM. Esto se repite hasta que haya más de una ficha o el último carácter no^
.:
, y esta es la primera ronda de la fase 2 (no un reinicio debido a CALL en la fase 6), entonces)
,<
,>
,&
y|
ya no tienen un significado especial. Todo el resto de la línea se considera parte de la etiqueta "comando".^
sigue siendo especial, lo que significa que de continuación de línea se puede utilizar para añadir una línea posterior a la etiqueta.(
ya no tiene un significado especial para el primer comando que sigue a la etiqueta no ejecutada .|
tubería o&
,&&
o||
concatenación de comando en la línea.Fase 3) Haga eco de los comandos analizados solo si el bloque de comandos no comenzó
@
y ECHO estaba ENCENDIDO al comienzo del paso anterior.Fase 4)
%X
Expansión variable FOR : solo si un comando FOR está activo y los comandos después de DO están siendo procesados.%%X
en%X
. La línea de comando tiene diferentes reglas de expansión porcentual para la fase 1. Esta es la razón por la que las líneas de comando usan%X
pero los archivos%%X
por lotes usan las variables FOR.~modifiers
no distinguen entre mayúsculas y minúsculas.~modifiers
tienen prioridad sobre los nombres de variables. Si el siguiente carácter~
es un modificador y un nombre de variable FOR válido, y existe un carácter posterior que es un nombre de variable FOR activo, entonces el carácter se interpreta como un modificador.---- A partir de este punto, cada comando identificado en la fase 2 se procesa por separado.
---- Las fases 5 a 7 se completan para un comando antes de pasar al siguiente.
Fase 5) Expansión retrasada: solo si la expansión retrasada está activada, el comando no está en un bloque entre paréntesis a cada lado de una tubería , y el comando no es un script por lotes "desnudo" (nombre del script sin paréntesis, CALL, concatenación de comandos, o tubería).
!
. Si no, entonces el token no se analiza, importante para los^
personajes. Si el token contiene!
, entonces escanea cada personaje de izquierda a derecha:^
), el siguiente carácter no tiene un significado especial, el símbolo de intercalación en sí mismo se elimina!
se colapsan en una sola!
!
se elimina<CR>
o<LF>
)Fase 5.3) Procesamiento de tubería: solo si los comandos están a cada lado de una tubería
Cada lado de la tubería se procesa de forma independiente y asíncrona.
%comspec% /S /D /c" commandBlock"
, por lo que el bloque de comando obtiene un reinicio de fase, pero esta vez en modo de línea de comando.<LF>
trata de un bloque de comandos entre paréntesis, todos los que tienen un comando antes y después se convierten a<space>&
. Otros<LF>
son despojados.Fase 5.5) Ejecutar redireccionamiento: cualquier redirección que se descubrió en la fase 2 ahora se ejecuta.
||
se use .Fase 6) Procesamiento de LLAMADA / duplicación de Caret: solo si el token de comando es CALL, o si el texto antes del primer delimitador de token estándar es CALL. Si CALL se analiza desde un token de comando más grande, la parte no utilizada se antepone al token de argumentos antes de continuar.
/?
. Si se encuentra en algún lugar dentro de los tokens, entonces cancele la fase 6 y continúe con la Fase 7, donde se imprimirá la AYUDA para la LLAMADA.CALL
, para que se puedan apilar varias LLAMADAS&
o|
(
@
IF
oFOR
no se reconoce como un comando interno o externo.:
.:
, entoncesLa fase 7 no se ejecuta para scripts CALLed o: etiquetas.
Fase 7) Ejecutar: el comando se ejecuta
+
/
[
]
<space>
<tab>
,
;
o=
Si el texto anterior es un comando interno, recuerde ese comando
.
\
o:
Si el texto anterior no es un comando interno, vaya a 7.2. De lo
contrario, el texto anterior puede ser un comando interno. Recuerda este comando.
+
/
[
]
<space>
<tab>
,
;
o=
Si el texto anterior es una ruta a un archivo existente, vaya a 7.2 De lo
contrario, ejecute el comando interno recordado.
/?
se detecta. La mayoría reconoce/?
si aparece en alguna parte de los argumentos. Pero algunos comandos como ECHO y SET solo imprimen ayuda si el primer token de argumento comienza con/?
.set "name=content" ignored
-> valor =content
entonces el texto entre el primer signo igual y la última comilla se usa como contenido (se excluyen la primera comilla y la última comilla). El texto después de la última cita se ignora. Si no hay comillas después del signo igual, entonces el resto de la línea se usa como contenido.
set name="content" not ignored
-> valor ="content" not ignored
entonces el resto completo de la línea después del igual se usa como contenido, incluidas todas y cada una de las comillas que puedan estar presentes.
::
siempre dará como resultado un error a menos que se use SUBST para definir un volumen para.::
Si se usa SUBST para definir un volumen
::
, entonces el volumen se cambiará, no se tratará como una etiqueta.,
,;
,=
o+
luego romper el comando de token en la primera aparición de<space>
,
;
o=
y anteponer el resto al argumento token (s).Si no puede encontrar el volumen, anule con un error.
:
, entonces vaya a 7.4Tenga en cuenta que si el token de etiqueta comienza con
::
, entonces esto no se alcanzará porque el paso anterior habrá abortado con un error a menos que se use SUBST para definir un volumen::
.:
y, a continuación Goto 7.4Tenga en cuenta que esto rara vez se alcanza debido a que el paso anterior se habrá abortado con un error a menos que el símbolo de comandos comienza con
::
, y SUBST se utiliza para definir un volumen para::
, y el el token de comando completo es una ruta válida a un comando externo.:
.Las reglas en 7.2 y 7.3 pueden evitar que una etiqueta llegue a este punto.
Analizador de línea de comando:
Funciona como el BatchLine-Parser, excepto:
Fase 1) Porcentaje de expansión:
%*
,%1
expansión de argumentos etc.%var%
se modifica.%%
. Si var = contenido, se%%var%%
expande a%content%
.Fase 3) Eco los comandos analizados
Fase 5) Expansión retardada: solo si DelayedExpansion está habilitada
!var!
se modifica.Fase 7) Ejecutar comando
::
Análisis de valores enteros
Hay muchos contextos diferentes donde cmd.exe analiza valores enteros de cadenas y las reglas son inconsistentes:
SET /A
IF
%var:~n,m%
(expansión de subcadena variable)FOR /F "TOKENS=n"
FOR /F "SKIP=n"
FOR /L %%A in (n1 n2 n3)
EXIT [/B] n
Los detalles de estas reglas se pueden encontrar en Reglas sobre cómo CMD.EXE analiza los números
Para cualquiera que desee mejorar las reglas de análisis de cmd.exe, hay un tema de discusión en el foro DosTips donde se pueden informar problemas y hacer sugerencias.
Espero que ayude a
Jan Erik (jeb) - Autor original y descubridor de las fases
Dave Benham (dbenham) - Mucho contenido adicional y edición
fuente
)
realmente hace la función casi como unREM
comando cuando el contador paréntesis es 0. Trate ambos desde la línea de comandos:) Ignore this
yecho OK & ) Ignore this
Al invocar un comando desde una ventana de comando, la tokenización de los argumentos de la línea de comando no se realiza mediante
cmd.exe
(también conocido como "el shell"). Muy a menudo, la tokenización se realiza mediante el tiempo de ejecución C / C ++ de los procesos recién formados, pero esto no es necesariamente así, por ejemplo, si el nuevo proceso no se escribió en C / C ++, o si el nuevo proceso elige ignorarargv
y procesar la línea de comando sin procesar por sí misma (por ejemplo, con GetCommandLine ()) En el nivel del sistema operativo, Windows pasa las líneas de comando tokenizadas como una sola cadena a los nuevos procesos. Esto contrasta con la mayoría de los shells * nix, donde el shell tokeniza los argumentos de una manera consistente y predecible antes de pasarlos al proceso recién formado. Todo esto significa que puede experimentar un comportamiento de tokenización de argumentos muy divergente entre diferentes programas en Windows, ya que los programas individuales a menudo toman la tokenización de argumentos en sus propias manos.Si suena a anarquía, es algo así. Sin embargo, ya que un gran número de programas de Windows hacer utilizar el Microsoft C / C ++ en tiempo de ejecución de
argv
, puede ser útil en general para entender cómo el MSVCRT tokenizes argumentos. Aquí hay un extracto:El "lenguaje por lotes" de Microsoft (
.bat
) no es una excepción a este entorno anárquico, y ha desarrollado sus propias reglas únicas para la tokenización y el escape. También parece que el símbolo del sistema de cmd.exe realiza un procesamiento previo del argumento de la línea de comando (principalmente para la sustitución de variables y el escape) antes de pasar el argumento al nuevo proceso de ejecución. Puede leer más sobre los detalles de bajo nivel del lenguaje por lotes y el escape de cmd en las excelentes respuestas de jeb y dbenham en esta página.Creemos una utilidad de línea de comando simple en C y veamos qué dice sobre sus casos de prueba:
(Notas: argv [0] siempre es el nombre del ejecutable, y se omite a continuación por brevedad. Probado en Windows XP SP3. Compilado con Visual Studio 2005.)
Y algunas de mis propias pruebas:
fuente
[a "b" c]
podría convertirse[a "b] [c]
en un procesamiento posterior.GetCommandLine
. Quizás TinyPerl está ignorando argv y simplemente tokenizando la línea de comando sin procesar por sus propias reglas.Reglas de expansión porcentual
Aquí hay una explicación ampliada de la Fase 1 en la respuesta de jeb (Válido tanto para el modo por lotes como para el modo de línea de comandos).
Fase 1) Porcentaje de expansión Comenzando desde la izquierda, escanea cada carácter para
%
o<LF>
. Si se encuentra entonces<LF>
)<LF>
entonces<LF>
adelante<CR>
)%
, así que proceda a 1.1%
) omitido si el modo de línea de comando%
,reemplace
%%
con un solo%
y continúe escaneando*
y las extensiones de comando están habilitadas,reemplace
%*
con el texto de todos los argumentos de la línea de comando (Reemplace con nada si no hay argumentos) y continúe con el escaneo.<digit>
entoncesReemplazar
%<digit>
con valor de argumento (sustituir con nada si no definido) y continuar la exploración.~
y las extensiones de comando están habilitadas, entonces<digit>
entoncesReemplazar
%~[modifiers]<digit>
con valor de argumento modificado (sustituir con nada si no está definida o si se especifica $ PATH: modificador no está definido) y continuar la exploración.Nota: los modificadores no distinguen entre mayúsculas y minúsculas y pueden aparecer varias veces en cualquier orden, excepto $ PATH: el modificador solo puede aparecer una vez y debe ser el último modificador antes del
<digit>
mire la siguiente cadena de caracteres, separando antes
%
o al final del búfer, y llámelos VAR (puede ser una lista vacía)%
entoncesReemplace
%VAR%
con el valor de VAR y continúe escaneandoEliminar
%VAR%
y continuar el escaneomire la siguiente cadena de caracteres, separando antes
%
:
o al final del búfer, y llámelos VAR (puede ser una lista vacía). Si VAR se rompe antes:
y el carácter subsiguiente se%
incluye:
como último carácter en VAR y se rompe antes%
.%
entoncesReemplace
%VAR%
con el valor de VAR y continúe escaneandoEliminar
%VAR%
y continuar el escaneo:
entonceselimine
%VAR:
y continúe escaneando.~
entonces[integer][,[integer]]%
entoncesReemplace
%VAR:~[integer][,[integer]]%
con una subcadena de valor de VAR (posiblemente resultando en una cadena vacía) y continúe el escaneo.=
o*=
luego, lasintaxis de búsqueda y reemplazo de variables no válidas genera un error grave: todos los comandos analizados se anulan y el procesamiento por lotes se anula si está en modo por lotes.
[*]search=[replace]%
, donde la búsqueda puede incluir cualquier conjunto de caracteres excepto=
, y reemplazar puede incluir cualquier conjunto de caracteres excepto%
, luegoReemplazar
%VAR:[*]search=[replace]%
con el valor de VAR después de realizar la búsqueda y reemplazar (posiblemente resultando en una cadena vacía) y continuar escanearelimine
%
y continúe la exploración comenzando con el siguiente carácter después de%
%
y continúe escaneando comenzando con el siguiente carácter después del encabezado preservado%
Lo anterior ayuda a explicar por qué este lote
Da estos resultados:
Nota 1 - La fase 1 ocurre antes del reconocimiento de las declaraciones REM. ¡Esto es muy importante porque significa que incluso un comentario puede generar un error fatal si tiene una sintaxis de expansión de argumento no válida o una búsqueda de variable no válida y reemplaza la sintaxis!
Nota 2 - Otra consecuencia interesante de las reglas de análisis porcentual: las variables que contienen: en el nombre pueden definirse, pero no pueden expandirse a menos que las extensiones de comando estén deshabilitadas. Hay una excepción: un nombre de variable que contiene dos puntos al final se puede expandir mientras las extensiones de comando están habilitadas. Sin embargo, no puede realizar operaciones de subcadena o búsqueda y reemplazo en nombres de variables que terminan con dos puntos. El archivo por lotes a continuación (cortesía de jeb) demuestra este comportamiento
Nota 3 - Un resultado interesante del orden de las reglas de análisis que Jeb establece en su publicación: Al realizar la búsqueda y reemplazo con expansión retrasada, los caracteres especiales en los términos de búsqueda y reemplazo deben escaparse o citarse. Pero la situación es diferente para el porcentaje de expansión: el término de búsqueda no se debe escapar (aunque se puede citar). El porcentaje de reemplazo de cadena puede o no requerir escape o presupuesto, dependiendo de su intención.
Reglas de expansión retrasadas
Aquí hay una explicación ampliada y más precisa de la fase 5 en la respuesta de jeb (Válido tanto para el modo por lotes como para el modo de línea de comandos)
Fase 5) Expansión retrasada
Esta fase se omite si se aplica alguna de las siguientes condiciones:
CALL
bloque entre paréntesis, ninguna forma de concatenación de comandos (&
,&&
o||
) o una tubería|
.El proceso de expansión retrasada se aplica a los tokens de forma independiente. Un comando puede tener múltiples tokens:
for ... in(TOKEN) do
if defined TOKEN
if exists TOKEN
if errorlevel TOKEN
if cmdextversion TOKEN
if TOKEN comparison TOKEN
, En que la comparación es uno de==
,equ
,neq
,lss
,leq
,gtr
, ogeq
No se realizan cambios en los tokens que no contienen
!
.Para cada ficha que contenga al menos una
!
, escanee cada carácter de izquierda a derecha en busca de^
o!
, y si lo encuentra, entonces!
o^
literales^
entonces^
!
, entoncesmire la siguiente cadena de caracteres, separándola antes
!
o<LF>
, y llámelos VAR (puede ser una lista vacía)!
entoncesReemplace
!VAR!
con el valor de VAR y continúe escaneandoelimine
!VAR!
y continúe escaneandoMira siguiente cadena de caracteres, rompiendo antes
!
,:
o<LF>
, y llamarlos VAR (puede ser una lista vacía). Si VAR se rompe antes:
y el siguiente carácter se!
incluye:
como último carácter en VAR y se rompe antes!
!
entoncesReemplace
!VAR!
con el valor de VAR y continúe escaneandoelimine
!VAR!
y continúe escaneando:
entonceselimine
!VAR:
y continúe escaneando~
entonces[integer][,[integer]]!
entonces Reemplace!VAR:~[integer][,[integer]]!
con una subcadena de valor de VAR (posiblemente resultando en una cadena vacía) y continúe el escaneo.[*]search=[replace]!
, donde la búsqueda puede incluir cualquier conjunto de caracteres excepto=
, y reemplazar puede incluir cualquier conjunto de caracteres excepto!
, luegoReemplazar
!VAR:[*]search=[replace]!
con el valor de VAR después de realizar la búsqueda y reemplazar (posiblemente resultando en una cadena vacía) y continuar escaneando!
Else conserva el inicio
!
!
fuente
%definedVar:a=b%
vs%undefinedVar:a=b%
y los%var:~0x17,-010%
formularios%<digit>
,%*
o%~
. Y el comportamiento cambia para variables indefinidas. Tal vez necesite abrir una segunda respuestaComo se señaló, los comandos pasan toda la cadena de argumentos en μSoft land, y depende de ellos analizar esto en argumentos separados para su propio uso. No hay coherencia en esto entre los diferentes programas y, por lo tanto, no hay un conjunto de reglas para describir este proceso. Realmente necesita verificar cada caso de esquina para cualquier biblioteca C que use su programa.
En cuanto a los
.bat
archivos del sistema , aquí está esa prueba:Ahora podemos ejecutar algunas pruebas. Vea si puede descubrir exactamente lo que μSoft está tratando de hacer:
Bien hasta ahora. (Dejaré de lado lo poco interesante
%cmdcmdline%
y%0
de ahora en adelante).Sin expansión de nombre de archivo.
Sin comillas, aunque las comillas evitan la división de argumentos.
Las comillas dobles consecutivas les hacen perder cualquier habilidad especial de análisis que hayan tenido. @ El ejemplo de Beniot:
Prueba: ¿Cómo se pasa el valor de cualquier entorno var como un argumento único (es decir, como
%1
) a un archivo bat?El análisis sensato parece estar roto para siempre.
Para su entretenimiento, trate de añadir diversos
^
,\
,'
,&
(& c.) Caracteres a estos ejemplos.fuente
t
esa "b c
. ¿Tiene una receta para conseguir esos 6 caracteres (a
, 2 × espacio,"
,b
, yc
) para aparecer como%1
el interior de una.cmd
? Aunque me gusta tu pensamiento.args "%t:"=""%"
está bastante cerca :-)Ya tiene algunas respuestas excelentes, pero para responder una parte de su pregunta:
Lo que está sucediendo allí es que debido a que tiene un espacio antes de =, se crea una variable llamada
%a<space>%
así cuandoecho %a %
se evalúa correctamente comob
.La parte restante
b% c%
se evalúa como texto sin formato + una variable indefinida% c%
, que debe repetirse como se ha escrito, para míecho %a %b% c%
devuelvebb% c%
Sospecho que la capacidad de incluir espacios en nombres de variables es más un descuido que una 'característica' planificada
fuente
editar: ver la respuesta aceptada, lo que sigue es incorrecto y explica solo cómo pasar una línea de comando a TinyPerl.
Con respecto a las citas, tengo la sensación de que el comportamiento es el siguiente:
"
se encuentra un, comienza el bloqueo de cuerdas"
es un es englobado"
se encuentra a:""
( por lo tanto, un triple"
), se agrega una comilla doble a la cadena"
( por lo tanto, un doble"
), entonces se agrega una comilla doble a la cadena y termina la secuencia global"
, termina el bloqueo de cadenaEn breve:
"a """ b "" c"""
consta de dos cadenas:a " b "
yc"
"a""
,"a"""
y"a""""
son todos la misma cadena si al final de una líneafuente
Tenga en cuenta que Microsoft ha publicado el código fuente de su Terminal. Puede funcionar de manera similar a la línea de comando con respecto al análisis sintáctico. Quizás alguien esté interesado en probar las reglas de análisis de ingeniería inversa de acuerdo con las reglas de análisis del terminal.
Enlace al código fuente.
fuente