Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

windows batch files: setting variable in for loop

I have a number of files with the same naming scheme. As a sample, four files are called "num_001_001.txt", "num_002_001.txt", "num_002_002.txt", "num_002_003.txt"

The first set of numbers represents which "package" it's from, and the second set of numbers is simply used to distinguish them from one another. So in this example we have one file in package 001, and three files in package 002.

I am writing a windows vista batch command to take all of the files and move them into their own directories, where each directory represents a different package. So I want to move all the files for package 001 into directory "001" and all for 002 into directory "002"

I have successfully written a script that will iterate over all of the txt files and echo them. I have also written a scrip that will move one file into another location, as well as creating the directory if it doesn't exist.

Now I figure that I will need to use substrings, so I used the %var:~start,end% syntax to get them. As a test, I wrote this to verify that I can actually extract the substring and create a directory conditionally

 @echo off set temp=num_001_001.txt NOT IF exist %temp:~0,7%\   mkdir %temp:~0,7% 

And it works. Great.
So then I added the for loop to it.

@echo off FOR /R %%X IN (*.txt) DO (   set temp=%%~nX   echo directory %temp:~0,7% ) 

But this is my output:

 directory num_002 directory num_002 directory num_002 directory num_002 

What's wrong? Does vista not support re-assigning variables in each iteration? The four files are in my directory, and one of them should create num_001. I put in different files with 003 004 005 and all of it was the last package's name. I'm guessing something's wrong with how I'm setting things.

I have different workarounds to get the job done but I'm baffled why such a simple concept wouldn't work.

like image 379
MxLDevs Avatar asked Apr 10 '11 22:04

MxLDevs


People also ask

Can you use variables in batch files?

When creating batch files, you can use set to create variables, and then use them in the same way that you would use the numbered variables %0 through %9. You can also use the variables %0 through %9 as input for set. If you call a variable value from a batch file, enclose the value with percent signs (%).

How do I loop a batch file in Windows?

Pressing "y" would use the goto command and go back to start and rerun the batch file. Pressing any other key would exit the batch file.

Why is %% used in batch file?

Use double percent signs ( %% ) to carry out the for command within a batch file. Variables are case sensitive, and they must be represented with an alphabetical value such as %a, %b, or %c. Required. Specifies one or more files, directories, or text strings, or a range of values on which to run the command.


2 Answers

Your problem is that the variable get replaced when the batch processor reads the for command, before it is executed.

Try this:

SET temp=Hello, world! CALL yourbatchfile.bat 

And you'll see Hello printed 5 times.

The solution is delayed expansion; you need to first enable it, and then use !temp! instead of %temp%:

@echo off SETLOCAL ENABLEDELAYEDEXPANSION FOR /R %%X IN (*.txt) DO (   set temp=%%~nX   echo directory !temp:~0,7! ) 

See here for more details.

like image 106
configurator Avatar answered Sep 29 '22 02:09

configurator


Another solution is to move the body of the for loop to a subroutine and call it.

@echo off FOR /R %%X IN (*.txt) DO call :body %%X goto :eof  :body set temp=%~n1 echo directory %temp:~0,7% goto :eof 

Why do this? One reason is that the Windows command processor is greedy about parentheses, and the results may be surprising. I usually run into this when dereferencing variables that contain C:\Program Files (x86).

If the Windows command processor was less greedy, the following code would either print One (1) Two (2) or nothing at all:

@echo off if "%1" == "yes" (    echo 1 (One)    echo 2 (Two) ) 

However, that's not what it does. Either it prints 1 (One 2 (Two), which missing a ), or it prints 2 (Two). The command processor interprets the ) after One as the end of the if statement's body, treats the second echo as if it's outside the if statement, and ignores the final ).

like image 23
bk1e Avatar answered Sep 29 '22 03:09

bk1e