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

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

Related

Using CMDER, issue with setting PATH

I'm trying to use CMDER for a development environment that I've setup.
Basically I've created a .bat file that calls:
#ECHO OFF
start Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CMDER\Cmder.exe
Then I've placed the file startdev.bat in:
%CMDER_HOME%\config\profile.d
So everything seems to work just fine, but when the startdev.bat finishes, issuing an:
echo %PATH%
returns:
Z:\_DEV\OS_WINDOWS\1_COMPILER\JDK\ORACLE\1.8.0_181\bin;Z:\_DEV\OS_CYGWIN\bin;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CLutils;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\PUTTY;Z:\_DEV\OS_WINDOWS\6_VERSION_CONTROL\PortableGit\bin;C:\WINDOWS;C:\WINDOWS\SysWOW64;C:\WINDOWS\System32
...any idea what's happening?
I would either expect CMDER to override PATH with the value from its own settings, or use my full path, which before the startdev.bat ends shows the value of:
PATH=Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CMDER\vendor\conemu-maximus5;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CMDER\vendor\conemu-maximus5\ConEmu;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CMDER\vendor\conemu-maximus5\ConEmu\Scripts;Z:\_DEV\OS_ALL\JVM\3_BUILD_TOOLS\GRADLE\5.4\bin;Z:\_DEV\OS_ALL\JVM\3_BUILD_TOOLS\MAVEN\3.5.4\bin;Z:\_DEV\OS_ALL\JVM\3_BUILD_TOOLS\ANT\1.10.5\bin;Z:\_DEV\OS_WINDOWS\3_BUILD_TOOLS\NODE\LTS\10.15.3;Z:\_DEV\OS_WINDOWS\3_BUILD_TOOLS\NODE\LTS\10.15.3\node_modules;Z:\_DEV\OS_WINDOWS\1_COMPILER\GO\1.12.4\bin;Z:\_DEV\OS_WINDOWS\1_COMPILER\PYTHON\32bit\2.7.13;Z:\_DEV\OS_WINDOWS\1_COMPILER\PYTHON\32bit\2.7.13\scripts;Z:\_DEV\OS_WINDOWS\1_COMPILER\ANDROID\android-sdk-windows\platform-tools;Z:\_DEV\OS_WINDOWS\1_COMPILER\JDK\ORACLE\1.8.0_181\bin;Z:\_DEV\OS_CYGWIN\bin;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CLutils;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\PUTTY;Z:\_DEV\OS_WINDOWS\6_VERSION_CONTROL\PortableGit\bin;C:\WINDOWS;C:\WINDOWS\SysWOW64;C:\WINDOWS\System32
..but the fact that it only seems to be keeping the value as defined about halfway through the batch job is strange.
Any ideas?
First I recommend opening a command prompt window and run setlocal /? and endlocal /? to get displayed the help/documentation for those two commands. Very important to know is that every setlocal without a corresponding endlocal results in an implicit execution of endlocal by cmd.exe before exiting processing of a batch file or a subroutine called with command CALL.
Next I suggest reading this answer for even more details about the commands SETLOCAL and ENDLOCAL and what happens on using them.
I suggest like michael_heath to change this code block:
setLocal EnableDelayedExpansion
set CLASSPATH=.
for /R %JRE_HOME%\lib %%a in (*.jar) do (
set CLASSPATH=!CLASSPATH!;%%a
)
set CLASSPATH=!CLASSPATH!
Better would be:
setLocal EnableExtensions EnableDelayedExpansion
set CLASSPATH=.
for /R "%JRE_HOME%\lib" %%a in (*.jar) do set "CLASSPATH=!CLASSPATH!;%%a"
endlocal & set "CLASSPATH=%CLASSPATH%"
Now the local environment is ended with passing the environment variable CLASSPATH from local environment, on which it was defined, to the restored previous environment because of cmd.exe expands %CLASSPATH% to current value of the environment variable CLASSPATH in current local environment before executing the command endlocal which restores the previous environment.
Wrong in your batch file is also set WINDIR=%SystemRoot%;%SystemRoot% which should be set "WINDIR=%SystemRoot%".
I recommend further reading Why is no string output with 'echo %var%' after using 'set var = text' on command line? It explains why the syntax set "variable=string value" is recommended nowadays. Many of the environment variable definitions use directly or indirectly %UserProfile% which means depending on whatever the user currently running the batch file has entered as user name on creation of the user account. I have seen users entering their name containing a space and non ASCII characters. And I have seen users creating an account with a user name containing character & like Company GmbH & Co. An ampersand outside a double quoted argument string is interpreted as AND operator and cmd.exe tries to execute after set also the remaining string after & as command line on using something like set USERHOME=%DEVHOME%\%USERNAME% instead of set "USERHOME=%DEVHOME%\%USERNAME%". Well, startdev.bat redefines nearly all predefined Windows Environment Variables including USERNAME and USERPROFILE and so is written safe for most environment variable definitions.
This code block is also not optimal:
FOR /F "usebackq" %%i IN (`hostname`) DO SET HOSTNAME=%%i
echo Running on hostname: %HOSTNAME%
The host name respectively computer name could contain also a space or characters critical for command line or start with a semicolon for some unknown reason. So better would be:
FOR /F delims^=^ eol^= %%i IN ('hostname') DO SET "HOSTNAME=%%i"
setlocal EnableDelayedExpansion & echo Running on host name: !HOSTNAME!& endlocal
Whereby there is the environment variable COMPUTERNAME predefined by Windows making it possible to use just following command line:
setlocal EnableDelayedExpansion & echo Running on host name: !ComputerName!& endlocal
An ECHO command line containing an immediately expanded environment variable reference on which it is unknown if its value contains &|<> is always a problem because of the environment variable reference is expanded before further processing of the command line by cmd.exe as described at How does the Windows Command Interpreter (CMD.EXE) parse scripts?
I suggest also reading DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ and avoid the usage of echo. in the batch file to output an empty line.
"halfway through the batch job" as you have a
setLocal EnableDelayedExpansion which sets any further
changes to the variable PATH or other set variables as local.
The endLocal not specified is implied at the end of the script.
To resolve this, use endLocal and set CLASSPATH=%CLASSPATH%
on the same parsed line to set CLASSPATH as global.
Change this part:
setLocal EnableDelayedExpansion
set CLASSPATH=.
for /R %JRE_HOME%\lib %%a in (*.jar) do (
set CLASSPATH=!CLASSPATH!;%%a
)
set CLASSPATH=!CLASSPATH!
to this:
setLocal EnableDelayedExpansion
set CLASSPATH=.
for /R %JRE_HOME%\lib %%a in (*.jar) do (
set CLASSPATH=!CLASSPATH!;%%a
)
endLocal & set CLASSPATH=%CLASSPATH%
After that changed part, the script will set variables as global again.

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

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.

