Using variable in sub-string operation in batch file FOR loop - variables

I am trying to use the FIXED_LENGTH variable within a FOR loop.
Using the length of the string directly in the loop results in correct result, however trying to use the variable does not work. Any ideas?
Snippet that provides correct result
SETLOCAL ENABLEDELAYEDEXPANSION
REM Loop through the input file and read each line.
FOR /F %%G IN (SVN_URLs_List.txt) DO (
SET CUR_LINE=%%G
REM Make sure the variable CUR_LINE is surrounded with ! instead of % to ensure delayed variable expansion.
#ECHO Processing the line: !CUR_LINE!
REM Set fixed length of "https://projects.abcdefg.com/subversion/"
SET FIXED_LENGTH=40
REM Get the sub-string of CUR_LINE to get the folder name.
SET FOLDER_NAME=!CUR_LINE:~40!
#ECHO !FOLDER_NAME!
)
ENDLOCAL
Snippet that does NOT produce correct result:
SETLOCAL ENABLEDELAYEDEXPANSION
REM Loop through the input file and read each line.
FOR /F %%G IN (SVN_URLs_List.txt) DO (
SET CUR_LINE=%%G
REM Make sure the variable CUR_LINE is surrounded with ! instead of % to ensure delayed variable expansion.
#ECHO Processing the line: !CUR_LINE!
REM Set fixed length of "https://projects.abcdefg.com/subversion/"
SET FIXED_LENGTH=40
REM Get the sub-string of CUR_LINE to get the folder name.
SET FOLDER_NAME=!CUR_LINE:~!!FIXED_LENGTH!!!
#ECHO !FOLDER_NAME!
)
ENDLOCAL
I have tried
SET FOLDER_NAME=!CUR_LINE:~!!FIXED_LENGTH!!!
SET FOLDER_NAME=!CUR_LINE:~!FIXED_LENGTH!!
SET FOLDER_NAME=!CUR_LINE:~%FIXED_LENGTH%!
The input in the SVN_URLs_List.txt looks like:
https://projects.abcdefg.com/subversion/svnrepo1
https://projects.abcdefg.com/subversion/svn_repo_2
https://projects.abcdefg.com/subversion/another repo 3
I am expecting result:
svnrepo1
svn_repo_2
another repo 3
The first snippet produces right results.
However using the second snippet produces something like:
https://projects.abcdefg.com/subversion/svnrepo1FIXED_LENGTH
https://projects.abcdefg.com/subversion/svn_repo_2FIXED_LENGTH
https://projects.abcdefg.com/subversion/another repo 3FIXED_LENGTH

SET FOLDER_NAME=!CUR_LINE:~!!FIXED_LENGTH!!!
SET FOLDER_NAME=!CUR_LINE:~!FIXED_LENGTH!!
Wrong syntax. Where does the variable reference begin/end? The parser can not determine it.
SET FOLDER_NAME=!CUR_LINE:~%FIXED_LENGTH%!
Almost, the parse can handle this syntax but bad behaviour. As the variable (FIXED_LENGTH) is assigned its value inside of the for code block, and as the parser replaced the read operation on the variable with its value at parse time (no delayed expansion syntax used to read the variable value) before the variable was assigned its value, the operation will fail.
In your case, instead of defining the FIXED_LENGTH variable inside the for loop, you can define the variable before the for loop, out of it. So, when the block of lines in the do clause of the for command is parsed, the variable will have the adecuated value.
If you prefer/need to declare the variable inside the for loop, you will have to use something like
for %%a in (!FIXED_LENGTH!) do SET FOLDER_NAME=!CUR_LINE:~%%a!
That way the read operation on FIXED_LENGTH is delayed, assigned to the replaceable parameter of the for loop and this parameter is used instead of the variable reference in the substring operation.

Related

Using a Batch file can you remove part of a file name from files in a folder and all sub folders?

Hi I am new at using batch files and I am struggling to find a way of removing part of a file name for multiple files in a folder and all sub-folder.
the files are all named like r1_c02_200111_145423_am.csv and I need to remove the _am from the files.
I have tried the following
FOR /R "C:\Users\bob\Documents\data\" %%G IN (*_am.csv) DO REN "%%G" *.csv
but this does not change anything.
can anybody point me in the right direction please?
With the help of such a script, you can perform the required operation.
#ECHO off
FOR %%i IN (*_am.csv) DO CALL :do_rename %%i
GOTO :eof
:do_rename
SET "_file=%1"
REN "%_file%" "%_file:~0,-7%.csv"
:eof
Now more.
The search in the question was correct. To perform a rename while passing through the FOR ... DO, you can use both the SETLOCAL EnableDelayedExpansion or a procedure call. I think it's easier to use a procedure call. you don’t have to puzzle over understanding (and misunderstanding) how the SETLOCAL EnableDelayedExpansion works.
For each iteration of the loop, the Wick procedure is called with the first argument of the file name passed to it:
CALL :do_rename %%i
In the procedure itself, the argument is converted to a variable. You can already use substring operations on the variable itself. More information about substring operations can be found here.
:do_rename
SET "_file=%1"
REN "%_file%" "%_file:~0,-7%.csv"

