"&"会导致 echo 命令中断,如何解决?



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

@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.

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

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


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

@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直接嵌入到批处理文件中,在可执行批处理代码之后。为此,您需要将exit /B放在 VBScript 部分之前。VBScript 部分通过位于仅由#组成的一对行之间来标识(这意味着包括 VBScript 部分在内的整个批处理文件本身不得包含此类行;或者,每个 VBScript 行前面可以有一个稍后删除的某个标识器前缀,但原理是相同的)。使用此方法,VBScript 行按字面输出,绝对不需要转义。所以我们来了:

@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在开头被清除,并在找到仅由#个字符组成的行后立即切换。如果FLAG为非空,则输出当前行,否则不输出。切换延迟扩展以不丢失文本中的任何感叹号。

最新更新