我遇到了ss64.com,它为如何编写Windows命令解释器将运行的批处理脚本提供了很好的帮助。
但是,我无法找到批处理脚本的语法,扩展或不扩展的方法以及如何逃避事情的良好解释。
以下是我无法解决的示例问题:
foreach $i (@ARGV) { print '*' . $i ; }
),编译并以这种方式调用它:
my_script.exe "a ""b"" c"
→输出为*a "b*c
my_script.exe """a b c"""
→输出*"a*b*c"
echo
命令如何工作?在该命令中扩展了什么?for [...] %%I
,但在交互式会话中使用for [...] %I
?%PROCESSOR_ARCHITECTURE%
?我发现echo.exe %""PROCESSOR_ARCHITECTURE%
有效,是否有更好的解决方案?%
对如何匹配?例:
set b=a
,echo %a %b% c%
→%a a c%
set a =b
,echo %a %b% c%
→bb c%
set
命令时如何存储变量?例如,如果我set a=a" b
然后echo.%a%
,我会获得a" b
。但是,如果我使用UnxUtils中的echo.exe
,我会获得a b
。 %a%
如何以不同的方式扩展?谢谢你的灯光。
答案 0 :(得分:164)
答案 1 :(得分:59)
从命令窗口调用命令时,cmd.exe
(a.k.a。“shell”)不会对命令行参数进行标记化。大多数情况下,标记化是由新形成的进程的C / C ++运行时完成的,但这不一定是这样 - 例如,如果新进程不是用C / C ++编写的,或者新进程选择忽略{{ 1}}并为自己处理原始命令行(例如使用GetCommandLine())。在操作系统级别,Windows将未命名的命令行作为单个字符串传递给新进程。这与大多数* nix shell形成对比,其中shell在将参数传递给新形成的进程之前以一致,可预测的方式对参数进行标记。所有这些意味着您可能会在Windows上的不同程序中遇到极为不同的参数标记化行为,因为单个程序通常会将参数标记化放在自己手中。
如果它听起来像无政府状态,那就是。但是,由于大量Windows程序 使用Microsoft C / C ++运行时的argv
,因此理解how the MSVCRT tokenizes参数通常很有用。这是一段摘录:
Microsoft“批处理语言”(argv
)对于这种无政府环境也不例外,它已经为标记化和转义开发了自己独特的规则。在将参数传递给新执行的进程之前,它看起来像cmd.exe的命令提示符确实对命令行参数进行了一些预处理(主要用于变量替换和转义)。您可以在本页的jeb和dbenham的优秀答案中阅读有关批处理语言和cmd转义的低级详细信息的更多信息。
让我们在C中构建一个简单的命令行实用程序,并查看它对您的测试用例的描述:
.bat
(注意:argv [0]始终是可执行文件的名称,为简洁起见,在下面省略。在Windows XP SP3上测试。使用Visual Studio 2005编译。)
int main(int argc, char* argv[]) {
int i;
for (i = 0; i < argc; i++) {
printf("argv[%d][%s]\n", i, argv[i]);
}
return 0;
}
我自己的一些测试:
> test.exe "a ""b"" c"
argv[1][a "b" c]
> test.exe """a b c"""
argv[1]["a b c"]
> test.exe "a"" b c
argv[1][a" b c]
答案 2 :(得分:43)
答案 3 :(得分:7)
正如所指出的那样,命令在μSoftland中传递整个参数字符串,由它们将它解析为单独的参数供自己使用。在不同的程序之间没有任何一致性,因此没有一套规则来描述这个过程。你真的需要检查你的程序使用的任何C库的每个角落案例。
就系统.bat
文件而言,这是测试:
c> type args.cmd
@echo off
echo cmdcmdline:[%cmdcmdline%]
echo 0:[%0]
echo *:[%*]
set allargs=%*
if not defined allargs goto :eof
setlocal
@rem Wot about a nice for loop?
@rem Then we are in the land of delayedexpansion, !n!, call, etc.
@rem Plays havoc with args like %t%, a"b etc. ugh!
set n=1
:loop
echo %n%:[%1]
set /a n+=1
shift
set param=%1
if defined param goto :loop
endlocal
现在我们可以进行一些测试了。看看你是否可以弄清楚μSoft正在尝试做什么:
C>args a b c
cmdcmdline:[cmd.exe ]
0:[args]
*:[a b c]
1:[a]
2:[b]
3:[c]
到目前为止很好。 (从现在开始,我将忽略无趣的%cmdcmdline%
和%0
。)
C>args *.*
*:[*.*]
1:[*.*]
没有文件名扩展。
C>args "a b" c
*:["a b" c]
1:["a b"]
2:[c]
没有报价剥离,但报价确实阻止了参数拆分。
c>args ""a b" c
*:[""a b" c]
1:[""a]
2:[b" c]
连续双引号会导致它们失去任何特殊的解析能力。 @Beniot的例子:
C>args "a """ b "" c"""
*:["a """ b "" c"""]
1:["a """]
2:[b]
3:[""]
4:[c"""]
测验:如何将任何环境var的值作为单个参数(即%1
)传递给bat文件?
c>set t=a "b c
c>set t
t=a "b c
c>args %t%
1:[a]
2:["b c]
c>args "%t%"
1:["a "b]
2:[c"]
c>Aaaaaargh!
Sane解析似乎永远破碎了。
为了您的娱乐,请尝试在这些示例中添加其他^
,\
,'
,&
(&amp; c。)字符。
答案 4 :(得分:5)
上面你已经有了一些很好的答案,但要回答你问题的一部分:
set a =b, echo %a %b% c% → bb c%
正在发生的事情是因为你在=之前有一个空格,所以创建了一个名为%a<space>%
的变量
因此,当echo %a %
被正确评估为b
。
然后将剩余部分b% c%
评估为纯文本+未定义变量% c%
,应该按类型回显,对我来说echo %a %b% c%
返回bb% c%
我怀疑在变量名称中包含空格的能力更多的是疏忽而不是计划的特征&#39;
答案 5 :(得分:1)
FOR
-循环元变量扩展这是accepted answer(适用于批处理文件模式和命令行模式)中 第4阶段 的扩展说明。当然,for
命令必须处于活动状态。下面描述do
子句之后的命令行部分的处理。请注意,在批处理文件模式下,由于上述%%
的立即扩展阶段(阶段1)),%
已转换为%
。>
%
-符号;如果找到一个,则:
~
;如果是,则:
fdpnxsatz
中尽可能多地使用以下字符(每个字符甚至多次),这些字符位于定义for
变量引用或{{1}的字符之前}-标志;如果遇到这样的$
符号,则:
$
1 ;如果找到,则:
:
之后有一个字符,请将其用作:
变量引用,并按预期方式扩展,除非未定义,否则不要扩展并在该字符位置继续扫描; for
是最后一个字符,则 :
将崩溃! cmd.exe
)不会展开任何内容; :
-符号)使用所有修饰符扩展$
变量,除非未定义,否则不要扩展并在该字符位置继续扫描; for
或命令扩展名被禁用),请检查下一个字符:
~
,请不要展开任何内容,并在该字符位置 2 ; %
变量引用并扩展,除非未定义,否则不要扩展; 1)for
和$
之间的字符串被认为是环境变量的名称,它甚至可以为空。由于环境变量不能使用空名称,因此其行为与未定义的环境变量相同。
2)这意味着名为:
的{{1}}元变量如果没有for
修饰符就不能扩展。
原始来源:How to safely echo FOR variable %%~p followed by a string literal
答案 6 :(得分:0)
编辑:看到接受的答案,后面的内容是错误的,只解释了如何将命令行传递给TinyPerl。
关于引号,我觉得行为如下:
"
时,字符串通配开始"
的每个字符都是全球化的"
时:
""
(因此为三"
),则会在字符串中添加双引号"
(因此是一个双"
),则会在字符串中添加双引号并将字符串全局结束"
,则字符串通道结束简而言之:
"a """ b "" c"""
由两个字符串组成:a " b "
和c"
"a""
,"a"""
和"a""""
都是相同的字符串
答案 7 :(得分:0)
请注意,Microsoft已发布其终端的源代码。就语法分析而言,它可能类似于命令行。也许有人有兴趣根据终端的解析规则测试反向工程解析规则。
Link到源代码。