Convert configuration file into variables and convert forward slash to backslash

I have a configuration file which I need for my bash script which has a layout:
A=C:/Example1/A
B=C:/Example2/B
C=C:/Example3/C
I want to use the same configuration file for my windows batch file. I need to convert the above file into variables which I have done using:
for /f "delims=" %%x in (test.txt) do (set "%%x")
How do I go about converting this file into variables while also converting all the forward slashes into backslashes?
Thanks!
add after your for line,
for /f "delims==" %%x in (q888.txt) do call set "%%x=%%%%x:/=\%%"
or, as a replacement for your existing for,
for /f "tokens=1*delims==" %%x in (q888.txt) do set "%%x=%%y"&call set "%%x=%%%%x:/=\%%"
(I used a file called q888.txt for testing)
The first smply executes a substitution, using a parsing trick. The second combines the set and substitution into one cascaded command by tokenising on = into %%x and %%y
This could be done with the following batch code:
#echo off
if not exist "test.txt" goto :EOF
setlocal EnableDelayedExpansion
for /F "usebackq tokens=1* delims==" %%I in ("test.txt") do (
if not "%%~J" == "" (
set "Value=%%~J"
set "Value=!Value:/=\!"
set "_%~n0_%%~I=!Value!"
)
)
echo The variables set from file are:
echo/
set "_%~n0_"
echo/
pause
endlocal
The batch file first checks if the file to process exists in current directory at all. The batch file processing is exited with a jump to predefined label EOF (end of file, requires enabled extensions which are enabled by default) in case of the file test.txt does not exist at all.
Next the file is read line by line with skipping empty lines and lines starting with a semicolon by command FOR which splits each line up into two strings.
The first string left of first equal sign is assigned to loop variable I. Everything right of first equal sign is assigned next loop variable J according to ASCII table.
The IF condition in the loop checks if a value is also defined for a variable. The value is assigned to an environment variable on which a string substitution is executed using delayed expansion to replace all / by \.
Then the modified value is assigned to an environment variable with a name starting with _, the name of the batch file, one more underscore and the string assigned to loop variable I read from the file.
For demonstration the variables with their values are finally output before the local variables are discarded on execution of last command ENDLOCAL.
I strongly recommend not assigning the values read from the file directly to environment variables whose name is completely also read from the file as this makes the batch file easy to manipulate by just modifying the contents of the text file. For example path=C:\Temp in text file would otherwise result in set "Path=C:\Temp" and from this point of batch file execution the running Windows command process would not find anymore any standard executable in directories defined by default in environment variable PATH like %SystemRoot%\System32.
A second variant which incorporates answer posted by Magoo with above batch code:
#echo off
if not exist "test.txt" goto :EOF
setlocal DisableDelayedExpansion
for /F "usebackq tokens=1* delims==" %%I in ("test.txt") do if not "%%~J" == "" set "_%~n0_%%~I=%%~J" & call set "_%~n0_%%~I=%%_%~n0_%%~I:/=\%%"
echo The variables set from file are:
echo/
set "_%~n0_"
echo/
pause
endlocal
The advantage of this variant is that delayed expansion is not needed for this solution which makes it possible to correct process also lines from file containing 1 or more exclamation marks on which first variant fails. And it is also a little bit faster, not noticeable faster for a human, but nevertheless a bit faster.
In both batch code blocks _%~n0_ can be replaced by (nearly) anything including also nothing although that is not recommended. Using just an underscore would be also possible as there are no environment variables defined by default by Windows which start with an underscore.
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 /? ... explains %~n0 (name of argument 0 - the batch file name - without path and without file extension).
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
set /?
setlocal /?
The simplest solution is to let the ~f FOR variable modifier put the full path in canonical form (including conversion of forward slashes to back slashes). I use the DELIMS and TOKENS options to split each line into the variable name and path so that I can apply the ~f to the path. It is important to use tokens=1* instead of tokens=1,2 just in case the path includes a = character.
for /f "delims== tokens=1*" %%A in (test.txt) do (set "%%A=%%~fB")
Note, however, that this strategy only works if your "test.txt" already contains full, absolute paths. If the file contains relative paths, then the ~f modifier will add drive and or folder values from the current directory to turn the relative path into an absolute path.

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%