Simple SET not working in batch file

Disclaimer: I'm not a batch files guru, just digging my way with the help of Google and SO. Also note that I'm trying to run this batch file as a Custom Action in SourceTree.
The following simple SET command doesn't seem to assign value to the variable:
SET MYVAR = %1
ECHO "%MYVAR%"
ECHO "!MYVAR!"
ECHO %1
The first ECHO above returns ""; the second one "!MYVAR!", whereas the last one returns the actual value of command-line parameter. How can I assign value to MVAR?
set is sensitive to spaces on each side of the =. You are setting a variable named myvar[Space}

How to override batch variable number of characters limit?

I'm having an issue on the current project I'm working on.
I have to get a query stored from a file into a variable, whose file only contains a query (It is generated by part of the program). For instance I use the following code:
set /p query=<path\to\my\folder\fileContainingQuery.soql
It works when the query isn't that long, but when its length is beyond 1024 characters, the query is truncated and since I gotta send it to another configuration file using the FART tool, that's quite not convenient.
How to override this problem?
There is a limit in how many data can be stored in a environment variable. In theory, the maximum length is 32767 characters, BUT a batch file cannot set a variable that is longer than the maximum command line length (8192).
You can use a for /f command to read the line
for /f "usebackq delims=" %%f in ("path\to\my\folder\fileContainingQuery.soql") do set "q=%%f"
Or you can generate a auxiliary batch file to assign the content of the query
<nul set/px=set q=>sql.set.cmd
type "path\to\my\folder\fileContainingQuery.soql">>sql.set.cmd
call sql.set.cmd
And both will get the maximum possible text into the variable ONLY if the final command line is not too big (8186 characters in my tests). In other case, nothing is retrieved.
BUT, once the value has been retrieved, is has to be used inside a command. And here the same limitation happens. 8192 is the maximum line length and the content of the variable IS inside the line.
So, your final limit for the length of the query is less than that.
#MC ND: Thanks for your answer, I used the for /f command this way after thinking back at it. (Didn't read your answer at this moment).
(for /f "delims=" %%b in (path\to\my\folder\fileContainingQuery.soql) do (
set query=%%b
))
echo %query%
My hypothesis for my previous problem is that the set /p var function may only accept 1024 characters on user input, and as my query file only contains one line which length could be greater than this limit, it truncates the query. The above code works fine for me and I get my complete query in the variable.

How can i remove the extension of a filename which is saved in a batch variable?

first my batch script snippet (not working):
set _WRx32=wrar420.exe
set _WRx64=winrar-x64-420.exe
set _WRLNG=rarlng.rar
FOR %%f IN (_WRLNG, _WRx64, _WRx32) do set "%%f"="%%~nf"
[ for every variable name (!) in the list remove the extension from the variable content (!)
and save back to the same variable ]
Question: How can i remove the extension of the filenames which are saved in the
variables _WRLNG, _WRx64, _WRx32 and save this filename (without extension)
back to the same variables ??
i need something like: set var=%var~n% but that doesn't work. And in a FOR loop i don't know
how to convert the name from the (...variable names ...) list to the corresponding variable
so i can apply a parameter extension like "~n", "~nf" etc. on it.
Can anyone help me ?
Thanks
#echo off
setlocal EnableDelayedExpansion
set _WRx32=wrar420.exe
set _WRx64=winrar-x64-420.exe
set _WRLNG=rarlng.rar
FOR %%f IN (_WRLNG, _WRx64, _WRx32) do (
FOR %%g in (!%%f!) do set "%%f=%%~ng"
)
Try this:
FOR /f "delims=." %%f IN ("%_WRx32%") do set "_WRx32=%%f"
FOR /f "delims=." %%f IN ("%_WRx64%") do set "_WRx64=%%f"
FOR /f "delims=." %%f IN ("%_WRLNG%") do set "_WRLNG=%%f"
I tried a similar version on the command line, and it appears you can't do multiple variable variables in a for loop (that I know of).
So, you have two options:
If you know that the variables will always have a 4 character extension (including the period), then you could use something like set _WRLNG=%_WRLNG:~0,-4%. This will take the last 4 characters from the variable.
You have to do one loop per variable, and you need to fix up the way you are doing them. The proper way would be for /f %%i in ("%_WRLNG%") do set _WRLNG=%~ni.
Or, look at Endoro's answer for a better for loop example.
EDIT: Thought of another way:
#echo off
for /f "delims=. tokens=1,2" %%i in ("%var%") do set var=%%i

