是"&"导致echo命令中断,如何修复?

时间:2016-06-23 18:39:42

标签: batch-file echo

我在this question的答案中使用该技术让批处理文件输出另一个文件。问题是当我到达"&"符号,我得到错误。这是我的批处理文件,后跟错误输出:

@echo off>zip.vbs
@echo 'Get command-line arguments.>>zip.vbs
@echo Set objArgs = WScript.Arguments>>zip.vbs
@echo InputFolder = objArgs(0)>>zip.vbs
@echo ZipFile = objArgs(1)>>zip.vbs
@echo >>zip.vbs
@echo 'Create empty ZIP file.>>zip.vbs
@echo CreateObject("Scripting.FileSystemObject").CreateTextFile(ZipFile, True).Write "PK" & Chr(5) & Chr(6) & String(18, vbNullChar)>>zip.vbs
@echo >>zip.vbs
@echo Set objShell = CreateObject("Shell.Application")>>zip.vbs
@echo >>zip.vbs
@echo Set source = objShell.NameSpace(InputFolder).Items>>zip.vbs
@echo >>zip.vbs
@echo objShell.NameSpace(ZipFile).CopyHere(source)>>zip.vbs
@echo >>zip.vbs
@echo 'Required!>>zip.vbs
@echo wScript.Sleep 2000>>zip.vbs

生成此zip2.vbs文件:

'Get command-line arguments.
Set objArgs = WScript.Arguments
InputFolder = objArgs(0)
ZipFile = objArgs(1)
ECHO is off.
'Create empty ZIP file.
ECHO is off.
Set objShell = CreateObject("Shell.Application")
ECHO is off.
Set source = objShell.NameSpace(InputFolder).Items
ECHO is off.
objShell.NameSpace(ZipFile).CopyHere(source)
ECHO is off.
'Required!
wScript.Sleep 2000

此错误输出:

E:\scripts>zip2
CreateObject("Scripting.FileSystemObject").CreateTextFile(ZipFile, True).Write "
PK"
'Chr' is not recognized as an internal or external command,
operable program or batch file.
'Chr' is not recognized as an internal or external command,
operable program or batch file.
'String' is not recognized as an internal or external command,
operable program or batch file.

1 个答案:

答案 0 :(得分:1)

首先,让我对您的代码进行快速而严格的更正;解释如下:

