Windows 命令解释器 (CMD.EXE) 如何解析脚本? [英] How does the Windows Command Interpreter (CMD.EXE) parse scripts?

查看:29
本文介绍了Windows 命令解释器 (CMD.EXE) 如何解析脚本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了 ss64.com,它提供了有关如何编写 Windows 命令解释器将要执行的批处理脚本的很好帮助运行.

I ran into ss64.com which provides good help regarding how to write batch scripts that the Windows Command Interpreter will run.

但是,对于批处理脚本的语法,事物如何扩展或不扩展,以及如何逃避事物,我一直无法找到很好的解释.

However, I have been unable to find a good explanation of the grammar of batch scripts, how things expand or do not expand, and how to escape things.

以下是我无法解决的示例问题:

Here are sample questions that I have not been able to solve:

  • 如何管理报价系统?我做了一个 TinyPerl 脚本
    ( foreach $i (@ARGV) { print '*' . $i ; } ),编译它并这样调用它:
    • my_script.exe "a ""b""c" → 输出为 *a "b*c
    • my_script.exe """"a b c"""" → 输出 *"a*b*c"
    • How is the quote system managed? I made a TinyPerl script
      ( foreach $i (@ARGV) { print '*' . $i ; } ), compiled it and called it this way :
      • my_script.exe "a ""b"" c" → output is *a "b*c
      • my_script.exe """a b c""" → output it *"a*b*c"
      • set b=a , echo %a %b% c%%a a c%
      • set a =b, echo %a %b% c%bb% c%
      • set b=a , echo %a %b% c%%a a c%
      • set a =b, echo %a %b% c%bb% c%

      谢谢你的灯.

      推荐答案

      我们进行了实验来研究批处理脚本的语法.我们还研究了批处理模式和命令行模式之间的差异.

      We performed experiments to investigate the grammar of batch scripts. We also investigated differences between batch and command line mode.

      这里是批处理文件行解析器阶段的简要概述:

      Here is a brief overview of phases in the batch file line parser:

      阶段 0) 读取行:

      阶段 1) 百分比扩张:

      阶段 2) 处理特殊字符、标记化并构建缓存命令块: 这是一个复杂的过程,受引号、特殊字符、标记分隔符和脱字符转义等因素的影响.

      Phase 2) Process special characters, tokenize, and build a cached command block: This is a complex process that is affected by things such as quotes, special characters, token delimiters, and caret escapes.

      阶段 3) 回显解析的命令 仅当命令块不以 @ 开头,并且 ECHO 在上一步开始时为 ON.

      Phase 3) Echo the parsed command(s) Only if the command block did not begin with @, and ECHO was ON at the start of the preceding step.

      阶段 4) FOR %X 变量扩展: 仅当 FOR 命令处于活动状态并且正在处理 DO 之后的命令时.

      Phase 4) FOR %X variable expansion: Only if a FOR command is active and the commands after DO are being processed.

      阶段 5) 延迟扩展:仅当启用延迟扩展时

      阶段 5.3) 管道处理:仅当命令位于管道的任一侧时

      Phase 5.3) Pipe processing: Only if commands are on either side of a pipe

      阶段 5.5) 执行重定向:

      阶段 6) CALL 处理/插入符加倍: 仅当命令标记为 CALL 时

      Phase 6) CALL processing/Caret doubling: Only if the command token is CALL

      阶段 7) 执行: 命令被执行

      Phase 7) Execute: The command is executed

      以下是每个阶段的详细信息:

      Here are details for each phase:

      请注意,下面描述的阶段只是批处理解析器如何工作的模型.实际的 cmd.exe 内部结构可能无法反映这些阶段.但是这个模型在预测批处理脚本的行为方面是有效的.

      Note that the phases described below are only a model of how the batch parser works. The actual cmd.exe internals may not reflect these phases. But this model is effective at predicting behavior of batch scripts.

      Phase 0) Read Line:通过第一个读取输入的行.

      Phase 0) Read Line: Read line of input through first <LF>.

      • 当读取一行要解析为命令时,(0x1A)被读取为(LineFeed 0x0A)
      • 当 GOTO 或 CALL 在扫描 :label 时读取行​​时, 被视为自身 - 不是 转换为
      • When reading a line to be parsed as a command, <Ctrl-Z> (0x1A) is read as <LF> (LineFeed 0x0A)
      • When GOTO or CALL reads lines while scanning for a :label, <Ctrl-Z>, is treated as itself - it is not converted to <LF>

      阶段 1) 百分比扩张:

      • %% 替换为单个 %
      • 参数的扩展(%*%1%2 等)
      • %var% 的扩展,如果 var 不存在则用空替换
      • 第一行被截断 不在 %var% 扩展范围内
      • 有关完整说明,请阅读 dbenham 同一主题:百分比阶段
      • A double %% is replaced by a single %
      • Expansion of arguments (%*, %1, %2, etc.)
      • Expansion of %var%, if var does not exist replace it with nothing
      • Line is truncated at first <LF> not within %var% expansion
      • For a complete explanation read the first half of this from dbenham Same thread: Percent Phase

      阶段 2) 处理特殊字符、标记化并构建缓存命令块: 这是一个复杂的过程,受引号、特殊字符、标记分隔符和脱字符转义等因素的影响.下面是这个过程的近似.

      Phase 2) Process special characters, tokenize, and build a cached command block: This is a complex process that is affected by things such as quotes, special characters, token delimiters, and caret escapes. What follows is an approximation of this process.

      有些概念在整个阶段都很重要.

      There are concepts that are important throughout this phase.

      • 令牌只是被视为一个单元的字符串.
      • 令牌由令牌定界符分隔.标准的标记分隔符是 ; , = <0x0B> <0x0C><0xFF>
        连续的标记分隔符被视为一个 - 标记分隔符之间没有空标记
      • 带引号的字符串中没有标记分隔符.整个带引号的字符串始终被视为单个标记的一部分.单个标记可能由带引号的字符串和不带引号的字符组合而成.

      以下字符在此阶段可能具有特殊含义,具体取决于上下文:<CR> ^ ( @ & | < > ; , = <0x0B> <0x0C> <0xFF>

      The following characters may have special meaning in this phase, depending on context: <CR> ^ ( @ & | < > <LF> <space> <tab> ; , = <0x0B> <0x0C> <0xFF>

      从左到右查看每个字符:

      Look at each character from left to right:

      • 如果 <CR> 然后将其删除,就好像它从未存在过一样(除了奇怪的 重定向行为)
      • 如果是插入符号 (^),则转义下一个字符,并删除转义插入符号.转义字符失去所有特殊含义( 除外).
      • 如果是引用 ("),则切换引用标志.如果引用标志处于活动状态,则只有 " 是特殊的.所有其他字符都会失去它们的特殊含义,直到下一个引号将引号标志关闭.无法逃避结束语.所有引用的字符总是在同一个标​​记内.
      • 总是关闭引用标志.其他行为因上下文而异,但引号永远不会改变 的行为.
        • 转义
          • 被剥离
          • 下一个字符被转义.如果在行缓冲区的末尾,则下一行由阶段 1 和 1.5 读取和处理,并在转义下一个字符之前附加到当前行.如果下一个字符是 <LF>,则将其视为文字,这意味着此过程不是递归的.
          • If <CR> then remove it, as if it were never there (except for weird redirection behavior)
          • If a caret (^), the next character is escaped, and the escaping caret is removed. Escaped characters lose all special meaning (except for <LF>).
          • If a quote ("), toggle the quote flag. If the quote flag is active, then only " and <LF> are special. All other characters lose their special meaning until the next quote toggles the quote flag off. It is not possible to escape the closing quote. All quoted characters are always within the same token.
          • <LF> always turns off the quote flag. Other behaviors vary depending on context, but quotes never alter the behavior of <LF>.
            • Escaped <LF>
              • <LF> is stripped
              • The next character is escaped. If at the end of line buffer, then the next line is read and processed by phases 1 and 1.5 and appended to the current one before escaping the next character. If the next character is <LF>, then it is treated as a literal, meaning this process is not recursive.
              • 被剥离并终止当前行的解析.
              • 行缓冲区中的任何剩余字符都将被忽略.
              • <LF> is stripped and parsing of the current line is terminated.
              • Any remaining characters in the line buffer are simply ignored.
              • 转成
              • 如果在行缓冲区的末尾,则读取下一行并将其附加到当前行.
              • 被转换成被当作一部分处理命令方块的下一行.
              • 如果在行缓冲区的末尾,则读取下一行并将其附加到空格中.
              • <LF> is converted into <LF><space>, and the <space> is treated as part of the next line of the command block.
              • If at the end of line buffer, then the next line is read and appended to the space.
              • 在管道 (|) 的情况下,每一侧都是一个单独的命令(或命令块),在阶段 5.3 中得到特殊处理
              • &&&|| 命令串联的情况下,串联的每一侧都被视为一个单独的命令.
              • <<<<>>>的情况下> redirection,重定向子句被解析,暂时去掉,然后追加到当前命令的末尾.重定向子句由可选的文件句柄数字、重定向运算符和重定向目标标记组成.
                • 如果重定向运算符之前的标记是单个未转义的数字,则该数字指定要重定向的文件句柄.如果没有找到句柄标记,则输出重定向默认为 1(stdout),输入重定向默认为 0(stdin).
                • In the case of a pipe (|), each side is a separate command (or command block) that gets special handling in phase 5.3
                • In the case of &, &&, or || command concatenation, each side of the concatenation is treated as a separate command.
                • In the case of <, <<, >, or >> redirection, the redirection clause is parsed, temporarily removed, and then appended to the end of the current command. A redirection clause consists of an optional file handle digit, the redirection operator, and the redirection destination token.
                  • If the token that precedes the redirection operator is a single unescaped digit, then the digit specifies the file handle to be redirected. If the handle token is not found, then output redirection defaults to 1 (stdout), and input redirection defaults to 0 (stdin).
                  • 特殊的 @ 已删除.
                  • 如果 ECHO 为 ON,则此命令以及此行上的任何后续串联命令都将从阶段 3 回声中排除.如果 @ 在开头的 ( 之前,则整个括号中的块将从阶段 3 回声中排除.
                  • The special @ is removed.
                  • If ECHO is ON, then this command, along with any following concatenated commands on this line, are excluded from the phase 3 echo. If the @ is before an opening (, then the entire parenthesized block is excluded from the phase 3 echo.
                  • 如果解析器不是在寻找命令标记,则 ( 并不特殊.
                  • 如果解析器正在寻找命令标记并找到(,则开始一个新的复合语句并增加括号计数器
                  • 如果括号计数器是 >0 然后 ) 终止复合语句并递减括号计数器.
                  • 如果到达行尾并且括号计数器是 >0 那么下一行将被附加到复合语句中(再次从阶段 0 开始)
                  • 如果括号计数器为 0 并且解析器正在寻找命令,则 ) 的功能类似于 REM 语句,只要它后面紧跟一个标记分隔符、特殊字符、换行符或文件结尾
                    • ^(可以连接行)外,所有特殊字符都失去意义
                    • 一旦到达逻辑行的末尾,整个命令"就会被执行.被丢弃.
                    • If the parser is not looking for a command token, then ( is not special.
                    • If the parser is looking for a command token and finds (, then start a new compound statement and increment the parenthesis counter
                    • If the parenthesis counter is > 0 then ) terminates the compound statement and decrements the parenthesis counter.
                    • If the line end is reached and the parenthesis counter is > 0 then the next line will be appended to the compound statement (starts again with phase 0)
                    • If the parenthesis counter is 0 and the parser is looking for a command, then ) functions similar to a REM statement as long as it is immediately followed by a token delimiter, special character, newline, or end-of-file
                      • All special characters lose their meaning except ^ (line concatenation is possible)
                      • Once the end of the logical line is reached, the entire "command" is discarded.
                      • 删除命令标记之前的前导标记分隔符
                      • 解析命令标记时,(除了标准标记分隔符外,还充当命令标记分隔符
                      • 后续令牌的处理取决于命令.
                      • Leading token delimiters prior to the command token are stripped
                      • When parsing the command token, ( functions as a command token delimiter, in addition to the standard token delimiters
                      • The handling of subsequent tokens depends on the command.
                      • IF 分为两个或三个独立处理的不同部分.IF 构造中的语法错误将导致致命的语法错误.
                        • 比较操作是一直流到第 7 阶段的实际命令
                          • 所有 IF 选项都在第 2 阶段完全解析.
                          • 连续的标记分隔符合并为一个空格.
                          • 根据比较运算符,将标识一两个值标记.
                          • 通过 DO 的部分是贯穿第 7 阶段的实际 FOR 迭代命令
                            • 在第 2 阶段完全解析所有 FOR 选项.
                            • 带括号的 IN 子句将 视为 .解析 IN 子句后,所有标记连接在一起形成一个标记.
                            • 在整个 FOR 命令到 DO 中,连续的未转义/未加引号的标记定界符合并为一个空格.
                            • The portion through DO is the actual FOR iteration command that flows all the way through phase 7
                              • All FOR options are fully parsed in phase 2.
                              • The IN parenthesized clause treats <LF> as <space>. After the IN clause is parsed, all tokens are concatenated together to form a single token.
                              • Consecutive unescaped/unquoted token delimiters collapse into a single space throughout the FOR command through DO.
                              • 仅解析一个参数标记 - 解析器忽略第一个参数标记之后的字符.
                              • REM 命令可能出现在第 3 阶段的输出中,但该命令永远不会被执行,并且原始参数文本会被回显 - 不会删除转义插入符号,除了...
                                • 如果只有一个参数标记以未转义的 ^ 结尾,则该参数标记将被丢弃,随后的行将被解析并附加到 REM.如此重复直到有多个标记,或者最后一个字符不是 ^.
                                • Only one argument token is parsed - the parser ignores characters after the first argument token.
                                • The REM command may appear in phase 3 output, but the command is never executed, and the original argument text is echoed - escaping carets are not removed, except...
                                  • If there is only one argument token that ends with an unescaped ^ that ends the line, then the argument token is thrown away, and the subsequent line is parsed and appended to the REM. This repeats until there is more than one token, or the last character is not ^.
                                  • 令牌通常被视为未执行的标签.
                                    • 该行的其余部分被解析,但是 )<>&| 不再具有特殊含义.该行的整个其余部分被视为标签命令"的一部分.
                                    • ^ 仍然很特殊,这意味着可以使用换行符将后续行附加到标签.
                                    • 括号内的未执行标签将导致致命的语法错误,除非它紧跟在下一行的命令或已执行标签之后.
                                      • ( 对于 Unexecuted Label 之后的第一个命令不再具有特殊含义.
                                      • The token is normally treated as an Unexecuted Label.
                                        • The remainder of the line is parsed, however ), <, >, & and | no longer have special meaning. The entire remainder of the line is considered to be part of the label "command".
                                        • The ^ continues to be special, meaning that line continuation can be used to append the subsequent line to the label.
                                        • An Unexecuted Label within a parenthesized block will result in a fatal syntax error unless it is immediately followed by a command or Executed Label on the next line.
                                          • ( no longer has special meaning for the first command that follows the Unexecuted Label.
                                          • 在标签标记之前有重定向,并且有|管道或&&&|| 命令串联就行了.
                                          • 在标签标记之前有重定向,并且命令位于带括号的块内.
                                          • 标签标记是带括号的块内一行的第一个命令,上面的行以未执行的标签结尾.
                                          • There is redirection that precedes the label token, and there is a | pipe or &, &&, or || command concatenation on the line.
                                          • There is redirection that precedes the label token, and the command is within a parenthesized block.
                                          • The label token is the very first command on a line within a parenthesized block, and the line above ended with an Unexecuted Label.
                                          • 标签、它的参数和它的重定向都被排除在阶段 3 的任何回声输出中
                                          • 该行上的任何后续串联命令都将被完全解析和执行.

                                          阶段 3) 回显解析的命令 仅当命令块不以 @ 开头,并且 ECHO 在上一步开始时为 ON.

                                          Phase 3) Echo the parsed command(s) Only if the command block did not begin with @, and ECHO was ON at the start of the preceding step.

                                          阶段 4) FOR %X 变量扩展: 仅当 FOR 命令处于活动状态并且正在处理 DO 之后的命令时.

                                          Phase 4) FOR %X variable expansion: Only if a FOR command is active and the commands after DO are being processed.

                                          • 此时,批处理的第 1 阶段已经将像 %%X 这样的 FOR 变量转换为 %X.命令行对阶段 1 有不同的百分比扩展规则.这就是命令行使用 %X 而批处理文件使用 %%X 作为 FOR 变量的原因.
                                          • FOR 变量名区分大小写,但 ~modifiers 不区分大小写.
                                          • ~modifiers 优先于变量名称.如果 ~ 后面的字符既是修饰符又是有效的 FOR 变量名,并且存在作为活动 FOR 变量名的后续字符,则该字符被解释为修饰符.
                                          • FOR 变量名称是全局的,但仅在 DO 子句的上下文中.如果从 FOR DO 子句中调用例程,则不会在 CALL 例程中扩展 FOR 变量.但是如果例程有自己的 FOR 命令,那么所有当前定义的 FOR 变量都可以被内部 DO 命令访问.
                                          • FOR 变量名可以在嵌套的 FOR 中重复使用.内部 FOR 值优先,但一旦 INNER FOR 关闭,外部 FOR 值就会恢复.
                                          • 如果 ECHO 在此阶段开始时为 ON,则重复阶段 3) 以在扩展 FOR 变量后显示解析的 DO 命令.
                                          • At this point, phase 1 of batch processing will have already converted a FOR variable like %%X into %X. The command line has different percent expansion rules for phase 1. This is the reason that command lines use %X but batch files use %%X for FOR variables.
                                          • FOR variable names are case sensitive, but ~modifiers are not case sensitive.
                                          • ~modifiers take precedence over variable names. If a character following ~ is both a modifier and a valid FOR variable name, and there exists a subsequent character that is an active FOR variable name, then the character is interpreted as a modifier.
                                          • FOR variable names are global, but only within the context of a DO clause. If a routine is CALLed from within a FOR DO clause, then the FOR variables are not expanded within the CALLed routine. But if the routine has its own FOR command, then all currently defined FOR variables are accessible to the inner DO commands.
                                          • FOR variable names can be reused within nested FORs. The inner FOR value takes precedence, but once the INNER FOR closes, then the outer FOR value is restored.
                                          • If ECHO was ON at the start of this phase, then phase 3) is repeated to show the parsed DO commands after the FOR variables have been expanded.

                                          ---- 从这一点开始,阶段 2 中识别的每个命令都被单独处理.
                                          ---- 完成一个命令的第 5 到第 7 阶段,然后再进行下一个命令.

                                          阶段 5) 延迟扩展:仅当延迟扩展打开时,命令不在 括号中在管道的任一侧阻塞,并且命令不是 裸体"批处理脚本(不带括号的脚本名称、CALL、命令串联或管道).

                                          Phase 5) Delayed Expansion: Only if delayed expansion is on, the command is not in a parenthesized block on either side of a pipe, and the command is not a "naked" batch script (script name without parentheses, CALL, command concatenation, or pipe).

                                          • 一个命令的每个标记都被独立解析为延迟扩展.
                                            • 大多数命令解析两个或多个标记 - 命令标记、参数标记和每个重定向目标标记.
                                            • FOR 命令仅解析 IN 子句标记.
                                            • IF 命令仅解析比较值 - 一个或两个,具体取决于比较运算符.
                                            • 如果是插入符号(^),下一个字符没有特殊含义,插入符号本身被删除
                                            • 如果是感叹号,则搜索下一个感叹号(不再观察到插入符号),扩展到变量的值.
                                              • 连续打开的!被折叠成一个!
                                              • 删除任何剩余的未配对的 !
                                              • If it is a caret (^) the next character has no special meaning, the caret itself is removed
                                              • If it is an exclamation mark, search for the next exclamation mark (carets are not observed anymore), expand to the value of the variable.
                                                • Consecutive opening ! are collapsed into a single !
                                                • Any remaining unpaired ! is removed

                                                阶段 5.3) 管道处理:仅当命令位于管道的任一侧时
                                                管道的每一侧都是独立异步处理的.

                                                Phase 5.3) Pipe processing: Only if commands are on either side of a pipe
                                                Each side of the pipe is processed independently and asynchronously.

                                                • 如果命令在 cmd.exe 内部,或者它是一个批处理文件,或者如果它是一个带括号的命令块,那么它会通过 %comspec%/S/D 在一个新的 cmd.exe 线程中执行/c"commandBlock",所以命令方块会重新开始阶段,但这次是在命令行模式下.
                                                  • 如果是括号括起来的命令块,那么所有带有命令前后的都被转换为&.其他 被剥离.
                                                  • If command is internal to cmd.exe, or it is a batch file, or if it is a parenthesized command block, then it is executed in a new cmd.exe thread via %comspec% /S /D /c" commandBlock", so the command block gets a phase restart, but this time in command line mode.
                                                    • If a parenthesized command block, then all <LF> with a command before and after are converted to <space>&. Other <LF> are stripped.

                                                    阶段 5.5) 执行重定向:现在执行在阶段 2 中发现的任何重定向.

                                                    Phase 5.5) Execute Redirection: Any redirection that was discovered in phase 2 is now executed.

                                                    • The results of phases 4 and 5 can impact the redirection that was discovered in phase 2.
                                                    • If the redirection fails, then the remainder of the command is aborted. Note that failed redirection does not set ERRORLEVEL to 1 unless || is used.

                                                    阶段 6) CALL 处理/插入符加倍: 仅当命令标记为 CALL,或者第一个出现的标准标记定界符之前的文本为 CALL 时.如果从较大的命令标记解析 CALL,则在继续之前未使用的部分将添加到参数标记中.

                                                    Phase 6) CALL processing/Caret doubling: Only if the command token is CALL, or if the text before the first occurring standard token delimiter is CALL. If CALL is parsed from a larger command token, then the unused portion is prepended to the arguments token before proceeding.

                                                    • 扫描参数标记以查找未加引号的 /?.如果在令牌内的任何位置找到,则中止第 6 阶段并继续第 7 阶段,其中将打印 CALL 的帮助.
                                                    • 去掉第一个CALL,这样可以堆叠多个CALL
                                                    • 将所有插入符号加倍
                                                    • 重新开始阶段 1、1.5 和 2,但不要继续进行阶段 3
                                                      • 任何重复的插入符号只要没有被引用,就会减少回一个插入符号.但不幸的是,引用的插入符号仍然翻了一番.
                                                      • 第一阶段略有变化- 步骤 1.2 或 1.3 中的扩展错误中止 CALL,但该错误不是致命的 - 批处理继续.
                                                      • 第 2 阶段的任务略有改变
                                                        • 任何在第一轮阶段 2 中未检测到的新出现的未加引号、未转义的重定向都会被检测到,但会被删除(包括文件名),而不会实际执行重定向
                                                        • 行尾任何新出现的未加引号、未转义的插入符号都将被删除,而不执行续行
                                                        • 如果检测到以下任何一项,CALL 将无错误地中止
                                                          • 新出现的未加引号、未转义的 &|
                                                          • 生成的命令标记以未加引号、未转义的 (
                                                          • 删除 CALL 后的第一个标记以 @
                                                          • 开头
                                                          • Scan the arguments token for an unquoted /?. If found anywhere within the tokens, then abort phase 6 and proceed to Phase 7, where the HELP for CALL will be printed.
                                                          • Remove the first CALL, so multiple CALL's can be stacked
                                                          • Double all carets
                                                          • Restart phases 1, 1.5, and 2, but do not continue to phase 3
                                                            • Any doubled carets are reduced back to one caret as long as they are not quoted. But unfortunately, quoted carets remain doubled.
                                                            • Phase 1 changes a bit - Expansion errors in step 1.2 or 1.3 abort the CALL, but the error is not fatal - batch processing continues.
                                                            • Phase 2 tasks are altered a bit
                                                              • Any newly appearing unquoted, unescaped redirection that was not detected in the first round of phase 2 is detected, but it is removed (including the file name) without actually performing the redirection
                                                              • Any newly appearing unquoted, unescaped caret at the end of the line is removed without performing line continuation
                                                              • The CALL is aborted without error if any of the following are detected
                                                                • Newly appearing unquoted, unescaped & or |
                                                                • The resultant command token begins with unquoted, unescaped (
                                                                • The very first token after the removed CALL began with @
                                                                • 将当前批处理脚本文件位置推送到调用堆栈上,以便在 CALL 完成时可以从正确位置恢复执行.
                                                                • 使用所有结果标记为 CALL 设置 %0、%1、%2、...%N 和 %* 参数标记
                                                                • 如果命令标记是一个以 : 开头的标签,则
                                                                  • 重新启动第 5 阶段.这会影响调用 :label 的内容.但是由于 %0 等标记已经设置,它不会改变传递给 CALLed 例程的参数.
                                                                  • 执行 GOTO 标签以将文件指针定位在子例程的开头(忽略可能跟在 :label 之后的任何其他标记) 有关 GOTO 工作原理的规则,请参阅第 7 阶段.
                                                                    • Push the current batch script file position on the call stack so that execution can resume from the correct position when the CALL is completed.
                                                                    • Setup the %0, %1, %2, ...%N and %* argument tokens for the CALL, using all resultant tokens
                                                                    • If the command token is a label that begins with :, then
                                                                      • Restart Phase 5. This can impact what :label is CALLed. But since the %0 etc. tokens have already been setup, it will not alter the arguments that are passed to the CALLed routine.
                                                                      • Execute GOTO label to position the file pointer at the beginning of the subroutine (ignore any other tokens that may follow the :label) See Phase 7 for rules on how GOTO works.
                                                                        • If the :label token is missing, or the :label is not found, then the call stack is immediately popped to restore the saved file position, and the CALL is aborted.
                                                                        • If the :label happens to contain /?, then GOTO help is printed instead of searching for the :label. The file pointer does not move, such that code after the CALL is executed twice, once in the CALL context, and then again after the CALL return. See Why CALL prints the GOTO help message in this script?And why command after that are executed twice? for more info.

                                                                        阶段7)执行:命令被执行

                                                                        • 7.1 - 执行内部命令 - 如果命令标记被引用,则跳过此步骤.否则,尝试解析出内部命令并执行.
                                                                          • 进行以下测试以确定未加引号的命令标记是否代表内部命令:
                                                                            • 如果命令标记与内部命令完全匹配,则执行它.
                                                                            • 否则在 + / [ ] 空格> , ; or =
                                                                              如果前面的文本是内部命令,那么记住该命令
                                                                              • 如果在命令行模式下,或者如果命令来自括号中的块,IF真或假命令块,FOR DO命令块,或涉及命令串联,则执行内部命令
                                                                              • Else(必须是批处理模式下的独立命令)扫描当前文件夹和 PATH 以查找基本名称与原始命令标记匹配的 .COM、.EXE、.BAT 或 .CMD 文件
                                                                                • 如果第一个匹配的文件是 .BAT 或 .CMD,则转到 7.3.exec 并执行该脚本
                                                                                • 否则(未找到匹配项或第一个匹配项是 .EXE 或 .COM)执行记住的内部命令
                                                                                • 7.1 - Execute internal command - If the command token is quoted, then skip this step. Otherwise, attempt to parse out an internal command and execute.
                                                                                  • The following tests are made to determine if an unquoted command token represents an internal command:
                                                                                    • If the command token exactly matches an internal command, then execute it.
                                                                                    • Else break the command token before the first occurrence of + / [ ] <space> <tab> , ; or =
                                                                                      If the preceding text is an internal command, then remember that command
                                                                                      • If in command line mode, or if the command is from a parenthesized block, IF true or false command block, FOR DO command block, or involved with command concatenation, then execute the internal command
                                                                                      • Else (must be a stand-alone command in batch mode) scan the current folder and the PATH for a .COM, .EXE, .BAT, or .CMD file whose base name matches the original command token
                                                                                        • If the first matching file is a .BAT or .CMD, then goto 7.3.exec and execute that script
                                                                                        • Else (match not found or first match is .EXE or .COM) execute the remembered internal command
                                                                                        • 如果 SET 命令在启用变量名称和扩展名之前有引号
                                                                                          设置名称=内容"忽略 --> value=content
                                                                                          然后将第一个等号和最后一个引号之间的文本用作内容(排除第一个等号和最后一个引号).忽略最后一个引号之后的文本.如果等号后没有引号,则该行的其余部分用作内容.
                                                                                        • 如果 SET 命令的名称前没有引号
                                                                                          <代码>设置名称=内容"不被忽略 --> value=content"不被忽略
                                                                                          然后将等号之后的整个剩余行用作内容,包括可能存在的任何和所有引号.
                                                                                        • If a SET command has a quote before the variable name and extensions are enabled
                                                                                          set "name=content" ignored --> value=content
                                                                                          then the text between the first equal sign and the last quote is used as the content (first equal and last quote excluded). Text after the last quote is ignored. If there is no quote after the equal sign, then the rest of the line is used as content.
                                                                                        • If a SET command does not have a quote before the name
                                                                                          set name="content" not ignored --> value="content" not ignored
                                                                                          then the entire remainder of the line after the equal is used as content, including any and all quotes that may be present.
                                                                                        • 如果这是一个迭代命令块输出的 FOR/F,则:
                                                                                          • IN 子句通过 CMD/C 在新的 cmd.exe 进程中执行.
                                                                                          • 命令块必须第二次完成整个解析过程,但这次是在命令行上下文中
                                                                                          • ECHO 开始时为 ON,延迟扩展通常开始时为禁用(取决于注册表设置)
                                                                                          • 一旦子 cmd.exe 进程终止,IN 子句命令块所做的所有环境更改都将丢失
                                                                                          • 定义了 FOR 变量值
                                                                                          • 然后处理已经解析的 DO 命令块,从阶段 4 开始.
                                                                                          • 从第一个参数标记解析标签
                                                                                          • 扫描下一次出现的标签
                                                                                            • 从当前文件位置开始
                                                                                            • 如果到达文件末尾,则循环回到文件开头并继续到原始起点.
                                                                                            • All argument tokens are ignored
                                                                                            • If the volume specified by the first character cannot be found, then abort with an error
                                                                                            • A command token of :: will always result in an error unless SUBST is used to define a volume for ::
                                                                                              If SUBST is used to define a volume for ::, then the volume will be changed, it will not be treated as a label.
                                                                                            • All argument tokens are ignored
                                                                                            • If the volume specified by the first character cannot be found, then abort with an error
                                                                                            • A command token of :: will always result in an error unless SUBST is used to define a volume for ::
                                                                                              If SUBST is used to define a volume for ::, then the volume will be changed, it will not be treated as a label.
                                                                                            • If in command line mode and the command is not quoted and does not begin with a volume specification, white-space, ,, ;, = or + then break the command token at the first occurrence of <space> , ; or = and prepend the remainder to the argument token(s).
                                                                                            • If the 2nd character of the command token is a colon, then verify the volume specified by the 1st character can be found.
                                                                                              If the volume cannot be found, then abort with an error.
                                                                                            • If in batch mode and the command token begins with :, then goto 7.4
                                                                                              Note that if the label token begins with ::, then this will not be reached because the preceding step will have aborted with an error unless SUBST is used to define a volume for ::.
                                                                                            • Identify the external command to execute.
                                                                                              • This is a complex process that may involve the current volume, current directory, PATH variable, PATHEXT variable, and or file associations.
                                                                                              • If a valid external command cannot be identified, then abort with an error.
                                                                                              • If in command line mode and the command is not quoted and does not begin with a volume specification, white-space, ,, ;, = or + then break the command token at the first occurrence of <space> , ; or = and prepend the remainder to the argument token(s).
                                                                                              • If the 2nd character of the command token is a colon, then verify the volume specified by the 1st character can be found.
                                                                                                If the volume cannot be found, then abort with an error.
                                                                                              • If in batch mode and the command token begins with :, then goto 7.4
                                                                                                Note that if the label token begins with ::, then this will not be reached because the preceding step will have aborted with an error unless SUBST is used to define a volume for ::.
                                                                                              • Identify the external command to execute.
                                                                                                • This is a complex process that may involve the current volume, current directory, PATH variable, PATHEXT variable, and or file associations.
                                                                                                • If a valid external command cannot be identified, then abort with an error.

                                                                                                Works like the BatchLine-Parser, except:

                                                                                                Works like the BatchLine-Parser, except:

                                                                                                Phase 1) Percent Expansion:

                                                                                                • No %*, %1 etc. argument expansion
                                                                                                • If var is undefined, then %var% is left unchanged.
                                                                                                • No special handling of %%. If var=content, then %%var%% expands to %content%.
                                                                                                • No %*, %1 etc. argument expansion
                                                                                                • If var is undefined, then %var% is left unchanged.
                                                                                                • No special handling of %%. If var=content, then %%var%% expands to %content%.

                                                                                                Phase 3) Echo the parsed command(s)

                                                                                                • This is not performed after phase 2. It is only performed after phase 4 for the FOR DO command block.

                                                                                                Phase 5) Delayed Expansion: only if DelayedExpansion is enabled

                                                                                                Phase 5) Delayed Expansion: only if DelayedExpansion is enabled

                                                                                                • If var is undefined, then !var! is left unchanged.
                                                                                                • If var is undefined, then !var! is left unchanged.

                                                                                                Phase 7) Execute Command

                                                                                                • Attempts to CALL or GOTO a :label result in an error.
                                                                                                • As already documented in phase 7, an executed label may result in an error under different scenarios.
                                                                                                  • Batch executed labels can only cause an error if they begin with ::
                                                                                                  • Command line executed labels almost always result in an error

                                                                                                  There are many different contexts where cmd.exe parses integer values from strings, and the rules are inconsistent:

                                                                                                  There are many different contexts where cmd.exe parses integer values from strings, and the rules are inconsistent:

                                                                                                  • SET/A
                                                                                                  • IF
                                                                                                  • %var:~n,m% (variable substring expansion)
                                                                                                  • FOR/F "TOKENS=n"
                                                                                                  • FOR/F "SKIP=n"
                                                                                                  • FOR/L %%A in (n1 n2 n3)
                                                                                                  • EXIT [/B] n
                                                                                                  • SET /A
                                                                                                  • IF
                                                                                                  • %var:~n,m% (variable substring expansion)
                                                                                                  • FOR /F "TOKENS=n"
                                                                                                  • FOR /F "SKIP=n"
                                                                                                  • FOR /L %%A in (n1 n2 n3)
                                                                                                  • EXIT [/B] n

                                                                                                  Details for these rules may be found at Rules for how CMD.EXE parses numbers

                                                                                                  Details for these rules may be found at Rules for how CMD.EXE parses numbers

                                                                                                  For anyone wishing to improve the cmd.exe parsing rules, there is a discussion topic on the DosTips forum where issues can be reported and suggestions made.

                                                                                                  For anyone wishing to improve the cmd.exe parsing rules, there is a discussion topic on the DosTips forum where issues can be reported and suggestions made.

                                                                                                  Hope it helps
                                                                                                  Jan Erik (jeb) - Original author and discoverer of phases
                                                                                                  Dave Benham (dbenham) - Much additional content and editing

                                                                                                  Hope it helps
                                                                                                  Jan Erik (jeb) - Original author and discoverer of phases
                                                                                                  Dave Benham (dbenham) - Much additional content and editing

                                                                                                  这篇关于Windows 命令解释器 (CMD.EXE) 如何解析脚本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