Batch adding a character every x characters - windows

If I get my parameter with %1 and it is "Server" how can I add a + sign after every letter?
So my result would be "S+e+r+v+e+r"?
I think Batch file to add characters to beginning and end of each line in txt file this is a similar question but I don't know how to change the code for this purpose.
Any help would be great!

I'm pretty sure this has been asked and answered before, but I couldn't find it.
There is a really cool (and fast) solution that I saw posted somewhere. It uses a new cmd.exe process with the /U option so output is in unicode. The interesting thing about the unicode is that each ASCII character is represented as itself followed by a nul byte (0x00). When this is piped to MORE, it converts the nul bytes into newlines!. Then a FOR /F is used to iterate each of the characters and build the desired string. A final substring operation is used to remove the extra + from the front.
I tweaked my memory of the code a bit, playing games with escape sequences in order to get the delayed expansion to occur at the correct time, and to protect the character when it is appended - all to get the technique to preserve ^ and ! characters. This may be a new twist to existing posted codes using this general technique.
#echo off
setlocal enableDelayedExpansion
set "str=Server bang^! caret^^"
set "out="
for /f delims^=^ eol^= %%A in ('cmd /u /v:on /c echo(^^!str^^!^|more') do set "out=!out!+^%%A"
set "out=!out:~1!"
echo Before: !str!
echo After: !out!
--OUTPUT---
Before: Server bang! caret^
After: S+e+r+v+e+r+ +b+a+n+g+!+ +c+a+r+e+t+^

This batch file should do it:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET Text=%~1
SET Return=
REM Batch files don't have a LEN function.
REM So this loop will process up to 100 chars by doing a substring on each.
FOR /L %%I IN (0,1,100) DO (
CALL SET Letter=!Text:~%%I,1!
REM Only process when a letter is returned.
IF NOT "!Letter!" == "" (
SET Return=!Return!+!Letter!
) ELSE (
REM Otherwise, we have reached the end.
GOTO DoneProcessing
)
)
:DoneProcessing
REM Remove leading char.
SET Return=%Return:~1,999%
ECHO %Return%
ENDLOCAL
Calling with Test.bat Server prints S+e+r+v+e+r to the console.

Related

How to reverse string list in batch

