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

坚强是说给别人听的谎言 提交于 2019-11-27 19:17:26

The call command has this functionality built in. To quote the help for call:

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

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

Here is a primitive example:

@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

Output:

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

I should credit the 'environment variable tunneling' technique (endlocal&set ret=%thestring%) to Tim Hill, 'Windows NT Shell Scripting'. This is the only book I have ever found that addresses batch files with any depth.

You can use the %var:x=y% construction that replaces all x with y.

See this example what it can do:

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%
Daniel Budzyński

The following approach can be used to print a string without quotes:

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

To remove all quotation marks from a set variable, you need Delayed Variable Expansion to securely expand the variable and process it. Expansion using percent signs (i.e. %VAR% and %1) are inherently unsafe (they are vulnerable to command injection; read this for details).

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

To strip quotes from a text file or a command output, things will get complicated because with Delayed Expansion, string like !VAR! within the text document will get expanded (within the %%i expansion in FOR /F) when it shouldn't. (This is another vulnerability—information disclosure—that's not documented elsewhere.)

To safely parse the document, a switch between delayed-expansion-enabled and -disabled environment is needed.

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

The delims^=^ eol^= in the code above probably needs explanation: This effectively disables both "delims" characters (i.e. field separators) and "eol" character (i.e. comment character). Without it, the "delims" will default to tab and space and "eol" defaults to a semicolon.

  • The eol= token always read whichever the next character it is after the equal sign. To disable it this token has to be in the end of the options string so that no character may be used for "eol", effectively disabling it. If the options string is quoted, it might use quotation mark (") as the "eol", so we must not quote the options string.
  • The delims= option, when it's not the last option in the options string, will be terminated by a space. (To include space in "delims" it has to be the last option of FOR /F options.) So delims= followed by a space and then another option disables the "delims".

This will turn "C:\Program Files\somefile.txt" into C:\Program Files\somefile.txt while still preserving cases such as Height=5'6" and 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

Example

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

The above answer (starting with :DeQuote) assumes delayed environment variable expansion is set to on. From cmd /?:

Delayed environment variable expansion is NOT enabled by default. You can enable or disable delayed environment variable expansion for a particular invocation of CMD.EXE with the /V:ON or /V:OFF switch. You can enable or disable completion for all invocations of CMD.EXE on a machine and/or user logon session by setting either or both of the following REG_DWORD values in the registry using REGEDT32.EXE:

HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\DelayedExpansion

    and/or

HKEY_CURRENT_USER\Software\Microsoft\Command Processor\DelayedExpansion

to either 0x1 or 0x0. The user specific setting takes precedence over the machine setting. The command line switches take precedence over the registry settings.

If delayed environment variable expansion is enabled, then the exclamation character can be used to substitute the value of an environment variable at execution time.

The following batch file starts a series of programs with a delay after each one.

The problem is to pass a command line with parameters for each program. This requires quotes around the program argument, which are removed when the call is made. This illustrates a few techniques in batch file processing.

Look in the local subroutine :mystart for how an argument in quotes is passed in, and the quotes are removed.

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

I know that it is not actually for the author, but if you need to send some text to the file without quotes - the solution below works for me. You do not need to use quotes in the echo command, just surround the complete command with brackets.

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

Using the FOR command to strip the surrounding quotation marks is the most efficient way I've found to do this. In the compact form (Example 2) it's a one-liner.

Example 1: The 5-line (commented) solution.

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
)

Example 2: The 1-liner real-world example.

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

I find it interesting that the inner echo ignores the redirection characters '<' and '>'.
If you execute ECHO asdfsd>asdfasd you will write file out instead of std out.

Hope this helps :)

Edit:

I thought about it and realized there is an even easier (and less hacky) way of accomplishing the same thing. Use the enhanced variable substitution/expansion (see HELP SET) like this:

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

ECHO %STR:~1,-1%

That will print all but the first and last characters (your quotation marks). I would recommend using SETLOCAL ENABLEDELAYEDEXPANSION too. If you need to figure out where quotation marks are located in the string you can use FINDSTR to get the character #s.

Daniel Budzyński's response is brilliant. It works even in situations where there are special characters in the output. For example:

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

If you try tried a simple echo without the quotes, you get a error, due to the "<" in the variable:

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

Brute force method:

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

This requires finding a suitable Win32 version of sed, of course.

http://unxutils.sourceforge.net/ is a native win32 port of a bunch of GNU utilities including sed, gawk, grep and wget. (sorry that I don't have enough rep to post this as a comment!)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!