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

15

57

I have a Windows batch file I'm creating, but I have to ECHO a large complex string, so I'm having to put double quotes on either end. The problem is that the quotes are also being ECHOed to the file I'm writing it to. How do you ECHO a string like that and strip the quotes off?

UPDATE:

I've spent the last two days working on this and finally was able to kludge something together. Richard's answer worked to strip the quotes, but even when I put the ECHO in the subroutine and directly outputted the string, Windows still got hung up on the chars in the string. I'll accept Richard's answer since it answers the question asked.

I ended up using Greg's sed solution, but had to modify it because of sed/windows bugs/features (it didn't help that it came with no documentation). There are a few caveats to using sed in Windows: you have to use double quotes instead of single quotes, you can't escape the double quotes in the string directly, you have to endquote the string, escape using the ^ (so ^") then beqin quote for the next section. Also, someone pointed out that if you pipe input to sed, there's a bug with a pipe being in the string (I didn't get to verify this since in my final solution, I just found a way not to have all quotes in the middle of the string, and just removed all quotes, I never could get the endquote to be removed by itself.) Thanks for all the help.

Behnken answered 29/4, 2009 at 22:56 Comment(0)
E
59

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.

Explanation answered 29/4, 2009 at 23:36 Comment(8)
Great! Is there a similar easy way to strip the quotes when providing a parameter? For the case where we have control only over the calling side, but not the script processing the parameter?Merill
Would it be sufficient to wrap your call in your own routine that did the stripping, as above, before doing the call?Explanation
Interestingly this does not work for me. set thestring=%~1 yields "The syntax of the command is incorrect.", even when I use identical syntax to that shown by Richard and also despite seeing the same text in the "call /?" section.Balkhash
The problem is in my text. It is xml and has "<" & ">" in it. So Richard's solution does work, I just need to figure out another means of manipulating that text.Balkhash
That's a tricky one, @Jim2B, you might be able to escape the < and > with ^ before it gets into batch land. It depends upon your context.Explanation
Richard, what I eventually did was store the text I needed in a separate file and then performed "type file.txt >> output.txt". It seems like this successfully passes the ">" & "<" without any attempt at interpretation. Even when I tried using the "^<" syntax, it still didn't like something in the string and I didn't have the time to work out the exact issue.Balkhash
@Balkhash sometimes life is just too short. I'm glad you solved it, one way or another.Explanation
Can this be used with $1 parameters too?Hammel
D
36

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

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

pushing this string into file:

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

pushing this string into file and appending a CR/LF:

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

To check:

type test.txt
Dismount answered 22/12, 2016 at 1:22 Comment(3)
NIce trick. But terrible that MS's irresponsible, ad hoc approach to the cmd.exe batch language makes these kinds of hacks necessary.Indiscrimination
Note, that in this form the set command usage is invalid and rises %ERRORLEVEL%. To avoid this you need to provide some dummy variable name: echo | set /p DUMMY="<h1>Hello</h1>".Cincinnatus
this prints without a new lineDarbies
H
14

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%
Hadrian answered 6/9, 2012 at 11:21 Comment(0)
H
4

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".
Hernadez answered 25/4, 2017 at 13:27 Comment(0)
W
4

This worked for me:

SET "SOMETHING=Complex (String) (of stuff!)"
echo !SOMETHING! >> file.txt
Worm answered 20/4, 2021 at 21:56 Comment(2)
After adding SETLOCAL ENABLEDELAYEDEXPANSION first, this solution works for me (on Windows 10 cmd.exe)Amarette
I should have mentioned that was required because of the !VARIABLE! instead of %VARIABLE%Worm
P
3

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%
Parsley answered 29/4, 2009 at 23:17 Comment(1)
This could be simplifed by using setlocal and endlocal rather than dereferencing the two local variables. I prefer my solution, though.Explanation
F
3

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%"
Furfuran answered 29/5, 2015 at 8:51 Comment(0)
Y
2

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.

Yenta answered 29/4, 2009 at 23:33 Comment(3)
'The above' here refers to JRL's answer.Explanation
For a single batch you should probably rather use setlocal enabledelayedexpansion. No need to mess around in the registry or tell users how to call cmd.Footman
Thanks for the 'setlocal' tip, didn't know that one.Yenta
A
2

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
Audit answered 22/4, 2012 at 15:44 Comment(0)
S
2

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.

Scarfskin answered 16/3, 2013 at 23:21 Comment(1)
I don't think that this works for a STR like "This & That"Contort
T
1

Brute force method:

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

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

Tribute answered 29/4, 2009 at 23:2 Comment(0)
N
1

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
No answered 23/10, 2018 at 20:12 Comment(2)
Hi, and welcome to SO -- This would fit better in a comment than in an answer (though being a new contributor, you likely don't have permissions to write comments). Regardless, please see stackoverflow.com/help/how-to-answer for guidelines for answers.Toilsome
Please note that this will rise %ERRORLEVEL%. See my comment under Daniel Budzyński's answer.Cincinnatus
G
1

As you're already using external tools like sed, you might as well print using a language like Python, for example:

python -c "print('''#!/bin/env python\nimport re, sys; sys.exit(0 if re.match('^(bug|feat)', open(sys.argv[1], 'r').read()) else 1)''')"

which will output

#!/bin/env python
import re, sys; sys.exit(0 if re.match('^(bug|feat)', open(sys.argv[1], 'r').read()) else 1)
Geoponic answered 25/3 at 1:17 Comment(0)
D
1

I needed to echo a string without quotes, but with the | character and with a new line. I tried many solutions and none of them worked.

My solution works with the | character and also prints a new line.

This is my test data:

call:echo_no_quotes "test with quotes"
call:echo_no_quotes "test with nested "quotes""
call:echo_no_quotes "test with more ""quotes"""
call:echo_no_quotes "test with even more """quotes""""
call:echo_no_quotes "test with | quotes"
call:echo_no_quotes "test with | nested "quotes""
call:echo_no_quotes "test with | more ""quotes"""
call:echo_no_quotes "test with | even more """quotes""""

I have two solutions.

This removes all quotes:

:echo_no_quotes
    :: put argument in quotes first to allow | character
    set string="%~1"        
    :: substitute | character first
    set string=%string:|=^^^|%  
    :: remove all quotes    
    Set string=%string:"=%      
    :: colon to allow empty lines
    echo:%string%
EXIT /B 0

This is the output:

test with quotes                
test with nested quotes             
test with more quotes               
test with even more quotes              
test with | quotes              
test with | nested quotes               
test with | more quotes             
test with | even more quotes

This removes only outer quotes but is more complicated:

:echo_no_quotes
    :: put argument in quotes first to allow | character
    set string="###%~1###"
    :: substitute | character first
    set string=%string:|=^^^^^^^^^^^^^^^|%  

    :: remove outer quotes  
    SET string=%string:"###=%
    SET string=%string:###"=%
    SET string=%string:###=%

    :: colon to allow empty lines
    echo:%string%
EXIT /B 0

This is the output:

test with quotes  
test with nested "quotes"  
test with more ""quotes""  
test with even more """quotes"""  
test with | quotes  
test with | nested "quotes"  
test with | more ""quotes""  
test with | even more """quotes"""  

to echo to a file instead substitute:

echo:%string% >> test.txt

for:

echo:%string%
Darbies answered 19/4 at 15:49 Comment(0)
U
0

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

Undoubted answered 29/4, 2009 at 23:11 Comment(1)
Good idea, I would recommend unxutils over cygwin for most situations.Tribute

© 2022 - 2024 — McMap. All rights reserved.