Looping through string values using set in Windows cmd - string

My Windows batch file is supposed to read file names and create a directory that is named according to the filename's 2nd to 5th letter:
for %%f in (.\*.txt) do (
set string=%%~nf
mkdir %string:~2,5%
)
The value of 'string' is not updated though, i.e. it is the same in each step of the loop. How can I have it updated?
This is the cmd output:
>for %f in (.\*.txt) do (
set string=%~nf
mkdir le3
)
>(
set string=file1
mkdir le3
)
>(
set string=file2
mkdir le3
)
A subdirectory or file le3 already exists.
>(
set string=file3
mkdir le3
)
A subdirectory or file le3 already exists.

The problem you're running into is because of %variable% expansion. The entire FOR statement (including up to the closing parenthesis) is one statement. When it is read, the command processor replaces the %string% variable with whatever string is set to before the SET inside executes. (Actually, you're getting the subset of string starting at the 2nd character and proceeding 5 characters, but that's not really important here. My guess is that you've got %string% set to "file3".)
Anyway, you can fix it by adding SETLOCAL ENABLEDELAYEDEXPANSION at the top and using syntax like !string! instead of %string% inside the FOR.
You could also move the processing of %string% outside of FOR.
Look up HELP SETLOCAL and HELP SET for more.
Also, if you hadn't noticed, you're not getting the second and fifth characters of the filename... As I mentioned, you're getting a substring of (up to) length 5, starting at character 2. Look up HELP SET for more.

Nearly, you've got a couple of unnecessary exclamation marks in your last attempt:
SETLOCAL ENABLEDELAYEDEXPANSION
for %%f in (*.txt) do (
set string=%%~nf
if not exist "!string:~2,5!\" mkdir "!string:~2,5!"
)

Related

CMD Script - cannot set variable within FIND

I use an external program to generate a string
I need to find out if the string has the word "not" in it
If so, I set a certain variable to use later.
1 and 2 work without problems, but I cannot get 3 to work.
My code:
SET EXE="path\to\program.exe"
for /f "delims=" %%i in ('%EXE% argument') do (
rem echo %%i
set mystatus=%%i
)
echo %mystatus%
^ works up to here ^ - echo returns the correct value. Now I want to return a simpler value that tells me if the string "not" was present in %mystatus% var
set "isNot="
ECHO.%mystatus%| FIND /I "not">Nul && (
Echo.Found "not"
do something
rem ^this works^
set isNot= "true"
) || (
Echo.Did not find "not"
do something
rem ^this works^
set isNot= "false"
)
now, out of FIND, this returns nothing: WHY?
echo %isNot%
I find your answer - let's say, naïve.
First, I can't reproduce your result. It worked perfectly for me, returning isnot set to Space"false" or Space"true", not nothing.
Had isnot been undefined, then the echo response should have been Echo is off, so isnot was defined in your test. I would use echo +%isNot%+ for this test so that the string is shown with an obvious delimiter. If it contained just spaces - well, they tend to be remarkably invisible.
As to the solution, I's suggest
set "isnot="
if "%mystatus%" neq "%mystatus:not=%" set "isnot=Y"
which avoids find and piping and conditions, setting isnot to be either defined or not defined as appropriate, ready for testing using
if defined isnot (echo not was not found) else (echo not was found)
The reason for this is that if defined works on the run-time status of the variable, hence it can be used within a code block where the variable may be switched from one status to the other.
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign a terminal \, Space or " - build pathnames from the elements - counterintuitively, it is likely to make the process easier.
Idiotic mistake, it seems.
There should not be spaces after = when setting a variable.
Removed the spaces, and it work as it should.
This may be a guess as to your intended task, but my assumption is that you are trying to determine if the executable command returns the string, 'path\to\program.exe' is not recognized as an internal or external command, operable program or batch file..
If that is the case, then perhaps this would be a better overall solution for you:
#Set "exe=path\to\program.exe"
#For %%G In ("%exe%")Do #If "%%~aG" Lss "-" (Echo ERROR: No such object '%exe%')Else If "%%~aG" GEq "d" (Echo ERROR: '%exe%' is a directory object.)Else %%G argument
All you would need to change is the path on line 1, and the string argument at the end of line 2.