I have a problem with reversing string list in a batch script. Let say I have a list L=string1,string2,string3 I would like to obtain reversed list L=string3,string2,string1. Any ideas??
You may also use this shorter/simpler approach:
#echo off
setlocal EnableDelayedExpansion
set "L=string1,string2,string3"
echo Input =%L%
set "revL="
set "str=%L:,=" & set "revL=,!str!!revL!" & set "str=%"
set "revL=%str%%revL%"
echo Output=%revL%
This method use the same procedure of the other answers, but in less lines. If you want to know what happens here, remove the #echo off line and run it. ;)
Without knowing what your input looks like, this might be a first attempt:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET var=abc,def,ghi
SET rev=
:LOOP
IF NOT "!var!"=="" (
FOR /F "delims=, tokens=1,*" %%F IN ("!var!") DO (
SET rev=%%F,!rev!
SET var=%%G
)
) ELSE (
SET rev=!rev:~0,-1!
GOTO ENDLOOP
)
GOTO LOOP
:ENDLOOP
ECHO reversed list is: !rev!
EDIT: As requested, here is an explanation how it works:
var is your starting list of strings separated by commas.
rev will be the reversed string. At the beginning this string is empty.
Now let's take a look at the loop:
In each iteration, we are separating our string into two parts: %%F and %%G. %%F will be everything before the first comma and %%G will be the rest of the string: FOR /F "delims=, tokens=1,*" %%F IN ("!var!"). delims=, means that we are using comma as delimiter. tokens=1,* means that the first found substring will be stored in %%F while the rest will be stored in %%G (%%F is defined for the first token so Windows command interpreter will put every token afterwards in G, H, I, and so on - as we are using *, everything will land in %%G). Finally, we take the first token of our string (%%F) and append ,!rev! to it. Then we set the remaining string list to everything behind the first comma (%%G).
In the first iteration, this loop does the following (pseudo code):
var=abc,def,ghi
rev=
split the string into %%F=abc and %%G=def,ghi
set rev to %%F,rev //means abc,
set var to var but without the first token //means def,ghi
In the second iteration:
var=def,ghi
rev=abc,
split the string into %%F=def and %%G=ghi
set rev to %%F,rev //means def,abc
set var to var but without the first token //means ghi
In the third iteration:
var=ghi
rev=def,abc
split the string into %%F=ghi %%G=
set rev to %%F,rev //means ghi,def,abc,
set var to var but without the first token //means empty string
Now, after jumping back to :LOOP, the if condition is no longer fulfilled as !var! has shrunk from formerly abc,def,ghi to now an empty string. So IF NOT !var!=="" becomes false and we are jumping to the ELSE clause.
There is one problem left: as we are constructing our reversed string by pre-appending the first token from the original list AND a comma, we will end up with a comma at the end of the reversed string list: ghi,def,abc,
SET rev=!rev:~0,-1! fixes this. It takes a "substring" from our string, starting at index 0 and finishing at "end-1". So this line simply removes the last , at the end of our string. Then we are jumping to :ENDLOOP.
Here is a batch file code assuming L=string1,string2,string3 is assigned to an environment variable:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ListLine=L=string1,string2,string3"
for /F "tokens=1* delims==" %%I in ("%ListLine%") do (
set "LineBegin=%%I"
set "ListItems=%%J"
)
set "ReversedItems="
for %%I in (%ListItems%) do call set "ReversedItems=%%I,%%ReversedItems%%"
set "ListLine=%LineBegin%=%ReversedItems:~0,-1%"
echo %ListLine%
endlocal
Windows command interpreter interprets a comma in list of strings in a simple FOR loop like a space character as it can be seen on running this batch file without #echo off from within a command prompt window. Therefore the second FOR loop runs first with string1 assigned to loop variable I, second with string2 and third with string3.
Command CALL is used to do a double processing of the command SET to avoid the requirement to use delayed environment variable expansion as explained by help for command SET output on running set /? in a command prompt window.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
endlocal /?
for /?
set /?
setlocal /?
Aacini, definitely has the fastest code out of all of the answers. This is some longer code that uses a similar SET trick.
#echo off
setlocal EnableDelayedExpansion
set i=1
set "x=abc,def,ghi"
set "x!i!=%x:,=" & set /A i+=1 & set "x!i!=%"
FOR /L %%G IN (%i%,-1,1) DO (
IF "%i%"=="%%G" (
set "reverse=!x%%G!"
) ELSE (
set "reverse=!reverse!,!x%%G!"
)
)
echo %reverse%
pause
Just some quick timed testing of all 4 answers. First one uses the original string 3 characters in each of the 3 comma separated fields. The second one uses 3 characters in 9 comma separated fields. Each time I tested running each one 100 times and calculated the average. The differences are negligible.
Average of 100 tries using 3x3
Aacini 0.39254
Squashman 0.39851
Michael 0.3999
Mofi 0.40434
Average 100 tries using 3x9
Aacini 0.39925
Squashman 0.40278
Michael 0.41457
Mofi 0.43397

Batch Script Find String in String with a twist

