【问题标题】:Does "&" cause a break in echo command, how to fix?“&”是否会导致 echo 命令中断,如何解决?
【发布时间】:2016-10-26 05:53:56
【问题描述】:

我正在使用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.

【问题讨论】:

  • &符号对cmd有特殊含义,即命令连接运算符,如echo abc & echo def;要回显文字 &,您需要像 ^& 一样对其进行转义; <>|^ 以及 () 也是如此,如果 echo 命令位于带括号的代码块中。要输出空行,请使用echo(。我建议将重定向放在行首,例如>>"zip.vbs" echo Some text.>>"zip.vbs" echo(,以避免在输出中出现尾随空格或在> 之前的最后一个字符是数字时出现奇怪的行为。
  • 在每个 "&" 之前添加 "^" 效果很好。看起来你的评论应该是一个答案!谢谢。
  • 它即将推出......

标签: batch-file echo


【解决方案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) 放在行首,以避免在回显字符串以单个数字结尾时出现奇怪的行为,因为这可能被视为重定向的流标识符 ( 0STDIN1STDOUT2STDERR39 是未定义的;@ 987654349@ 与0< 相同,>/>>1>/1>> 相同)。例如,echo X=2>"zip.vbs" 将在控制台上显示X=,因为 STDOUT 没有被重定向,并且没有任何内容写入 zip.vbs,因为这里 STDERR 为空。您可以通过在 > 前面插入 SPACE 来避免这种情况,但它也会被回显。更好的方法是将整个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 直接嵌入到批处理文件中,位于可执行批处理代码之后。为此,您需要在 VBScript 部分之前放置 exit /B。 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 不为空,则输出当前行,否则不输出。延迟扩展切换为不会丢失文本中的任何感叹号。

【讨论】:

    猜你喜欢
    • 2011-05-19
    • 2018-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-13
    • 1970-01-01
    • 2020-11-22
    相关资源
    最近更新 更多