Batch: How do I parse a string containing a filesystem path? - batch-file

I have a string contained in a variable, for example:
"C:\Users\SomeUser\Desktop\SomeFolder\File.jar"
I would like to parse File.jar from this string into another variable. I currently have this somewhat working with the code:
FOR /f "tokens=1-6 Delims=\" %%A IN (%string%) DO (set myvariable=%%F)
This works as long as the folder path remains the same length. However, I want to be able to move the program and file and still have everything work right. Is there any way to parse, just as an idea, from right to left? Any advice would be greatly appreciated.

Try to apply path modifiers as follows:
set "inputPath=C:\Users\SomeUser\Desktop\SomeFolder\File.jar"
for %%i in ("%inputPath%") do set fname=%%~nxi
echo %fname%
%%~nx<loop-var> extracts the filename root (n) and filename extension (x) from the loop variable; i.e., it extracts the last/filename component from the loop variable's value.
(%%i was chosen as the loop variable in this case, but any letter will do.)
P.S.: Another frequently used construct is %%~dp<param-or-loop-var-> to extract the drive spec. (d) and the absolute path (without drive spec.) with a terminating \ (p) - this even works for relative input paths.
For instance, %%~dp0 will expand to the absolute path of the folder in which a batch file is located.
A list of all supported path modifiers is here.
(Note that they're only discussed in terms of parameters, but they equally work with for-loop variables).

I suppose you could change the script to process all tokens using *. And since you're setting your variable with the value of each token, at the end the last token will be assigned to the variable.
FOR /f "tokens=* Delims=\" %%A IN (%string%) DO (set myvariable=%%F)

Related

Get absolute path via CMD script of directory

script-being-invoked.cmd:
REM This is my script file, it is being invoked from anywhere
REM Get absolute path to root directory
set astr=%~dp0
set substr1=\mq_1.7.6\interface\
set substr2=\
call set rootPath=%%astr:%substr1%=%substr2%%%
I'm trying to get the absolute directory path a few paths below from the file being invoked in the interface directory. The issue however is that the path in the code above changes. The version in mq_1.7.6 could change. Is there anyway to get this value via cmd scripting? I would have done \..\.. but this doesn't seem to work in Windows.
I usually do that using the ~fi expansion pattern. Unfortunately this can only be used (at least to my knowledge) in a for loop.
set "parentdir=%~dp0.."
for %%i in ("%parentdir%") do set "realparent=%%~fi"
echo "%parentdir%"
echo "%realparent%"
If the above is in a batch file located in c:\foo\bar\mq_1.7.6\interface the variable realparent will contain c:\foo\bar\mq_1.7.6 and parentdir would contain c:\foo\bar\mq_1.7.6\interface\...
So the for loop essentially turns a relative path into an absolute path.
It appears from your question, that you're trying to retrieve the name of the scripts grandparent, in this case mq_1.7.6.
It is still a little unclear however, whether you want that in isolation, i.e. mq_1.7.6, or as an absolute path, e.g. P:\ath\to\mq_1.7.6
If the former is your intention then the following should suffice:
#For %%A In ("%~f0\..\..")Do #Echo(%%~nxA
If you need that as a variable then:
#For %%A In ("%~f0\..\..")Do #Set "Gp=%%~nxA"
If you're just trying to capture the version string from mq_1.7.6, in this case 1.7.6, then a simple additional line may be all that is required:
#For %%A In ("%~f0\..\..")Do #Set "Gp=%%~nxA"
#Set "Gp=%Gp:*_=%"
If on the other hand you wanted the latter, i.e. the absolute path of the scripts grandparent, the the only real difference is in the metavariable expansion:
#For %%A In ("%~f0\..\..")Do #Echo(%%~fA
If you need that as a variable then:
#For %%A In ("%~f0\..\..")Do #Set "Gp=%%~fA"
If neither of my interpretations of your intent are correct, can you please provide an example of what you wanting to be returned, and whether you want that simply printing to the console, or saving as a variable etc.
Here's one method:
#pushd "%~dp0..\"
#echo %cd%
#popd
shorter one line:
#pushd "%~dp0..\" && echo %cd% && popd

set PATH with multiple lines

In a Batch file I need to add some paths to the PATH env variable. Since its a larger numer of long paths I tried to spread them on multiple line and tried to make the bat-file as clean as I can by indenting the lines.
But it seems that the spaces at the beginning of the newline (and so in the %PATH%) are interpreted as part of the actual path.
So
SET PATH=^
\\somewhere\Tools\strawberryperl\perl\site\bin;^
\\somewhere\Tools\strawberryperl\perl\bin;^
\\somewhere\Tools\strawberryperl\c\bin;^
\\somewhere\Tools\KDiff3;^
%PATH%
does not work (programs are not found). Is there some trick I can use?
Because it is a medium complex batch file some indentation would be nice.
for %%x in (
"\\somewhere\Tools\strawberryperl\perl\site\bin;"
"\\somewhere\Tools\strawberryperl\perl\bin;"
"\\somewhere\Tools\strawberryperl\c\bin;"
"\\somewhere\Tools\KDiff3;"
) do call set "path=%%path%%%%~x"
this will append the extra items to the path. You'd need to initialise path to nothing first if all you want is to build the directory sequence specified.
There is no way to have PATH ignore the leading spaces. I see two possible options if you want indented lines:
Option 1 - Use undefined variable to indent, so spaces never get in the value
#echo off
SET PATH=^
% =%\\somewhere\Tools\strawberryperl\perl\site\bin;^
% =%\\somewhere\Tools\strawberryperl\perl\bin;^
% =%\\somewhere\Tools\strawberryperl\c\bin;^
% =%\\somewhere\Tools\KDiff3;^
% =%%PATH%
Option 2 - Remove the spaces afterwards
#echo off
SET PATH=^
\\somewhere\Tools\strawberryperl\perl\site\bin;^
\\somewhere\Tools\strawberryperl\perl\bin;^
\\somewhere\Tools\strawberryperl\c\bin;^
\\somewhere\Tools\KDiff3;^
%PATH%
set "PATH=%PATH:; =%"
First let me start by informing you that adding to the PATH variable in this way is ONLY for the running session. Once the cmd session is closed that variable returns to its previous value.
Here are a suggestion, append each addition one by one:
SET "ToAdd=\\somewhere\Tools\strawberryperl\perl\site\bin;"
SET "ToAdd=%ToAdd%;\\somewhere\Tools\strawberryperl\perl\bin;"
SET "ToAdd=%ToAdd%;\\somewhere\Tools\strawberryperl\c\bin;"
SET "ToAdd=%ToAdd%;\\somewhere\Tools\KDiff3"
SET "PATH=%PATH%;%ToAdd%"
BTW, if you were hoping to add to the environment variable beyond the running session then it is important that you ignore anyone suggesting you use SETX instead of SET. (the variable will be truncated at 1024 bytes therefore corrupting it). Your best solutions would involve editing the registry and possibly using a built in tool such as powershell.
Edit
This shows the method mentioned in my comment and uses the same structure as Magoo's answer:
C:\MyDir\Paths.txt
\\somewhere\Tools\strawberryperl\perl\site\bin
\\somewhere\Tools\strawberryperl\perl\bin
\\somewhere\Tools\strawberryperl\c\bin
\\somewhere\Tools\KDiff3
batch file
#Echo Off
SetLocal EnableDelayedExpansion
For /F "UseBackQDelims=" %%A In ("C:\MyDir\paths.txt") Do Set "Path=!Path!;%%~A"
Echo(%Path%
EndLocal
Timeout -1
This means that you only really need to include the for loop each time instead of adding each of the paths to it.
Not even remotely bulletproof, but the Magoo's answer reminded me this. Just because someone, somewhere, could find a better usage for this construct
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "delims=" %%a in ('echo "%path:;=" "%"
"\\somewhere\Tools\strawberryperl\perl\site\bin"
"\\somewhere\Tools\strawberryperl\perl\bin"
"\\somewhere\Tools\strawberryperl\c\bin"
"\\somewhere\Tools\KDiff3"
""') do (set "path=%%~a") & call set "path=%%path:" "=;%%"
path

Modify variable within loop of batch script

I am moving files based on their names to preset folders. I don't want to make new folders. So files should only be moved if the corresponding folder is existing already.
The file names all follow the same pattern: 1234_123456_AA_***********.(doc/pdf)
I have the following script below which works:
#echo on
for /r %%f in (*.*) do (
echo processing "%%f"
for /f "tokens=1-3 delims=_" %%a in ("%%~nxf") do (
move "%%f" C:\Users\xxxxxxxxx\Desktop\MOVEFILES\%%a_%%b_%%c\
)
)
pause
But the issue I am running into is that some of the files names have a '0' place holder in loop variable %%b, for example 1234_0123456_AA. But this file name should be interpreted like 1234_123456_AA and I want this file moved into the appropriate folder.
I have written this:
#echo on
SETLOCAL ENABLEDELAYEDEXPANSION
for /r %%f in (*.*) do (
for /f "tokens=1-3 delims=_" %%a in ("%%~nxf") do (
set z=%%b%
echo !z:~-6!
move "%%f" C:\Users\xxxxxxxxx\Desktop\MOVEFILES\%%a_%%z_%%c\
)
)
pause
I get the echo to remove the '0' place holder, but need to get that back into %%b in the file path of where the file should be moved to.
What to modify in code to get the desired behavior?
Use the following code with the corrections of JosefZ applied and some additional improvements:
#echo off
setlocal EnableDelayedExpansion
for /R %%F in (*_*_*.*) do (
for /F "tokens=1-3 delims=_" %%A in ("%%~nxF") do (
set "MiddlePart=%%B"
set "MiddlePart=!MiddlePart:~-6!"
if exist "%USERPROFILE%\Desktop\MOVEFILES\%%A_!MiddlePart!_%%C\*" (
move /Y "%%F" "%USERPROFILE%\Desktop\MOVEFILES\%%A_!MiddlePart!_%%C"
)
)
)
endlocal
pause
The improvements on code are explained below in details.
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.
echo /?
endlocal /?
if /?
move /?
set /?
setlocal /?
1. Search pattern
The first improvement is the pattern used in first FOR as only files should be moved with at least 2 underscores in file name. This pattern is still not the best one, but good enough for this task.
2. Loop variable
It is better to use loop variables in upper case to avoid problems with the modifiers.
For example using as loop variable %%f and using inside the loop %%~f to use the string (must not be a file or folder name) of loop variable f without surrounding quotes, command processor exits batch processing with an error message because it expects one more letter, the loop variable as %%~f is interpreted as full name of file/folder of loop variable ?.
The loop variables and the modifiers are case sensitive. Therefore %%~F is interpreted by command processor as string of loop variable F without surrounding quotes and %%~fF as file/folder name with full path and extension of the file/folder of loop variable F.
Some other characters like # can be also used as loop variable.
3. Assigning value to environment variable with quotes
On assigning a string to an environment variable, it is always good to use
set "variable=text or other variable"
Using the quotes as shown here has the advantage that not visible spaces/tabs after last double quote are ignored by command processor.
But with using just
set variable=text or other variable
everything after first equal sign up to line termination is assigned to the variable including trailing spaces and tabs added perhaps by mistake on this line in the batch file. This is nearly never good and a common source of a batch execution problem which can be easily avoided by using quotes right.
Using the quotes as show below is also not good as in this case both double quotes are part of the text assigned to the variable (plus trailing spaces/tabs). This is sometimes useful, but is most often a coding mistake.
set variable="text or other variable"
4. Delayed expansion
Referencing a variable set or modified within a block defined with (...) requires delayed expansion if the current variable value should be used and not the value assigned to the variable above the block. Therefore using %%z was wrong in original code as variable z was not defined above first FOR loop and therefore was replaced always with nothing on execution of the loops.
5. Environment variable USERPROFILE
Running in a command prompt window set results in getting displayed all predefined environment variables for the current user account. There is the variable USERNAME, but also USERPROFILE containing path to the user´s profile directory with the Desktop folder and other user account related folders. Using environment variable USERPROFILE makes the batch file less dependent on Windows version and language of Windows.
ATTENTION:
The first FOR runs because of /R recursive on current directory and all its subdirectories. As the inner FOR loop moves all found files in current directory tree to subdirectories of %USERPROFILE%\Desktop\MOVEFILES, the current directory should be never any directory of this path.

Tokens not creating from a variable by for [batch]

i want to extract tokens from a variable by using / as delimiter.
i used this ::
set fileinput=properties\iam\self_iam_properties.xml
for /f "tokens=*delims=\" %%a in (%fileinput%) do #echo %%a
which gives me following error :
The system cannot find the file properties\iam\self_iam_properties.xml.
why is it searching for a file, i just need to use the value present in the variable. i do not want to take values from a file.
Also after parsing, i wish to create a file named self_iam_parsedoutput.txt so for that i need to extract self_iam_ from the input file name. how to proceed with that ?
Put the %fileinput% in double quotes to treat it like a string.
for /f "tokens=*delims=\" %%a in ("%fileinput%") do #echo %%a
Note that because you're using tokens=*, it won't actually split into multiple variables. If you want each section of the path to get its own variable, you could use something like tokens=1-26
A second way to escape variables in for /f is to run a command which outputs the variable.
for /f %a in ('echo.%variable%')
This form is useful if you're otherwise running into problems with double quotes.
As for the filename change, if the input file is always xyz_properties.xml and the output is always xyz_parsedoutput.txt, then you can use a simple text substitution of the form %variable:old=new%
set inputfile=self_iam_properties.xml
set outputfile=%inputfile:properties.xml=parsedoutput.txt%

Find line in text file, check for text in between?

Question about Batch/Windows/CMD:
I would like that my batch file can search for a line (which I already achieved, but what comes next not), it looks like this:
<name>MyName</name>
It needs to find the text in between <name> and </name>. After that it needs to set that as a variable (%name%).
Does anyone have any idea?
EDIT: if someone wants to give an answer, please list the code. Perl is OK, but this should be open-source and not everyone has Perl.
It can be done this way (assuming your input is in file "test1.html"):
findstr "<name>" test1.html > temp1.lis
FOR /F "tokens=2 delims=>" %%i in (temp1.lis) do #echo %%i > temp2.lis
FOR /F "tokens=1 delims=<" %%i in (temp2.lis) do #echo %%i > temp3.lis
The first line is a guard that only HTML/XML tag
"name" will match in the two FOR lines (you may
already have done this). The result is saved in a temporary
file, "temp1.lis".
The second line capture what is to the right of the first
">" - in effect what is after "<name>". At this stage
"MyName</name" is left in temporary file "temp2.lis" (as
the closing tag also contains ">"). Note the double "%"s
(%%i) as this is in a BAT file (if you want to test directly
from the command line then it should only be one "%").
The third line capture what is to the left of the first "<"
- this is the desired result: "MyName" (is left of "<" in
"MyName</name"). The result is in variable %%i and you
can call a function with %%i as a parameter and access the
result in that function (in the FOR line above the function
was the built-in "echo" and the result thus ended up in
temporary file "temp3.lis" by the redirection of standard
output)
Note that the above only works if
<name>MyName</name>
is the first HTML/XML tag in a line.
If that is not the case or you want a more robust solution
you can instead call a function in the first FOR line (that
receives %%i as the first parameter). That function can then
replace "<name>" with a single character that you are
sure is not in the input, e.g.:
set RLINE=%MYLINE:<name>=£%
Explanation: if the input line is in variable %MYLINE% then
"<name>" will be replaced with "£" and the result will be
assigned to variable %RLINE%.
The reason for the replace is that the delimiters for the
FOR loop are single character only.
You can then use "£" as a delimiter in the FOR loop (to extract what is
to the right of "<name>" - as before):
echo %RLINE%>temp5.lis
FOR /F "tokens=2 delims=£" %%i in (temp5.lis) do #echo %%i > temp6.lis
You have to repeat this technique for "</name>"
(but only if <name>MyName</name> is not
the first HTML/XML tag in a line).
So as you see it is possible, but is quite painful.
Learn Perl, it's made for exactly that kind of thing.

Resources