I am trying to do this in a batch script, which should be simple, but after spending a couple of hours on it I am no closer to a solution.
If the CMD parameter contains a series of letters, I want to surround each letter with single quotes and separate by commas. For example, if the user enter this:
MYTEST.CMD ABCDEF
I want to create a string that looks like this:
'A','B','C','D','E','F'
The same as if they had entered this in the CMD line:
MYTEST.CMD "'A','B','C','D','E','F'"
Fairly easy, actually:
#echo off
set "LETTERS=%~1"
set OUTPUT=
if not defined LETTERS goto usage
:loop
if defined OUTPUT set "OUTPUT=%OUTPUT%,"
set "OUTPUT=%OUTPUT%'%LETTERS:~0,1%'"
set "LETTERS=%LETTERS:~1%"
if defined LETTERS goto loop
echo.%OUTPUT%
goto :eof
:usage
echo Please pass a few letters as argument, e.g.
echo. %~0 ABC
goto :eof
Let's dissect it a little:
We first store the argument in the variable LETTERS.
Then we initialise our output string to an empty string.
Then follows a loop that appends the first letter from LETTERS to OUTPUT in the proper format (with a comma before if OUTPUT is not empty) and removes that letter from LETTERS.
When LETTERS is empty, we exit the loop and print the result.
And just for the fun of it, the same as a PowerShell function:
function Get-LetterList([string]$Letters) {
([char[]]$Letters | ForEach-Object { "'$_'" }) -join ','
}
The Batch file below use an interesting trick I borrowed from this post that convert the Ascii (1-byte) characters into Unicode 2-bytes characters via cmd /U (inserting a zero-byte between characters), and then split the zero-bytes in individual lines via find command:
#echo off
setlocal EnableDelayedExpansion
set "output="
for /F "delims=" %%a in ('cmd /D /U /C echo %~1^| find /V ""') do (
set "output=!output!,'%%a'"
)
set output="%output:~1%"
echo %output%

I need to match or replace an asterisk * in a batch environmental variable using only native Windows commands. Is this possible?