@echo off
> "zip.vbs" echo('Get command-line arguments.
>>"zip.vbs" echo(Set objArgs = WScript.Arguments
>>"zip.vbs" echo(InputFolder = objArgs(0)
>>"zip.vbs" echo(ZipFile = objArgs(1)
>>"zip.vbs" echo(
>>"zip.vbs" echo('Create empty ZIP file.
>>"zip.vbs" echo(CreateObject("Scripting.FileSystemObject").CreateTextFile(ZipFile, True).Write "PK" ^& Chr(5) ^& Chr(6) ^& String(18, vbNullChar)
>>"zip.vbs" echo(
>>"zip.vbs" echo(Set objShell = CreateObject("Shell.Application")
>>"zip.vbs" echo(
>>"zip.vbs" echo(Set source = objShell.NameSpace(InputFolder).Items
>>"zip.vbs" echo(
>>"zip.vbs" echo(objShell.NameSpace(ZipFile).CopyHere(source)
>>"zip.vbs" echo(
>>"zip.vbs" echo('Required!
>>"zip.vbs" echo(wScript.Sleep 2000

&符号对cmd有特殊含义,它是命令连接运算符;例如,键入echo abc & echo def以将两个echo命令放在一行中。

要输出文字&,您需要使用像^&这样的插入符来转义它。 <>|以及^本身也是如此。 如果echo命令位于带括号的代码块中,您还需要转义(),否则它是可选的(所以我建议无论如何都要逃避它们)。

要输出空行,请使用echo(,因为echo会返回命令回显的开/关状态,如ECHO is on/off.。有广泛传播的方法,如echo.echo/来实现相同,但有些情况下,这些行为奇怪。 echo(是唯一安全的方法(虽然它的语法看起来很奇怪,我不得不承认)。顺便说一下,echo(也可用于回显非空文本。

如果您通过全局非重定向@echo off关闭命令回显,则不必在每个进一步的命令行之前加@

我建议将重定向部分(>>zip.vbs)放到行的开头,以避免在回显的字符串以单个数字结尾的情况下出现奇怪的行为,因为这可能被视为流的标识符重定向(0 STDIN 1 STDOUT 2 STDERR 和{{1 }} 3未定义; 9<相同,0< / >>> / {{1}相同}})。例如,1>将在控制台上显示1>>,因为 STDOUT 未被重定向,并且不向echo X=2>"zip.vbs"写入任何内容,因为 STDERR 是在这里空了。您可以通过在X=前插入 SPACE 来避免这种情况,但它也会被回应。更好的方法是将整个zip.vbs命令行放在>之间,因此数字与echo分开。但最好的解决方案应该是:()

当您将所有回声放在带括号的块中时,您也可以避免多次重定向和如此多的文件访问,该块仅被重定向一次(但如上所述,现在不要忘记从括号中删除):

>

这是一种完全不同的方法,不再需要进行转义。在可执行批处理代码之后,VBScript脚本>"zip.vbs" echo X=2直接嵌入批处理文件中。为此,您需要在VBScript部分之前放置@echo off > "zip.vbs" ( echo('Get command-line arguments. echo(Set objArgs = WScript.Arguments echo(InputFolder = objArgs^(0^) echo(ZipFile = objArgs^(1^) echo( echo('Create empty ZIP file. echo(CreateObject^("Scripting.FileSystemObject"^).CreateTextFile^(ZipFile, True^).Write "PK" ^& Chr^(5^) ^& Chr^(6^) ^& String^(18, vbNullChar^) echo( echo(Set objShell = CreateObject^("Shell.Application"^) echo( echo(Set source = objShell.NameSpace^(InputFolder^).Items echo( echo(objShell.NameSpace^(ZipFile^).CopyHere^(source^) echo( echo('Required! echo(wScript.Sleep 2000 ) 。 VBScript部分通过位于仅由zip.vbs组成的一对行之间来标识(这意味着包含VBScript部分的整个批处理文件不能单独包含这样的行;或者可以在每个VBScript行之前通过稍后删除的某个标识符前缀,但原理是相同的)。使用这种方法,VBScript行按字面输出,绝对不需要转义。所以我们走了:

exit /B

整个批处理文件由#循环读取。由于这会忽略空行,因此@echo off setlocal EnableExtensions DisableDelayedExpansion set "FLAG=" > "zip.vbs" ( for /F "delims=" %%L in ('findstr /N /R "^" "%~f0"') do ( set "LINE=%%L" setlocal EnableDelayedExpansion set "LINE=!LINE:*:=!" if not "!LINE:#=!"=="" ( if defined FLAG (echo(!LINE!) ) else ( endlocal if defined FLAG (set "FLAG=") else (set "FLAG=#") setlocal EnableDelayedExpansion ) endlocal ) ) endlocal exit /B ################################################################################ 'Get command-line arguments. Set objArgs = WScript.Arguments InputFolder = objArgs(0) ZipFile = objArgs(1) 'Create empty ZIP file. CreateObject("Scripting.FileSystemObject").CreateTextFile(ZipFile, True).Write "PK" & Chr(5) & Chr(6) & String(18, vbNullChar) Set objShell = CreateObject("Shell.Application") Set source = objShell.NameSpace(InputFolder).Items objShell.NameSpace(ZipFile).CopyHere(source) 'Required! wScript.Sleep 2000 ################################################################################ 用于在每行前面加上行号,冒号在for /F中不显示为空。稍后将在循环中删除此前缀。变量findstr在开头被清除,并且一旦找到仅包含for /F个字符的行就会切换。如果FLAG非空,则输出当前行,否则不输出。延迟扩展被切换为不会丢失文本中的任何惊叹号。