Batch file: dealing with a parameter that can contain spaces and ampersand - batch-file

I've got a simple batch file that places the parameter it's called with into a text file:
setlocal ENABLEDELAYEDEXPANSION
set filename=%~n1
set pathname=%~p1
set letter=%~d1
>>path.txt echo %letter%%pathname%%filename%
(it does more, but this is sufficient to show the problem)
The parameter is a full path:
C:\te st\file & name.xml
This batch file works as long as there's no & in the path name. But the above path results in filename=file
and the & is interpreted as an argument.
I tried using set "filename=%~n1"
but that results in
>>path.txt echo C:\te st\"file & name.xml"
which is incorrect. I can't get rid of the quotes.
I tried:
>>path.txt echo %letter%%pathname%!filename!
but that results in
>>path.txt echo C:\te st\!filename!
How do I get the correct path in my text file?

setlocal DisableDelayedExpansion
set "filename=%~n1"
set "pathname=%~p1"
set "letter=%~d1"
setlocal EnableDelayedExpansion
>>path.txt echo !letter!!pathname!!filename!
After assigning the content into variables, only delayed expansion should be used, because delayed expansion never changes or try to parse the content.
The setlocal DisableDelayedExpansion at the beginning ensures, that exclamation marks are preserved while assigning the arguments to variables.

Related

Extract a GUID from XML in batch script

I have a string
<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>
I am trying to trim out DA7C36F0-A749-4CC5-9575-398C06039325 from this line.
To begin with I tried to set this string in a variable but I am not able to do that may be because of < and ? in string, I tried:
set "var=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>"
But later I was able to fetch the string somehow at runtime and now I have the variable
line=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>
I am not able to figure out how can I trim only value i.e DA7C36F0-A749-4CC5-9575-398C06039325 out of this variable using batch script.
I gave it a try to trim suffix at least with:
set "line=%line:"?>%"
But getting error, can anyone help with better approach?
Note: the spaces are included in string
You can split the string using " as a delimiter, but since quotes are used to specify for loop options, the syntax looks a little different than usual:
#echo off
setlocal enabledelayedexpansion
set "line=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>"
echo !line!
for /f tokens^=2^ delims^=^" %%A in ("!line!") do set "line=%%A"
echo !line!
You may use this very simple trick:
#echo off
setlocal EnableDelayedExpansion
set "var=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>"
set "i=0" & set "v0=%var:"=" & set /A i+=1 & set "v!i!=%"
echo Desired string: [%v1%]
If you want to know how this works, remove the #echo off line and carefully review what appears in the screen...
Your command line:
set "line=%line:"?>%"
does not make sense, because there is an =-sign missing (refer to sub-string substitution):
set "line=%line:"?>=%"
To trim away the unwanted prefix you could remove everything up to the first quotation mark:
set "line=%line:*"=%"
However, this only works when you do that after having removed the suffix, because you are dealing with unbalanced quotation marks, which are problematic together with immediate variable expansion. If you want to change the order, you have to implement escaping in order not to exhibit the redirection operator > unquoted:
set ^"line=%line:*"=%"
set "line=%line:"?>=%"
To avoid the need of escaping depending on the input string, use delayed variable expansion, like this:
set "line=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>"
set line
rem // First enable delayed expansion:
setlocal EnableDelayedExpansion
rem // Then apply it by replacing `%` with `!`:
set "line=!line:*"=!"
set "line=!line:"?>=!"
set line
rem // This restores the previous state:
endlocal
rem // At this point changes in the variable are no longer available due to localisation:
set line

Use Bat file to split a string with semi-colon as delimiter

I want to delete a special path using batch.
I cannot use set somevar=" %path:specialstr=%",because the specialstr has dynamic part. Can I use batch to remove strings which are produced by %cd% from a large string like %path%?
Follow Dennis van Gils' advice, use Delayed Expansion as follows:
SETLOCAL EnableExtensions EnableDelayedExpansion
set "specialstr=%CD%\"
set "somevar=!path:;%specialstr%;=;!"
If you can't use Delayed Expansion for some reason, use call set (read Advanced usage : CALLing internal commands):
warning: some partial paths in %path% variable contain facultative trailing backslash whereas other do not.
call set "somevar=%%path:;%specialstr%;=;%%" most common usage.
call set "somevar=%%path:;%specialstr%=%%" use if ;%specialstr% is trailing part of %path% but be aware that Variable Edit/Replace is greedy and replaces all occurences of ;%specialstr% in %path%.
call set "somevar=%%path:%specialstr%;=%%" use if %specialstr%; is leading part of %path% but be careful for the same caution.
A sample script:
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
where p.bat
pushd "C:\Utils"
set "specialstr=%CD%\"
popd
echo specialstr=%specialstr%
echo path end=...%path:~-46%
call set "somevar=%%path:;%specialstr%=%%"
echo somevar end=...%somevar:~-36%
SETLOCAL
set "path=%somevar%"
echo path end=...%path:~-36%
where p.bat
ENDLOCAL
where p.bat
Output (shows only some few trailing characters of %path% and %somevar% variables for better readability):
==> D:\bat\SO\38017804.bat
C:\Utils\p.bat
specialstr=C:\Utils\
path end=...Microsoft SQL Server\120\Tools\Binn\;C:\Utils\
somevar end=...Microsoft SQL Server\120\Tools\Binn\
path end=...Microsoft SQL Server\120\Tools\Binn\
INFO: Could not find files for the given pattern(s).
C:\Utils\p.bat