I'm trying to remove an asterisk from an environmental variable string, but can't seem to do it.
I'm creating an m3u file based around search strings, so for instance I if I want to make an m3u file containing every song with the word love in it, I would enter:
m3u *Love*
And m3u.bat would create the file:
xLovex.m3u
But the regular method of replacing characters does not work with an asterisk. (Though I don't have that problem with the question mark.)
set nam=%nam:*=x%.m3u
Instead creates the filename
x.m3u
The easy answer is no.
The problem that you're encountering stems from the fact that the asterisk * is a special character when used with the SET search and replace method. It matches multiple characters in a limited, but still useful, way. You can learn about that here.
The hard answer is Yes!
I will provide you with two solutions. One an incomplete solution but elegent,
the other complete and inelegent.
Both methods will search for * and replace it with an x.
Both methods will both search and modify the following string:
*love*
The first method that comes to mind is using a 'FOR /L' statement, and requires that you know how many characters long the environmental variable is.
::Major Edit::
I thought I knew the various maximum size strings of environmental variables, but dbenham has taken me to school, shown me a kick-in-the-behind length function, and in the mean time completely reversed my opinions of the two solutions I'm presenting.
Other than for the Windows 95/98/ME limitation of a 256 Character maximum environmental variable size. It seems that all versions of Windows using CMD.EXE have a limitation of 8,192 characters, well below what the documentation suggests.
Both versions require delayed environmental variable expansion, but for two different reasons. One because I'm operating inside a FOR statement. The other because you cannot put a % pair inside another % pair because the command processor matches the second % that it encounters to the first one it encounters, but we need to use a variable inside another variable expression. (You'll see.)
This solution uses the strLen function (in line 3) from DosTips.com that can be found Here. Just slap it into a file called strLen.bat and be amazed at it's speed!
Solution 1: (FOR /L Solution) :: Preferred Solution ::
setlocal ENABLEDELAYEDEXPANSION
set nam=*love*
rem calling strLen
call :strLen nam len
for /l %%x in (0,1,%len%) do if not "!nam:~%%x,1!"=="" if "!nam:~%%x,1!"=="*" (
set /a plusone=%%x+1
for /l %%y in (!plusone!, 1, !plusone!) do (
set nam=!nam:~0,%%x!x!nam:~%%y!
)
)
echo %nam%
ENDLOCAL
I think this is a quick and elegant solution It could be sped up by adding the contents of strLen.bat to the routine, but I wanted no confusion as to the author.
If you, for some reason, do not wish to use strLen, then the next quickest method would probably use a GOTO loop.
Solution 2: (Goto Solution)
setlocal ENABLEDELAYEDEXPANSION
set nam=*love*
set num=0
:loop
set /a plusone=%num%+1
if "!nam:~%num%,1!"=="*" set nam=!nam:~0,%num%!x!nam:~%plusone%!
set /a num=%num%+1
if not "!nam:~%num%,1!"=="" goto :loop
echo %nam%
EndLocal
Special thanks to dbenham for pointing out the strLen function. It works faster than any batch based function has a right to!
Although there were already some very good and robust ways explained here, I'd still like to add another option for the sake of completion.
It's not as good as the other options but I personally use it in some cases where I'd like to keep the code clean and where I know that it will suffice:
The way it works is by using for /f's delims to cut the string into two parts, which are then put back together, getting rid of the * in the process:
for /f "tokens=1,* delims=*" %%a in ("a*b") do (set string=%%a%%b)
>>> string=ab
Obviously, the downside to this is that it can only be used to remove one *.
To remove more, we can either just use more tokens...
for /f "tokens=1-3,* delims=*" %%a in ("a*b*c*d") do (set string=%%a%%b%%c%%d)
>>> string=abcd
... or we can put the first line in a for /l-loop:
setlocal enableDelayedExpansion
set string=a*b*c*d
for /l %%a in (1, 1, 3) do (
for /f "tokens=1,* delims=*" %%b in ("!string!") do (set string=%%b%%c)
)
>>> string=abcd
Another thing to note is that you can define more than one character in delims, and they will all be removed at once:
for /f "tokens=1,* delims=+-*/" %%a in ("a*-/+b") do (set string=%%a%%b)
>>> string=ab
Another solution to the stated problem is to use a PowerShell replace command within your batch script.
set var=*Love*
echo %var%>var.txt | powershell -command "((get-content var.txt) -replace '[\x2A]','x') -replace '.{1}$' | set-content var.txt"
set /p var=<var.txt
set var=%var%.m3u
echo %var%
In the above code, the second line
writes your string into a text file
calls a PowerShell command to get the contents of that file
replaces the * character with null
overwrites the text file with the new value
Once that is done, you read the value back into your variable.
To further explain the replace command, the first single quotes is what you are searching for. We are using square brackets to identify the * character as a hex character (\x2A is the hex value for *). After the comma, the second set of single quotes contains no value so that the searched object is removed. To prevent a space between xLovex and the .m3u, we have to use -replace '.{1}$' before writing the result to the text file.
Once you are done with the text file, enter a line to delete it.
if exist var.txt del var.txt
Here is an approach that does not walk through all characters of a string, but it uses a for /F loop to split the string at every occurrence of a (sequence of a) certain character. The actual functionality is packed into a sub-routine for easy reuse, so the main section of the following script just contains some code to test:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
::This is the main routine of the script holding code for test and demonstration:
rem // Definition of some sample text to test (note that `%%` becomes one literal `%`):
set "DATA=some text,"^&"&;0'#%%~#`$:wild**card*?.re<dir>=|+([{parens}])-^/equal==to=!_"
echo/
call :REPL_CHAR TEXT DATA "*" "?"
setlocal EnableDelayedExpansion
echo(In: !DATA!
echo(Out:!TEXT!
echo/
echo(In: !TEXT!
call :REPL_CHAR TEXT TEXT "=" "/"
echo(Out:!TEXT!
endlocal
endlocal
exit /B
:REPL_CHAR
::This function replaces in a string every occurrence of a sequence of a certain character
::by another character or a string. It even correctly handles the characters `*` and `=`.
:: USAGE:
:: call :REPL_CHAR ref_output_string ref_input_string val_search_char val_replace_char
:: PARAMETERS:
:: ref_output_string reference to (name of) variable to receive the resulting string;
:: ref_input_string reference to variable that holds the original string; if empty
:: (`""`), the variable referenced by `ref_output_string` is used;
:: val_search_char single character that is to be replaced;
:: val_replace_char character or string to replace every sequence of `val_search_char`
:: with; this may even be empty;
rem // Localise environment and detect whether delayed expansion is enabled (needed later):
setlocal & set "$NDX=!"
setlocal DisableDelayedExpansion
rem // Fetch arguments and verify them:
set "#RET=%~1" & if not defined #RET endlocal & endlocal & exit /B 2
set "#STR=%~2" & if not defined #STR set "#STR=%#RET%"
set "CHR=%~3"
if not defined CHR endlocal & endlocal & exit /B 1
set "RPL=%~4"
setlocal EnableDelayedExpansion
rem // Initialise several auxiliary variables:
set "TST=!%#STR%!" & set "CHR=!CHR:~,1!" & set "INS="
if "!CHR!"=="_" (set "BUF=#" & set "WRK=!TST!#") else (set "BUF=_" & set "WRK=!TST!_")
:REPL_CHAR_LOOP
rem // Check whether the end of the string has been reached:
if not defined TST set "BUF=!BUF:~1,-1!" & goto :REPL_CHAR_NEXT
rem // Split the string at the next sequence of search characters:
for /F tokens^=1*^ delims^=^%CHR%^ eol^=^%CHR% %%S in ("!BUF!!INS!!WRK!") do (
rem // Store the portions before and after the character sequence:
endlocal & set "BUF=%%S" & set "TST=%%T" & set "WRK=%%T" & setlocal EnableDelayedExpansion
)
rem // Loop back and find the next character sequence:
set "INS=!RPL!" & goto :REPL_CHAR_LOOP
:REPL_CHAR_NEXT
rem // Return the resulting string with all special characters properly handled:
if not defined $NDX if defined BUF set "BUF=!BUF:"=""!^"
if not defined $NDX if defined BUF set "BUF=!BUF:^=^^^^!"
if not defined $NDX if defined BUF set "BUF=%BUF:!=^^^!%" !
if not defined $NDX if defined BUF set "BUF=!BUF:""="!^"
for /F "delims=" %%S in (^""!BUF!"^") do endlocal & endlocal & endlocal & set "%#RET%=%%~S" !
exit /B
The input and output data of this script (let us call it repl_char_demo.bat) are:
>>> repl_char_demo.bat
In: some text,"&"&;0'#%~#`$:wild**card*?.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild?card??.re<dir>=|+([{parens}])-^/equal==to=!_
In: some text,"&"&;0'#%~#`$:wild?card??.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild?card??.re<dir>/|+([{parens}])-^/equal/to/!_
This is a script that uses for /L loops to walk through all characters of the string, to check each character against a predefined one and replaces it as specified. This method replaces every single matching character rather than sequences. Again the functionality is put into a sub-routine (the main section is dismissed this time):
:REPL_CHAR
::This function replaces in a string every occurrence of one certain character by another
::character or a string. It even correctly handles the characters `*` and `=`, as well as
::sequences of search characters so that every single one becomes replaced.
:: USAGE:
:: call :REPL_CHAR ref_output_string ref_input_string val_search_char val_replace_char
:: PARAMETERS:
:: ref_output_string reference to (name of) variable to receive the resulting string;
:: ref_input_string reference to variable that holds the original string; if empty
:: (`""`), the variable referenced by `ref_output_string` is used;
:: val_search_char single character that is to be replaced;
:: val_replace_char character or string to replace every single `val_search_char`
:: with; this may even be empty;
rem // Localise environment and detect whether delayed expansion is enabled (needed later):
setlocal & set "$NDX=!"
setlocal DisableDelayedExpansion
rem // Fetch arguments and verify them:
set "#RET=%~1" & if not defined #RET endlocal & endlocal & exit /B 2
set "#STR=%~2" & if not defined #STR set "#STR=%#RET%"
set "CHR=%~3"
if not defined CHR endlocal & endlocal & exit /B 1
set "RPL=%~4"
setlocal EnableDelayedExpansion
rem // Initialise several auxiliary variables:
set "WRK=!%#STR%!" & set "CHR=!CHR:~,1!" & set "BUF="
rem // Loop through all characters and check for match:
if defined WRK for /L %%J in (0,1,63) do for /L %%I in (0,1,127) do (
set /A "POS=%%J*64+%%I" & for %%P in (!POS!) do (
set "TST=!WRK:~%%P,1!" & if not defined TST goto :REPL_CHAR_QUIT
rem // Store character or replacement depending on whether there is a match:
if "!TST!"=="!CHR!" (set "BUF=!BUF!!RPL!") else (set "BUF=!BUF!!TST!")
)
)
:REPL_CHAR_QUIT
rem // Return the resulting string with all special characters properly handled:
if not defined $NDX if defined BUF set "BUF=!BUF:"=""!^"
if not defined $NDX if defined BUF set "BUF=!BUF:^=^^^^!"
if not defined $NDX if defined BUF set "BUF=%BUF:!=^^^!%" !
if not defined $NDX if defined BUF set "BUF=!BUF:""="!^"
for /F "delims=" %%S in (^""!BUF!"^") do endlocal & endlocal & endlocal & set "%#RET%=%%~S" !
exit /B
There are actually two nested for /L loops rather than a single one, both of which become broken as soon as the end of the string is reached, using the goto command. Breaking a for /L loop means that it completes iterating in the background although its body is no longer executed. Therefore, using a single loop takes much more time to finish after being broken rather than two nested ones.
The input and output data of this script (with the same main section as above) are:
>>> repl_char_demo.bat
In: some text,"&"&;0'#%~#`$:wild**card*?.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild??card??.re<dir>=|+([{parens}])-^/equal==to=!_
In: some text,"&"&;0'#%~#`$:wild??card??.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild??card??.re<dir>/|+([{parens}])-^/equal//to/!_
See this answer, and with set-ast.bat you'll want to put set-ast nam "x" in your file where needed.
set-ast takes the parameters <variable-to-modify> <string-to-replace-asterisks-with>

Remove trailing spaces from a file using Windows batch?

How could I trim all trailing spaces from a text file using the Windows command prompt?
The DosTips RTRIM function that Ben Hocking cites can be used to create a script that can right trim each line in a text file. However, the function is relatively slow.
DosTips user (and moderator) aGerman developed a very efficient right trim algorithm. He implemented the algorithm as a batch "macro" - an interesting concept of storing complex mini scripts in environment variables that can be executed from memory. The macros with arguments are a major discussion topic in and of themselves that is not relevent to this question.
I have extracted aGerman's algorithm and put it in the following batch script. The script expects the name of a text file as the only parameter and proceeds to right trim the spaces off each line in the file.
#echo off
setlocal enableDelayedExpansion
set "spcs= "
for /l %%n in (1 1 12) do set "spcs=!spcs!!spcs!"
findstr /n "^" "%~1" >"%~1.tmp"
setlocal disableDelayedExpansion
(
for /f "usebackq delims=" %%L in ("%~1.tmp") do (
set "ln=%%L"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
set /a "n=4096"
for /l %%i in (1 1 13) do (
if defined ln for %%n in (!n!) do (
if "!ln:~-%%n!"=="!spcs:~-%%n!" set "ln=!ln:~0,-%%n!"
set /a "n/=2"
)
)
echo(!ln!
endlocal
)
) >"%~1"
del "%~1.tmp" 2>nul
Assuming the script is called rtrimFile.bat, then it can be called from the command line as follows:
rtrimFile "fileName.txt"
A note about performance
The original DosTips rtrim function performs a linear search and defaults to trimming a maximum of 32 spaces. It has to iterate once per space.
aGerman's algorithm uses a binary search and it is able to trim the maximum string size allowed by batch (up to ~8k spaces) in 13 iterations.
Unfotunately, batch is very SLOW when it comes to processing text. Even with the efficient rtrim function, it takes ~70 seconds to trim a 1MB file on my machine. The problem is, just reading and writing the file without any modification takes significant time. This answer uses a FOR loop to read the file, coupled with FINDSTR to prefix each line with the line number so that blank lines are preserved. It toggles delayed expansion to prevent ! from being corrupted, and uses a search and replace operation to remove the line number prefix from each line. All that before it even begins to do the rtrim.
Performance could be nearly doubled by using an alternate file read mechanism that uses set /p. However, the set /p method is limited to ~1k bytes per line, and it strips trailing control characters from each line.
If you need to regularly trim large files, then even a doubling of performance is probably not adequate. Time to download (if possible) any one of many utilities that could process the file in the blink of an eye.
If you can't use non-native software, then you can try VBScript or JScript excecuted via the CSCRIPT batch command. Either one would be MUCH faster.
UPDATE - Fast solution with JREPL.BAT
JREPL.BAT is a regular expression find/replace utility that can very efficiently solve the problem. It is pure script (hybrid batch/JScript) that runs natively on any Windows machine from XP onward. No 3rd party exe files are needed.
With JREPL.BAT somewhere within your PATH, you can strip trailing spaces from file "test.txt" with this simple command:
jrepl " +$" "" /f test.txt /o -
If you put the command within a batch script, then you must precede the command with CALL:
call jrepl " +$" "" /f test.txt /o -
Go get yourself a copy of CygWin or the sed package from GnuWin32.
Then use that with the command:
sed "s/ *$//" inputFile >outputFile
Dos Tips has an implementation of RTrim that works for batch files:
:rTrim string char max -- strips white spaces (or other characters) from the end of a string
:: -- string [in,out] - string variable to be trimmed
:: -- char [in,opt] - character to be trimmed, default is space
:: -- max [in,opt] - maximum number of characters to be trimmed from the end, default is 32
:$created 20060101 :$changed 20080219 :$categories StringManipulation
:$source http://www.dostips.com
SETLOCAL ENABLEDELAYEDEXPANSION
call set string=%%%~1%%
set char=%~2
set max=%~3
if "%char%"=="" set char= &rem one space
if "%max%"=="" set max=32
for /l %%a in (1,1,%max%) do if "!string:~-1!"=="%char%" set string=!string:~0,-1!
( ENDLOCAL & REM RETURN VALUES
IF "%~1" NEQ "" SET %~1=%string%
)
EXIT /b
If you're not used to using functions in batch files, read this.
There is a nice trick to remove trailing spaces based on this answer of user Aacini; I modified it so that all other spaces occurring in the string are preserved. So here is the code:
#echo off
setlocal EnableDelayedExpansion
rem // This is the input string:
set "x= This is a text string containing many spaces. "
rem // Ensure there is at least one trailing space; then initialise auxiliary variables:
set "y=%x% " & set "wd=" & set "sp="
rem // Now here is the algorithm:
set "y=%y: =" & (if defined wd (set "y=!y!!sp!!wd!" & set "sp= ") else (set "sp=!sp! ")) & set "wd=%"
rem // Return messages:
echo input: "%x%"
echo output: "%y%"
endlocal
However, this approach fails when a character of the set ^, !, " occurs in the string.
Good tool for removing trailing spaces in files in windows:
http://mountwhite.net/en/spaces.html
I just found a very nice solution for trimming off white-spaces of a string:
Have you ever called a sub-routine using call and expanded all arguments using %*? You will notice that any leading and/or trailing white-spaces are removed. Any white-spaces occurring in between other characters are preserved; so are all the other command token separators ,, ;, = and also the non-break space (character code 0xFF). This effect I am going to utilise for my script:
#echo off
set "STR="
set /P STR="Enter string: "
rem /* Enable Delayed Expansion to avoid trouble with
rem special characters: `&`, `<`, `>`, `|`, `^` */
setlocal EnableDelayedExpansion
echo You entered: `!STR!`
call :TRIM !STR!
echo And trimmed: `!RES!`
endlocal
exit /B
:TRIM
set "RES=%*"
exit /B
This script expects a string entered by the user which is then trimmed. This can of course also be applied on lines of a file (which the original question is about, but reading such line by line using for /F is shown in other answers anyway, so I skip this herein). To trim the string on one side only, add a single character to the opposite side prior to trimming and remove it afterwards.
This approach has got some limitations though: it does not handle characters %, !, ^ and " properly. To overcome this, several intermediate string manipulation operations become required:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "STR="
set /P STR="Enter string: "
setlocal EnableDelayedExpansion
echo You entered: `!STR!`
set "STR=!STR:%%=%%%%!"
set "STR=!STR:"=""!^"
if not "%STR%"=="%STR:!=%" set "STR=!STR:^=^^^^!"
set "STR=%STR:!=^^^!%"
call :TRIM !STR!
set "RES=!RES:""="!^"
echo And trimmed: `!RES!`
endlocal
endlocal
exit /B
:TRIM
set "RES=%*"
exit /B
Update
Both of the above scripts cannot handle the characters &, <, > and |, because call seems to become aborted as soon as such a character appears in an unquoted and unescaped manner.
However, I finally found a way to fix that and come up with an approach that can successfully deal with all characters (except perhaps some control characters, which I did not test):
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem // The last white-space in `STRING` is a tabulator:
set "RESULT=" & set "STRING= (<&>"^|)^^!^^^^;,= ^"
echo Input string: `!STRING!`
rem // Double quotes to avoid troubles with unbalanced ones:
if defined STRING set "STRING=!STRING:"=""!^"
rem // Particularly handle carets and exclamation marks as delayed expansion is enabled:
if defined STRING set "STRING=!STRING:^=^^^^!"
if defined STRING set "STRING=%STRING:!=^^^!%" !
if defined STRING (
rem // Escape all characters that `call` has got troubles with:
set "STRING=!STRING:^=^^!"
set "STRING=!STRING:&=^&!"
set "STRING=!STRING:<=^<!"
set "STRING=!STRING:>=^>!"
set "STRING=!STRING:|=^|!"
)
rem /* Call the sub-routine here; the strigs `!=!` constitute undefined dummy variables
rem with an illegal name, which eventually become removed; the purpose of them us to
rem enable usage of that `call` inside of a `for` loop with the meta-variable `%%S`,
rem which would otherwise become unintentionally expanded rather than `%%STRING%%`,
rem which literally contained `%%S`; the `!=!` at the end is just there in case you
rem want to append another string that could also match another `for` meta-variable;
rem note that `!!` is not possible as this would be collapsed to a single `!`, so
rem a (most probably undefined) variable `!STRING%!` would then become expanded: */
call :TRIM %%!=!STRING%%!=!
rem /* The caret doubling done by `call` does not need to be reverted, because due to
rem doubling of the quotes carets appear unquoted, so implicit reversion occurs here;
rem of course the doubling of the quotes must eventually be undone: */
if defined RESULT set "RESULT=!RESULT:""="!^"
echo Now trimmed: `!RESULT!`
endlocal
exit /B
:TRIM
rem // This is the effective line that does the left- and right-trimming:
set "RESULT=%*" !
exit /B
I use this Python 2 script to print lines with trailing whitespace and remove them manually:
#!/usr/bin/env python2
import sys
if not sys.argv[1:]:
sys.exit('usage: whitespace.py <filename>')
for no, line in enumerate(open(sys.argv[1], 'rb').read().splitlines()):
if line.endswith(' '):
print no+1, line
I know that Python is not preinstalled for Windows, but at least it works cross-platform.

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

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.
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.
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
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%
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".
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%"
This worked for me:
SET "SOMETHING=Complex (String) (of stuff!)"
echo !SOMETHING! >> file.txt
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
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!)

Resources