如何在Windows批处理文件的ECHOed字符串中去除引号?

How do you strip quotes out of an ECHO'ed string in a Windows batch file?

我有一个正在创建的Windows批处理文件,但是我必须回显一个大的复杂字符串,因此我必须在两端加上双引号。问题在于引号也被回显到我正在写入的文件中。您如何回显这样的字符串并去除引号?

更新:

我花了最后两天的时间进行这项工作,终于能够将某些东西融合在一起。理查德的答案可以消除引号,但是即使我将ECHO放在子例程中并直接输出字符串,Windows仍然挂在字符串的字符上。我会接受Richard的回答,因为它可以回答所提出的问题。

我最终使用了Greg的sed解决方案,但是由于sed / windows错误/功能而不得不对其进行修改(它没有文档也没有帮助)。在Windows中使用sed有一些注意事项:您必须使用双引号而不是单引号,不能直接在字符串中对双引号进行转义,必须对字符串加引号,并使用^(所以^" ),然后指出下一节的引号。另外,有人指出,如果您通过管道将输入传递给sed,则字符串中存在管道的错误(我没有进行验证,因为在我的最终解决方案中,我只是发现一种不在字符串中间包含所有引号的方法,而只是删除所有引号,我永远无法使endquote本身被删除。)感谢所有帮助。


call命令具有内置的此功能。要引用呼叫帮助:

1
2
3
4
 Substitution of batch parameters (%n) has been enhanced.  You can
 now use the following optional syntax:

 %~1         - expands %1 removing any surrounding quotes (")

这是一个原始示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@echo off
setlocal
set mystring="this is some quoted text"
echo mystring=%mystring%
call :dequote %mystring%
echo ret=%ret%
endlocal
goto :eof

:dequote
setlocal
rem The tilde in the next line is the really important bit.
set thestring=%~1
endlocal&set ret=%thestring%
goto :eof

输出:

1
2
3
C:\>dequote
mystring="this is some quoted text"
ret=this is some quoted text

我应该将"环境变量隧道"技术(endlocal&set ret =%thestring%)归功于Tim Hill," Windows NT Shell脚本"。这是我找到的唯一一本涉及任何深度的批处理文件的书。


您可以使用%var:x=y%构造将所有x替换为y

请参见以下示例,它可以做什么:

1
2
3
4
5
6
set I="Text in quotes"
rem next line replaces" with blanks
set J=%I:"=%
echo original %I%
rem next line replaces the string 'in' with the string 'without'
echo stripped %J:in=without%


可以使用以下方法来打印不带引号的字符串:

1
echo|set /p=";lt;h1;gt;Hello;lt;/h1;gt;"

要从集合变量中删除所有引号,您需要延迟变量扩展来安全地扩展变量并对其进行处理。使用百分号(即%VAR%%1)进行扩展本质上是不安全的(它们容易受到命令注入的影响;有关详细信息,请阅读此内容)。

1
2
3
4
5
SETLOCAL EnableDelayedExpansion
SET VAR=A ^"quoted^" text.
REM This strips all quotes from VAR:
ECHO !VAR:^"=!
REM Really that's it.

要从文本文件或命令输出中去除引号,事情会变得复杂,因为使用延迟扩展,文本文档中的字符串如!VAR!将在不应该扩展的情况下(在FOR /F%%i扩展范围内)扩展。 (这是另一个漏洞-信息泄露-在其他地方没有记录。)

为了安全地解析文档,需要在启用延迟扩展的环境和禁用延迟的环境之间切换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
REM Suppose we fetch the text from text.txt

SETLOCAL DisableDelayedExpansion
REM The FOR options here employs a trick to disable both"delims"
REM characters (i.e. field separators) and"eol" character (i.e. comment
REM character).
FOR /F delims^=^ eol^= %%L IN (text.txt) DO (

    REM This expansion is safe because cmd.exe expands %%L after quotes
    REM parsing as long as DelayedExpansion is Disabled. Even when %%L
    REM can contain quotes, carets and exclamation marks.
    SET"line=%%L"

    CALL :strip_quotes
    REM Print out the result. (We can't use !line! here without delayed
    REM expansion, so do so in a subroutine.)
    CALL :print_line
)
ENDLOCAL
GOTO :EOF

REM Reads !line! variable and strips quotes from it.
:strip_quotes
    SETLOCAL EnableDelayedExpansion
    SET line=!line:^"=!

    REM Make the variable out of SETLOCAL
    REM I'm expecting you know how this works:
    REM (You may use ampersand instead:
    REM `ENDLOCAL ; SET"line=%line%"`
    REM I just present another way that works.)
    (
    ENDLOCAL
    SET"line=%line%"
    )
GOTO :EOF

:print_line
    SETLOCAL EnableDelayedExpansion
    ECHO !line!
    ENDLOCAL
GOTO :EOF

上面代码中的delims^=^ eol^=可能需要解释:
这有效地禁用了" delims"字符(即字段分隔符)和" eol"字符(即注释字符)。没有它," delims"将默认为制表符和空格,而" eol"将默认为分号。

  • eol=标记始终读取等号后的下一个字符。要禁用它,此令牌必须在选项字符串的末尾,以便" eol"不能使用任何字符,从而有效地将其禁用。如果在选项字符串中加上了引号,则可能使用引号(")作为" eol",因此我们不能在选项字符串中加上引号。
  • delims=选项不是选项字符串中的最后一个选项时,将以空格终止。 (要在" delims"中包含空格,它必须是FOR /F选项的最后一个选项。)因此delims=后跟一个空格,然后另一个选项禁用了" delims"。

我知道这实际上不是针对作者的,但是如果您需要将一些文本发送到文件中而不使用引号,则以下解决方案对我有效。您无需在echo命令中使用引号,只需将完整命令括在方括号中即可。

1
2
3
4
(
    echo first very long line
    echo second very long line with %lots% %of% %values%
) >"%filename%"

下面的批处理文件启动一系列程序,每个程序之后都有一个延迟。

问题是传递带有每个程序参数的命令行。这需要在程序参数周围加上引号,在进行调用时将其删除。这说明了批处理文件处理中的一些技术。

在本地子例程:mystart中查找如何传递引号中的参数,以及如何删除引号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@echo off

rem http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/if.mspx?mfr=true

rem Start programs with delay

rem  Wait n seconds
rem  n number retries to communicate with the IP address
rem  1000 milliseconds between the retries
rem  127.0.0.1 is the LocalHost
rem  start /b (silent)  /min (minimized) /belownormal (lower priority)
rem  /normal provides a no-op switch to hold the place of argument 1

rem  start  /normal"Opinions"  %SystemRoot%\explorer.exe /e,d:\agar\jobs\opinion
rem  ping 127.0.0.1 -n 8 -w 1000 > nul

rem   Remove quotes in Batch
rem     http://ss64.com/nt/syntax-dequote.html
rem   String manipulation in Batch
rem     http://www.dostips.com/DtTipsStringManipulation.php
rem   ^ line continuation
rem  
rem   set p="One Two"      p has the exact value "One Two" including the quotes          
rem   set p=%p:~1,-1%      Removes the first and last characters
rem   set p=%p:"=%         Removes all double-quotes
rem   set p=%p:cat=mouse%  Replaces cat with mouse

rem  ping 127.0.0.1 -n 12 -w 1000 > nul
rem        1       2            3                                                         4

@echo on
call :mystart /b/min "Opinions"  "%SystemRoot%\explorer.exe  /e,d:\agar\jobs\opinion"   8  
@echo on
call :mystart /b/min "Notepad++"  D:\Prog_D
otepad++
otepad++.exe  14
@echo on
call :mystart /normal"Firefox"    D:\Prog_D\Firefox\firefox.exe      20
@rem call :mystart /b/min"ProcessExplorer"  D:\Prog_D\AntiVirus\SysInternals\procexp.exe  8
@echo on
call :mystart /b/min/belownormal"Outlook" D:\Prog_D\MSOffice\OFFICE11\outlook.exe  2
@echo off
goto:eof

:mystart
@echo off
 rem  %3 is"program-path  arguments" with the quotes. We remove the quotes
 rem  %4 is seconds to wait after starting that program
 set p=%3
 set p=%p:"=%
 start  %1  %2  %p%
 ping 127.0.0.1 -n %4 -w 1000 > nul
 goto:eof


上面的答案(以:DeQuote开头)假定延迟的环境变量扩展设置为on。从cmd /?

默认情况下不启用延迟的环境变量扩展。您
可以启用或禁用延迟的环境变量扩展
/ V:ON或/ V:OFF开关对CMD.EXE的特定调用。您
可以启用或禁用对CMD.EXE的所有调用的完成
机器和/或用户登录会话,方法是设置
使用REGEDT32.EXE在注册表中跟踪以下REG_DWORD值:

1
2
3
4
5
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\DelayedExpansion

    and/or

HKEY_CURRENT_USER\Software\Microsoft\Command Processor\DelayedExpansion

设为0x1或0x0。用户特定设置优先于
机器设置。命令行开关优先于
注册表设置。

如果启用了延迟的环境变量扩展,则惊叹号
字符可以用来替代环境变量的值
在执行时。


这会将"C:\Program Files\somefile.txt"变成C:\Program Files\somefile.txt
同时仍保留Height=5'6"Symbols="!@#之类的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
:DeQuote

SET _DeQuoteVar=%1
CALL SET _DeQuoteString=%%!_DeQuoteVar!%%
IF [!_DeQuoteString:~0^,1!]==[^"] (
IF [!_DeQuoteString:~-1!]==[^"] (
SET _DeQuoteString=!_DeQuoteString:~1,-1!
) ELSE (GOTO :EOF)
) ELSE (GOTO :EOF)
SET !_DeQuoteVar!=!_DeQuoteString!
SET _DeQuoteVar=
SET _DeQuoteString=
GOTO :EOF

1
2
3
4
SetLocal EnableDelayedExpansion
set _MyVariable ="C:\Program Files\ss64"
CALL :dequote _MyVariable
echo %_MyVariable%


Daniel Budzyski的回应非常出色。即使在输出中包含特殊字符的情况下,它也可以工作。例如<; />
n <; pre <> cod>; C:p; gt;对于"" usebackq tokens = 2 delim""%i in(`%comspec% .. ping -n 1 -w 200 10.200.1.1 ^ | nn findstr"" T""`)做echo | set"""

nbytes = 32 timp; <1ms TTL = 255 <; / code&gt <; / pr>
n <;>;如果尝试使用不带引号的简单回显,则由于变量<; />中的p;""而导致错误
n <; pre <> cod>; C:p; gt; s""输出=字节= 32 timp; lt; 1ms TTL = 2"
nC:p; gt;回声%输出
n系统找不到指定的文件

nC:p; gt; echo | set""%outpu"
nbytes = 32 timp; <1ms TTL = 255 <; / code&gt <; / pr>

H


我发现使用FOR命令去除周围的引号是最有效的方法。在紧凑形式中(示例2),它是单线的。

示例1:5行(带注释)解决方案。

1
2
3
4
5
6
7
8
REM Set your string
SET STR=" <output file>    (Optional) If specified this is the name of your edited file"

REM Echo your string into the FOR loop
FOR /F"usebackq tokens=*" %%A IN (`ECHO %STR%`) DO (
    REM Use the"~" syntax modifier to strip the surrounding quotation marks
    ECHO %%~A
)

示例2:1线性现实示例。

1
2
3
SET STR=" <output file>    (Optional) If specified this is the name of your edited file"

FOR /F"usebackq tokens=*" %%A IN (`ECHO %STR%`) DO @ECHO %%~A

我发现有趣的是,内部回声忽略了重定向字符" <"和">"。
如果执行ECHO asdfsd>asdfasd,您将写出文件而不是标准输出。

希望这可以帮助 :)

编辑:

我考虑了一下,意识到实现同一件事的方法甚至更简单(更不容易破解)。使用增强的变量替换/扩展(请参见HELP SET),如下所示:

1
2
3
SET STR=" <output file>    (Optional) If specified this is the name of your edited file"

ECHO %STR:~1,-1%

这将打印除第一个和最后一个字符(引号)以外的所有字符。我也建议使用SETLOCAL ENABLEDELAYEDEXPANSION。如果您需要弄清楚引号在字符串中的位置,则可以使用FINDSTR获取字符#s。


http://unxutils.sourceforge.net/是一堆GNU实用程序(包括sed,gawk,grep和wget)的本机win32端口。 (很抱歉,我没有足够的代表将此评论发表!)


蛮力法:

1
echo"foo <3 bar" | sed -e 's/\(^"\|"$\)//g'

当然,这需要找到合适的Win32版本的sed