Catenating Variables in bat file

I've created bat file named vk.bat . The code is as following :-
SET "tcs="
FOR %%A IN (%*) DO (
Set "tcs=%tcs% -t %%A"
)
Echo %tcs%
I am executing this bat from cmd as following :-
c:\vk.bat Apple Cat Play
I want the final string as " -t Apple -t Cat -t Play"
But I am getting final string as "-t Play" . I am not able to find out that why and how it's overwriting the previous contents of string tcs.
DelayedExpansion-free Solution
Using the call command, we can emulate the delayed expansion. Take a look at this example
call command %%foo%% = setlocal enableDelayedExpansion
command !foo!
According to your case, the code should be changed to this:
SET "tcs="
FOR %%G IN (%*) DO (
CALL SET "tcs=%%tcs%% -t %%G"
)
ECHO %tcs%
Some Buggy Behaviour With CALL
Call doesn't handle redirection characters and some special characters properly:
&
|
<
>
"foo^bar"
The redirection of the first 4 examples won't function as intended, while in the last example, the caret(^) will be doubled. Source: SS64
You have fallen into the delayed expansion trap, like Squashman mentioned.
What Is Delayed Expansion?
In earlier batch files, variables are expanded(changed to it's value) when each line is phrased.
The command processor treats the entire for loop as one command, so the variable %tcs% is treated as nothing. (Because tcs was previously set to nothing.
How To Make Batch Variables Get Expanded At Run-Time?
To preserve compatibility with older batch files, delayed expansion feature is added. To allow the processor to do so, add this line to the start of the batch file:
setlocal enableDelayedExpansion
and we also need to tell the processor which variables to be expanded at run-time, to do so, change the following:
From -> To
%var% -> !var!
Note only %n% variables can be changed to !n!. Metavariables like %%G/%G and %0 cannot be changed.
Here's is the fixed code using delayed expansion.:
SETLOCAL EnableDelayedExpansion
SET "tcs="
FOR %%G IN (%*) DO (
SET "tcs=!tcs! -t %%G"
)
ECHO %tcs%

Get base name of file without file extension

Let's say I'd have a file named "testfile.txt" set on a variable:%File% and I'd like to remove the extension when echoing it . Echo %File:~0,8% would come out as "testfile" but what I want to do is to have it display anything and everything to the left of the ".txt" because I won't always make files which have 8 characters in their name.
Is there a simple solution to this ?
Yep.
for %%I in ("testfile.txt") do echo %%~nI
or
for %%I in ("%file%") do echo %%~nI
Do help for in a cmd console window and see the last two pages for more information on tilde operations.
There is another way to accomplish what you want, using substring substitution similar to your attempts illustrated in your question.
set "file=testfile.txt"
echo %file:.=&rem;%
That substitutes the dot with &rem;. When the variable is evaluated, the batch interpreter treats the newly substituted data as a compound command. And since everything following rem is treated as a remark to be ignored, you're left with only testfile as the output. This will only work if:
you don't include quotation marks in your variable value
your filename only includes the one dot
you don't do it within a parenthetical code block (if statement or for loop) where delayed expansion is required
You can try this:
#echo off
set "file=testfile.txt"
call :removeExtension "%file%"
echo %newFile%
pause
goto :eof
:removeExtension
set "newFile=%~n1"
goto :eof
However, this only works if the file has no path. If it does, you can do this:
#echo off
setlocal EnableDelayedExpansion
set "file=files\testfile.txt"
call :removeExtension "%file%"
echo %newFile%
pause
goto :eof
:removeExtension
set "file=%~1"
set "newFile=!file:%~x1=!"
goto :eof

Batch file string manipulation

This is a very specific question, however;
Say I have a batch file running from\located in the directory c:\data\src\branch_1
How do I set my environment variable %buildpath% to c:\build\bin\branch_1 in a batch file?
(To be extra clear, if the same batch file is located in c:\foo\bar\branch_2 I want it to set %buildpath% to c:\build\bin\branch_2)
You should be able to use the environment variable %~dp0 to get you the drive and path of the batch file currently running. From there, it's a not-very-efficient method of stripping off the end of that string character by character and building a new string.
For example, the batch file:
#setlocal enableextensions enabledelayedexpansion
#echo off
set olddir=%~dp0
echo Current directory is: !olddir!
if "!olddir:~-1!" == "\" (
set olddir=!olddir:~0,-1!
)
set lastbit=
:loop
if not "!olddir:~-1!" == "\" (
set lastbit=!olddir:~-1!!lastbit!
set olddir=!olddir:~0,-1!
goto :loop
)
set newdir=c:\build\bin\!lastbit!
echo New directory is: !newdir!
endlocal
running as c:\data\src\branch1\qq.cmd returns the following:
Current directory is: C:\data\src\branch1\
New directory is: c:\build\bin\branch1
As to how it works, you can use !xyz:~n,m! for doing a substring of an environment variable, and negative m or n means from the end rather than the beginning. So the first if block strips off the trailing \ if it's there.
The loop is similar but it transfers characters from the end of the path to a new variable, up until the point where you find the \. So then you have the last bit of the path, and it's a simple matter to append that to your fixed new path.
Old case, but still...
easy way of setting current directory into variable.
#echo off
cd > dir.tmp
set /p directory= <dir.tmp
echo %directory% <-- do whatever you want to the variable. I just did a echo..
del dir.tmp > nul