Windows Batch:string replacement

The following batch file accepts a parameter which is a path and filename.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET filename=%~1
echo !filename!
ENDLOCAL
The filename, when received as a parameter will always be formatted using forward slashes.
In order to replace the forwardshlashes with backslashes, I tried this:
SET filename=!filename:/=\!
But that's not working.
What is the simplest way to do string replacement in a windows batch file?
Thanks
First of all you need to remove the space after =:
SET filename=%~1
Otherwise the space will become part of your variable.
To replace / with \ you have to use % instead of !:
SET filename=!filename:/=\!
Further, there is nothing in your code that would require ENABLEEXTENSIONS so you can skip it.
EDIT:
This is my code of something.bat:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET file=%~1
SET file=!file:\=/!
ECHO !file!
Calling the something.bat ABC/DEF/GHI results in the output ABC\DEF\GHI.
You have a problem when you set the variable
v...v. Initial and ending spaces included in value
SET filename = %~1
^........ Space included in variable name
As the variable is not %filename%, but %filename %, your replacement fails. For a string replacement approach you can use
#echo off
setlocal enableextensions disabledelayedexpansion
set "filename=%~1"
set "filename=%filename:/=\%"
echo %filename%
or, still better, this case can be solved using argument modifiers
#echo off
setlocal enableextensions disabledelayedexpansion
set "filename=%~f1"
echo %filename%

Windows Batch: Build string from lines in text file

I have a simple text file containing one file name per file. I want to merge all of these files. My plan for this was to read the text file, build a string like "filename1+file2+f3" and then use that as a parameter to copy /b.
However, I am having trouble reading the file correctly.
Here is what I have right now:
SET x=
FOR /F %%G IN (merge.txt) DO SET x=%x%+%%G
ECHO %x%
However, the "recursion" here does not seem to work properly and %x% just gets set to "+fl", where fl is the last filename in the file.
How do I do this properly?
Your logic is correct, but you are just missing the delayed expansion usage.
SETLOCAL EnableDelayedExpansion
SET "x="
FOR /F %%G IN (merge.txt) DO SET x=!x!+%%G
ECHO %x%
REM Trim the leading +
SET x=%x:~1,999%
ECHO %x%
ENDLOCAL
Without the delayed expansion, %x% is only evaluated when the FOR loop starts, so it would be blank for each iteration. By enabling delayed expansion, !x! (the notation for this) is evaluated on each iteration so it will build the compound string you are looking for.

how to do calculation in batch file for getting specified character

How to get selected character
for %%A in (controls\vbalSGrid6.ocx) do (
SET TEXT=%A%
SET SUBSTRING=%TEXT:~9%
echo %SUBSTRING%
)
this is giving echo is off but i only need vbalsgrid6.ocx.
The direct way
set "text=controls\vbalscrid6.ocx"
set "substring=%text:~9%"
No need for the for command, unless you are iterating over a set of files or you don't want to use substring operations to get file names
The easy way to get the name and extension of the file
for %%a in (controls\vbalsgrid6.ocx) do set "fileName=%%~nxa"
%%a hold a reference to the file, and %%~nxa is the file name and extension of the referenced file
A direct translation/corrected version of your code (in this case, iterating over the list of files, but not needed)
#echo off
setlocal enableextensions enabledelayedexpansion
for %%a in (controls\*.ocx) do (
set "text=%%a"
set "substring=!text:~9!"
echo !substring!
)
When the batch parser reaches a line/block of code (code inside parenthesis), the full line/block is checked searching the places where a variable will be readed. All this reads are replaced with the value stored in the variable at parse time, before the line/block is executed. That means that if a variable changes its value inside a block, this changed value can not be accessed from inside the same block as the read operation on the variable was previously replaced with the initial value stored inside it.
To handle this case, delayed expansion is used. When delayed expansion is enabled, it is possible to change (where needed) the syntax to read a variable, from %var% to !var!, indicating to the parser that this read operation should be delayed until the command is executed.
The included code will work while there is no ! in the name of the files. As delayed expansion is active, the parser will try to interpret any !, giving non expected results in some cases. It can be handled but sometimes it can be a bit tricky.
#echo off
setlocal enableextensions disabledelayedexpansion
for %%a in (controls\*.ocx) do (
rem Retrieve the initial text. No problem as delayed expansion is disabled
set "text=%%a"
rem Enable delayed expansion to read the value in %text%. And ensure
rem it is disabled at the moment of the assignment to the substring var
setlocal enabledelayedexpansion
set "substring="
for /f "delims=" %%b in ("!text:~9!") do (endlocal & set "substring=%%b")
rem We need delayed expansion enabled to read the changed value
rem If substring is empty, the previous endlocal was not executed and
rem there is no need for a new setlocal
if defined substring setlocal enabledelayedexpansion
echo(substring value=!substring!
endlocal
)

Resources