whats the difference between: %%a and %variable% variables?

for /f "tokens=*" %%a in ('find /v ":" "%appdata%\gamelauncher\options.txt" ^| find "menu=a"') do ( set usemenu=a )
for /f "tokens=*" %%a in ('find /v ":" "%appdata%\gamelauncher\options.txt" ^| find "menu=b"') do ( set usemenu=b )
for /f "tokens=*" %%a in ('find /v ":" "%appdata%\gamelauncher\options.txt" ^| find "menu=c"') do ( set usemenu=c )
Right, in this code (which may not work, that what i'm trying to find out) we have this "%%a" in that 'for' command.
First, whats the difference between %variable% and %%a?
Second, can someone explain the 'for' command to me? I have Google'd it way too much and all the explanations seem way to complicated...
What I am trying to do is pull a variable from options.txt, so i can change the menu style of my game launcher. there are 3 styles (a, b and c), so if the options.txt reads "menu=a" how can i get it to set a variable like %usemenu% to the value of a?
Thanks for any help in advance!
%variable% are environment variables. They are set with set and can be accessed with %foo% or !foo! (with delayed expansion if enabled). %%a are special variables created by the for command to represent the current loop item or a token of a current line.
for is probably about the most complicated and powerful part of batch files. If you need loop, then in most cases for has you covered. help for has a summary.
You can
iterate over files: for %x in (*.txt) do ...
repeat something n times: for /l %x in (1, 1, 15) do... (the arguments are start, step and end)
iterate over a set of values: for %x in (a, b, c) do ...
iterate over the lines of a file: for /f %x in (foo.txt) do ...
tokenize lines of a file: for /f "tokens=2,3* delims=," %x in (foo.txt) do ...
iterate over the output of a command: for /f %x in ('somecommand.exe') do ...
That's just a short overview. It gets more complex but please read the help for that.
Variables of the form %%a (or %a if for is used outside of batch files) are very similar to arguments to batch files and subroutines (%1, %2, ...). Some kinds of expansions can be applied to them, for example to get just the file name and extension if the variable represents a file name with path you can use %%~nxa. A complete overview of those is given in help for.
On the other hand, environment variables have other kinds of special things. You can perform replacements in them via %foo:a=b% would result in %foo% except that every a is replaced by a b. Also you can take substrings: %foo:~4,2%. Descriptions of those things can be found in help set.
As to why %variables% and %%a are different things that's a bit hard to answer and probably just a historical oddity. As outlined above there is a third kind of variable, %1, etc. which are very similar to those used in for and have existed for longer, I guess. Since environment variables are a bit unwieldy to use in for due to blocks and thus heavy reliance on delayed expansion the decision probably was made to use the same mechanisms as for arguments instead of environment variables.
Also environment variables could be more expensive, given that the process has a special “environment” block of memory where they are stored in variable=value␀ pairs, so updating environment variables involves potentially copying around a bit of memory while the other kind of variables could be more lightweight. This is speculation, though.
As for your problem, you don't really need for here:
find /v ":" "%appdata%\gamelauncher\options.txt" | find "menu=a" && set usemenu=a
This will only run the set if the preceding command was successful, i.e. menu=a was found. This should be considerably easier than for. From what I read you're trying to look whether menu=a exists in a line that does not contain a colon and in that case usemenu should be set to a, right? (And likewise for b and c. You could try coaxing for into doing that by looping over the lines of the file or output and tokenizing appropriately to figure out the value of menu but depending on the format of the lines this can be tricky. If what you have there works in theory then you should simply stick to that. You can however use a loop around it to avoid having to repeat the same line three times for a, b and c:
for %%X in (a b c) do (
find /v ":" "%appdata%\gamelauncher\options.txt" | find "menu=%%X" && set usemenu=%%X
)
If the file you are parsing is simple, however, with just name=value pairs in each line where : foo would be a comment, then you could use for as well:
for /f "tokens=1,* eol=: delims==" %%A in (%appdata%\gamelauncher\options.txt) do (
if "%%A"=="menu" set usemenu=%%B
)
But that depends a little on the exact format of the file. Above snippet would now read the file line by line and for each line would discard everything after a colon (the eol=: option), use the equals sign as a token delimiter and capture two tokens: The part before the first = and everything after it. The tokens are named starting with %%A so the second one is implicitly %%B (again, this is explained in help for). Now, for each line we examine the first token and look whether it's menu and if so, assign its value to the usemenu variable. If you have a lot of possible options to support this is certainly easier to maintain :-).