How can I URL-encode spaces in an NT batch file?

I have the misfortune of working with a program which requires all filenames passed into it to be valid URLs. (No, I don't know why.) Rather than having to drop to the command line and hand-craft file: URLs each time, I'm throwing together a batch file onto which I can simply drop files dragged from the Windows GUI.
A full, proper URL encoder is beyond my needs or interest (most of the characters the app chokes on aren't valid in Windows filenames, anyway), but the two cases I do need to solve are backslashes and spaces. Backslashes are easily handled by variable replacement syntax (SET URL=%URL:\=/%), but spaces are tricky — % is special for both URLs and batch files.
Neither type of batch escaping I'm familiar with (^%, %%) allows the variable replacement to behave as desired and I haven't had any success Googling a solution. Can any batch gurus help me out?
Here's what I have so far:
#ECHO OFF
SETLOCAL
SET URLPATH=file:/%~dp1
SET URLPATH=%URLPATH:\=/%
REM none of these work
REM SET URLPATH=%URLPATH: =%20%
REM SET URLPATH=%URLPATH: =%%20%
REM SET URLPATH=%URLPATH: =^%20%
REM works; I just need to figure out how to generate it
SET URLPATH=file:/C:/Documents%%20and%%20Settings/bblank/example.dat
stupidapp.exe %URLPATH%
ENDLOCAL
Side note - I believe you want %~f1 instead of %~dp1
You need to switch over to delayed expansion.
#echo off
setlocal enableDelayedExpansion
set "URLPATH=file:/%~f1"
set "URLPATH=!URLPATH:\=/!"
set "URLPATH=!URLPATH: =%%20!"
stupidapp.exe !URLPATH!
endlocal
A bit more work is required if any of your file names happen to contain the ! character because it will be corrupted when %1 is expanded if delayed expansion is enabled.
#echo off
setlocal disableDelayedExpansion
set "URLPATH=file:/%~f1"
setlocal enableDelayedExpansion
set "URLPATH=!URLPATH:\=/!"
set "URLPATH=!URLPATH: =%%20!"
stupidapp.exe !URLPATH!
endlocal
endlocal
dbenham's solution is almost certainly preferable (being rather easier to read), but for the sake of completeness, here is an alternative solution:
SET URLPATH=file:/%~dp1
SET URLPATH=%URLPATH:\=/%
REM for each space in the path, split the path into the portions before and after
REM that space, then join them with an escaped space
:ESCAPE_SPACE
SET TRAILING=%URLPATH:* =%
CALL SET URLPATH=%%URLPATH: %TRAILING%=%%
SET URLPATH=%URLPATH%%%20%TRAILING%
IF NOT "%URLPATH%"=="%URLPATH: =%" GOTO ESCAPE_SPACE
stupidapp.exe %URLPATH%

Resources