String processing using Batch Script

I'm currently creating a batch script that has to loop through the lines in a file, checking for some string, and if theres a match prefix that string with a '#' (comment it out).
I'm perfectly new to batch script, all I got this far is:
for /f %%j in (CMakeLists.txt) do (
if "%%j"=="Extensions_AntTweakBar" (
echo lol1
)
if "%%j"=="Extensions_Inspection" (
echo lol2
)
if "%%j"=="Extensions_InspectionBar" (
echo lol3
)
)
So my current issue is, I don't know how to operate on string within batch scripts. If someone could help me out that would be appreciated :)
You can just use the text you want to append followed by your variable generally.
C:\>set MY_VAR=Hello world!
C:\>echo #%MY_VAR%
#Hello world!
C:\>set MY_VAR=#%MY_VAR%
C:\>echo %MY_VAR%
#Hello world!
If you're just doing echo, that's fine. echo #%%j will do what you need.
But if you want to set the line to a variable, you have to enable delayed expansion. Add setlocal ENABLEDELAYEDEXPANSION to the top of your file and then surround your variables with ! instead of %. For example (and notice that I've added delims= to put the entire line in %%j instead of the first word on the line):
#echo off
setlocal ENABLEDELAYEDEXPANSION
set LINE=
for /f "delims=" %%j in (CMakeLists.txt) do (
set LINE=%%j
if "%%j"=="Extensions AntTweakBar" (
set LINE=#%%j
)
if "%%j"=="Extensions Inspection" (
set LINE=#%%j
)
if "%%j"=="Extensions InspectionBar" (
set LINE=#%%j
)
echo !LINE!
)
Given this input file:
Extensions AntTweakBar
some text
Extensions Inspection
Extensions What?
some more text
Extensions InspectionBar
Extensions InspectionBar this line doesn't match because delims= takes all text
even more text
The above script produces this output:
C:\>comment.bat
#Extensions AntTweakBar
some text
#Extensions Inspection
Extensions What?
some more text
#Extensions InspectionBar
Extensions InspectionBar this line doesn't match because delims= takes all text
even more text
And of course removing #echo off will help you debug problems.
But all that being said, you're about at the limit of what you can accomplish with batch string processing. If you still want to use batch commands, you may need to start writing lines to temporary files and using findstr with a regex.
Without a better understanding of what you want inside your loop or what your CMakeLists.txt file looks like, try this on for starters:
FINDSTR "SOMETHING" %%J && ECHO #%%J || ECHO %%J
The && makes the second command (the ECHO) conditional on the first command exiting without an error state, and the || is like a logical OR and it runs when the first one doesn't.
Really, for modifying the internals of a text file you are probably going to be much better off using either sed or awk - win32 binaries can be found in the UnxUtils project.

Resources