String compare with batch file [duplicate] - batch-file

This question already has an answer here:
Variables are not behaving as expected
(1 answer)
Closed 3 years ago.
A very simple batch file. I'm trying to search for file extensions that are not .txt. There will be one .txt, but the rest will be like .txt_20190607.
for %%I in (\\01mtsdv130\Myapp\Log\*.*) do (
set var1=%%~xI
echo %var1
if %var1%==".txt" (
echo Matches
) else (
echo does not match
)
)
I have files in that folder both .txt and those with the extra date info in the extension. What do I have wrong?

There are two problems in the code.
The first one is that %-based expansion of normal variables is rather "static", in that it happens the first time code is parsed/executed and is fixed since then. That means that in iterations of the loop after the first, the result of %var1% will not change. You'd have to use !var1! (along with setting EnableDelayedExpansion) to get the behaviour you want.
An easier alternative is to get rid of var1 altogether and just use %%~xI.
The other problem is that CMD treats quotes (almost) as any other character. Most notably, the strings a and "a" are not considered equal. Therefore, the if should look like this:
if "%%~xI"==".txt" (

Related

Why is my for loop repeating the last operation? [duplicate]

This question already has answers here:
Where does GOTO :EOF return to?
(4 answers)
Closed 8 months ago.
I have a batch file which is performing a series of functions for each item in a file. It's running correctly, however, for some reason, it's performing the operation for the last line in the file twice. Can anyone help me determine why? This is the first for loop I've made on my own, so I'm sure I've made some mistakes.
for /F "tokens=*" %%A in (nations.txt) do (
set "nationname=%%A"
call :ageofdiscovery
)
Point of clarification, what I am trying to do is call each and every line of "nations.txt" one at a time, storing them as a variable, then performing an elaborate series of operations using that variable, before moving on to the next line, and running through the whole of "nations.txt". The idea is to allow the script to work for an arbitrary number of loops, so as to make the script more flexible (It's a text generator, creating histories for fantasy kingdoms).
If there is no problem with the for loop, could someone explain why it's repeating the last output? I have an exit command after the loop, so it shouldn't be executing the script again, and it also has the same random generated output for the repeated last line.
EDIT: As requested, the current contents of nations.txt is:
Nation1
Nation2
Nation3
Nation4
As for the batch script itself, it's 2,134 lines long (and runs perfectly fine with a hard-coded version of the nation selection system. I'm retrofitting code here). I'm unsure of what, or where, any problems could be occurring. I also know that people here do not want me to share the entire script. I will do as requested in relation to the script itself.
The best guess for your observed phenomenon is that your function is defined directly after your loop (you don't show that important detail).
for /F "tokens=*" %%A in (nations.txt) do (
set "nationname=%%A"
call :ageofdiscovery
)
:ageofdiscovery
echo %nationname%
In that case, the function code will be executed after the loop is finished, because batch-files don't have a concept of functions, they only know labels.
It can be easily fixed by an exit /b or goto :eof
for /F "tokens=*" %%A in (nations.txt) do (
set "nationname=%%A"
call :ageofdiscovery
)
exit /b
:ageofdiscovery
echo %nationname%

How to pass a string to a Windows command that expects a file argument? [duplicate]

This question already exists:
How to pass a string to a Windows cmd that expects a file argument? [closed]
Closed 2 years ago.
Suppose a program cook takes one argument: the pathname of a text file containing the recipe of the food to cook. Suppose I wish to call this program from within a batch script, also suppose I already have the recipe in a string variable:
set the_recipe = "wash cucumbers" "wash knife" "slice cucumbers"
cook ... # What should I do here? It expects a file, but I only have a string.
Adapted from here.
How can I pass the recipe to the command when it expects a filename argument?
I thought about creating a temporary file just for the purpose passing a file, but I wish to know if there are alternative ways to solve this problem.
Unfortunately, Batch does not provide a mechanism similar to the Bourne shell heredoc. There are two ways to do this:
Option 1: Change the command
If you have access to the cook executable, you can add a flag to indicate passing a string or list of strings instead of a file. For example, COOK/A might take argument input, while COOK/F takes a file.
Option 2: Use a temporary file
Use a temporary file. The typical way to generate a temporary file is:
SET TEMPFILE=%TMP%\%~N0-%RANDOM%.tmp
ECHO.FILE CONTENT LINE 1 >> %TEMPFILE%
ECHO.FILE CONTENT LINE 2 >> %TEMPFILE%
REM AS MANY LINES AS ARE NEEDED
REM USE THE FILE
DEL/F "%TEMPFILE%" & REM DELETE THE TEMPORARY FILE
Remember that putting "" in an ECHO statement will cause the quotation marks to be included in the file, and that ECHO. must be used to include an indent in the file.
If you mean to convert the variable in your example into a tempfile with each quoted segment on a separate line, you'll need to use FOR:
FOR %A IN (%THE_RECIPE%) DO (
ECHO.%~A >> %TEMPFILE%
)
Including the ~ in the variable substitution strips out the quotation marks that would otherwise end up in the file. If you want the quotation marks in the file, you can omit the tilde.

cmd batch variable behavior is unintuitive

I'm using windows 10, running batch files through the command prompt window.
I can make things work, but I don't know why it works or why I can't do certain things:
set "file_list=a1 a2"
for %%a in (%file_list%) do (
echo %%a.py
)
This little piece of code works. I can build on it, BUT
Q1: I want to change the variable %%a to %%filename... but that doesn't work! I wondered if maybe filename were reserved, so I tried %%fname .
In this case I get the error:
%fname was unexpected at this time.
I can do a set fromm the command line and use a descriptive variable name, but it doesn't seem to work when looping. (I did it with the %file_list% variable above!) So how come I can only use a single character for a loop variable? Is there some way around that?
Q1a. This makes me think that the loop index variable is a different kind of variable that the ones in set commands. Is that correct? If so, is there a link that clearly and concisely explains the difference?
Q2. I notice the loop index variable is %%a, instead of a or %a or %a% . I never would have guessed this. The web sites I've looked at have just said, do this. But I can't see any explanation of why, except that the first percent is an escape. Okay. That doesn't really explain anything. It just means "this is how you do it." The error message when I use one percent sign is interesting.
set "file_list=a1 a2"
for %a in (%file_list%) do (
echo %a.py
)
"file_list) was unexpected at this time."
So I can vaguely see that maybe something isn't being escaped correctly. Why does that % in the %a need to be escaped, so it becomes %%a ?
A for meta-variable must consist of % (in Command Prompt) or %% (in a batch file) and a single character (case-sensitive letter), like %a or %%a. You cannot define %filename or %%filename.
Loop variables only exist within the respective for loop. Do not confuse such loop variables with normal environment variables, like %TEMP%, for example, which are available globally.
There are these things marked by %-signs:
%-escaping (only applicable for batch files), so %% denotes one literal %-sign;
command line arguments/parameters (only applicable for batch files, obviously), like %1;
immediately expanded environment variables*, like %TEMP%;
for meta-variables, like %a (in Command Prompt) or %%a (in batch files), which are specific to the for command, so they do not exist outside of the related loop context;
%-escaping (1.) happens before expanding for meta-variables (4.), hence actually the for command receives a loop variable like %a.
Then environment variables (3.) are treated differently in Command Prompt and in batch files: the former keeps undefined variables literally, the latter removes them.
The detailed parsing rules can be found in this post, which have been implemented by Microsoft (or IBM?) that way in order to be able to distinguish between the different %-things, so at the end it was their decision, therefore you have to ask them for the exact reason…
*) There is also something like delayed environment variable expansion, but this uses !-signs to mark the variables, like !TEMP!, and this is something that happens after all the %-sign expressions have been parsed.

Batch File How to echo Series Variable's Value in a loop [duplicate]

This question already has answers here:
Arrays, linked lists and other data structures in cmd.exe (batch) script
(11 answers)
Batch Script - How to get variable value using a different variable that has the variable name stored
(1 answer)
using a DelayedExpansion index variable for an array within an IF statement fails
(2 answers)
Closed 3 years ago.
I have a series variable named Installer1 to Installer18
I am trying to install all installer 18 using a loop, so far I have this code to check if I am getting the value but the real value of variable is not reflecting, I believe there is missing on my code but I have tried my best but it is not enough. Thank you in advance
Here is what I have so far
I've got this result, I want to echo the value of the variable i declared on top of my loop but I got the variable name instead of value. I am not sure how to concatenate string and script will look it as a variable name.
I tried below code:
And I got this result:
Use array , Please try this.
#echo off
setlocal enabledelayedexpansion
SET var[0]=A
SET var[1]=foo bar
SET var[2]=123
for %%a in (0,1,2) do (
echo !var[%%a]!
)
echo Get one of the variables directly: %var[1]%

How to get a the directory path from a variable in a cmd batch file

Within my batch file I have a variable that contains a file path:
SET VAR1=C:\Folder1\Folder2\File.txt
I would like to extract on the directory structure and retreive:
C:\Folder1\Folder2\
I have read threads like this where I need to use %~dp0 where 0 I believe is passed as a parameter. I have tried %~dpVAR1 but that doesn't work. How can I get the output I'm looking for, but with a variable containing the file path?
Also, to make matters difficult, I have to perform all of this within an IF condition which means that once the variable is declared, I will need to refer to it with ! instead of % (I have declared setlocal enableextensions enabledelayedexpansion at the beginning of my script to allow for this).
Any help is much appreciated!
Thanks!
Andrew
You are attempting to use parameter expansion syntax on an environment variable - that cannot work. But it is relatively easy to do what you want.
Using a CALL (relatively slow):
(...
call :getPath "!var!" var
...
)
exit /b
:getPath
set "%2=%~dp1"
exit /b
Using FOR, assuming the variable does not contain any wildcards (fast)
(...
for %%F in ("!var!") do set "var=%%~dpF"
...
)
Using FOR, if the variable may contain wildcards (also fast)
(...
for /f "delims=" %%F in ("!var!") do set "var=%%~dpF"
...
)
Note 1: If the variable does not contain the full path, then all the solutions will attempt to resolve the name into an absolute path and will return the full absolute path. For example, if var contains foobar\test.txt, then the solutions will include the full path to the current directory, even if the file is not found. Something like c:\pathToCurrentDirectory\foobar\.
Note 2: All solutions above will remove all quotes from the path.
Note 3: A path could include the ! character, which will cause problems when expanding %~dp1 or %%~dpF because you have delayed expansion enabled. The delayed expansion will corrupt both ^ and ! if the value contains !. There is a solution that involves protecting both ! and ^. Here is a demonstration applied to the last solution above. The protection requires normal expansion, and since you are within a code block, it requires at least one CALL. It could be done without a subroutine, but it is easier with a subroutine. The subroutine assumes the variable is named var.
(...
call :getPath
...
)
exit /b
:getPath
set "var=!var:"=!"
set "var=!var:^=^^^^!"
set "var=%var:!=^^^!%" !
for /f "delims=" %%F in ("!var!") do set "var=%%~dpF" !
exit /b
I do believe (once again) many questions are on the same topic (string constraints, or splitting strings).
Instead of giving you the whole code, I'm going to give you a template and explain why %~dpVAR! didn't work.
Firstly, why %~dpVAR! did't work.
Before I get into modifiers, let's discuss parameters. You may know that batch files can parse parameters to each other. These parameters can be called by using a single percent sign (%) in front of the numbers 0-9. As far as I'm aware (someone might have made a way for more to be parsed), only 9 parameters can be parsed. You may think that is wrong (there's 10 parameters right?). Parameters 1-9 are parsed to the batch file (or function within one), %0 is the file path of the batch file (or function name). If you look, %~dp0 shares some (not really) resemblance to %0. This will be discussed below.
Secondly, the term %~dp0 has modifiers in it. Modifiers are things that modify variables (only in the case of parameters and those declared in for loops, you know the ones with double percent signs like %%i) and parameters. The modifier d expands the parameter to a drive letter only while p expands the parameter to a path only. You may think that these would contradict themselves, but parameters can be combined to create extremely wacky formats.
So, as you can see, you attempt at replacing 0 with your variable name failed because it's not specified for those sort of things.
Now, on to the template.
You can constrain variables (and put them into other variables) like this:
set variable=!variable:~offset,amount!
Don't worry if that seems confusing, I'm about to explain the components.
Firstly, notice that there is no /a switch. This is because this is not a mathematical function (don't really know why I added this). So, before I explain it, here's an example of what it would do to a variable name numbers that has the value of 0123456789.
set numbers=!numbers:~5,1!
By using that line of code, numbers would now equal 5. This is because it is recreating the variable with a smaller version of the original value (gee this is hard to explain). As you can see, there is a 5 where offset was on the template above. This is because it is skipping the first 5 characters and setting the variable as the next amount, or 1 character (I really hope you're getting this).
So basically, it sets a variable as a shorter value of a different (or the same) variable determined by the offset and the amount of characters to contain in it.
I really hope this helps because I probably wouldn't understand a word of this.
Can someone redirect this poor guy to a link explaining this better (I tried, ok!)?
Complete example of extracting paths from variable:
#echo off
set /p Fullpath="Specify full path: "
call :getPath %Fullpath% filename folder
echo %filename%
echo %folder%
pause
exit /b
:getPath
set "%2=%~nx1"
set "%3=%~dp1"
exit /b
Would this work:
SET VAR1=C:\Folder1\Folder2\File.txt
echo %var1%
Where Echo is the name of your exe.
%CD% may work as well: Echo %CD%

Resources