46

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

更新:

在过去的两天里,我一直在努力解决这个问题,终于能够将一些东西拼凑在一起。理查德的回答去掉了引号,但即使我把 ECHO 放在子程序中并直接输出字符串,Windows 仍然挂断在字符串中的字符上。我会接受理查德的回答,因为它回答了所提出的问题。

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

4

13 回答 13

58

call 命令内置了这个功能。引用 call 的帮助:

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

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

这是一个原始示例:

@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

输出:

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

我应该将 '环境变量隧道' 技术 (endlocal&set ret=%thestring%) 归功于 Tim Hill, 'Windows NT Shell Scripting'。这是我发现的唯一一本深入探讨批处理文件的书。

于 2009-04-29T23:36:25.967 回答
27

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

echo|set /p="<h1>Hello</h1>"

将此字符串推送到文件中:

echo|set /p="<h1>Hello</h1>" > test.txt

将此字符串推入文件并附加 CR/LF:

echo|(set /p="<h1>Hello</h1>" & echo.) > test.txt`

去检查:

type test.txt
于 2016-12-22T01:22:22.187 回答
11

您可以使用%var:x=y%将 all 替换x为的结构y

请参阅此示例它可以做什么:

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%
于 2012-09-06T11:21:51.907 回答
4

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

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

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

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

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”。
于 2017-04-25T13:27:53.000 回答
2

这将"C:\Program Files\somefile.txt"变成C:\Program Files\somefile.txt 同时仍然保留诸如Height=5'6"Symbols="!@#

: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

例子

SetLocal EnableDelayedExpansion
set _MyVariable = "C:\Program Files\ss64\"
CALL :dequote _MyVariable
echo %_MyVariable%
于 2009-04-29T23:17:08.103 回答
2

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

默认情况下不启用延迟环境变量扩展。您可以使用 /V:ON 或 /V:OFF 开关为 CMD.EXE 的特定调用启用或禁用延迟的环境变量扩展。您可以通过使用 REGEDT32.EXE 在注册表中设置以下任一或两个 REG_DWORD 值来启用或禁用计算机和/或用户登录会话上对 CMD.EXE 的所有调用的完成:

HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\DelayedExpansion

    and/or

HKEY_CURRENT_USER\Software\Microsoft\Command Processor\DelayedExpansion

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

如果启用了延迟环境变量扩展,则感叹号可用于在执行时替换环境变量的值。

于 2009-04-29T23:33:48.420 回答
2

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

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

查看本地子例程 :mystart 以了解如何传入引号中的参数,并删除引号。

@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\Notepad++\notepad++.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
于 2012-04-22T15:44:25.760 回答
2

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

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

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 线现实世界示例。

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):

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

ECHO %STR:~1,-1%

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

于 2013-03-16T23:21:06.993 回答
2

我知道它实际上并不适合作者,但如果您需要将一些文本发送到不带引号的文件 - 下面的解决方案对我有用。您不需要在 echo 命令中使用引号,只需将完整的命令用方括号括起来即可。

(
    echo first very long line
    echo second very long line with %lots% %of% %values%
) >"%filename%"
于 2015-05-29T08:51:01.740 回答
2

这对我有用:

SET "SOMETHING=Complex (String) (of stuff!)"
echo !SOMETHING! >> file.txt
于 2021-04-20T21:56:21.290 回答
1

Daniel Budzyński 的反应非常出色。它甚至在输出中有特殊字符的情况下也有效。例如:

C:\> for /f "usebackq tokens=2 delims=:" %i in (`%comspec%\..\ping -n 1 -w 200 10.200.1.1 ^| \
     findstr /c:"TTL="`) do echo|set /p="%i"

bytes=32 time<1ms TTL=255

如果您尝试尝试不带引号的简单回显,由于变量中的“<”,您会收到错误:

C:\> set "output=bytes=32 time<1ms TTL=255"
C:\> echo %output%
The system cannot find the file specified.

C:\> echo|set /p="%output%"
bytes=32 time<1ms TTL=255
于 2018-10-23T20:12:24.090 回答
0

蛮力法:

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

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

于 2009-04-29T23:02:52.613 回答
0

http://unxutils.sourceforge.net/是一堆 GNU 实用程序的原生 win32 端口,包括 sed、gawk、grep 和 wget。(对不起,我没有足够的代表发表评论!)

于 2009-04-29T23:11:41.547 回答