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.
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.
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 ).
I'm not sure whether this is officially documented, but you can simulate delayed expansion using the call statement:
#echo off
FOR /R %%X IN (*.txt) DO (
set temp=%%~nX
call echo directory %%temp:~0,7%%
)
Doubling the percent signs defers the variable substitution to the second evaluation. However, delayed expansion is much more straightforward.
Related
I'm a novice at batch sripting. I've been using Robocopy in a batch file (that calls a PowerShell script) to do various tasks that have made work life much more efficient. Now I need to rename a folder with a different name and increment that different name by 1 for each time the batch script is executed. Spacing is important because I have software that will not read the path if it is underscored between the naming.
C:\Users\Desktop\Source\
What exists in this path is a folder sequence like this:
Folders Now
Folders Now Previous 1
Folders Now Previous 2
I've been trying to find a batch script that will rename Folders Now to Folders Now Previous 'next increment', but I've had no luck. I've tried various scripts that I've found and they work to a certain degree, but none of them do exactly this. Spacing between the naming is important (there can't be Folders_Now_Previous_3), and that's where the other scripts fail.
I've been using a batch script that will look at the highest last number and increment that name by 1. That works, but the naming parameter (source name and new name) aka set "baseName= are the same and they need to be different, and it also involves underscores and that's a no-go for the legacy software.
EDIT:
I've been using this script from an older post in this forum:
#echo off
setlocal enableDelayedExpansion
set "baseName=Folders_Now_Previous_"
set "n=0"
for /f "delims=" %%F in (
'2^>nul dir /b /ad "%baseName%*."^|findstr /xri "%baseName%[0-9]*"'
) do (
set "name=%%F"
set "name=!name:*%baseName%=!"
if !name! gtr !n! set "n=!name!"
)
set /a n+=1
md "%baseName%%n%"
When this script is executed, it returns this:
Folders_Now_Previous_1
Folders_Now_Previous_2
Folders_Now_Previous_3
What I've been trying to do is modify it so that the output doesn't contain underscores. It should output this:
Folders Now Previous 1
Folders Now Previous 2
Folders Now Previous 3
My attempts at modifying it will work the first time but it will stop working after the first folder is renamed.
I want to generate multiple files similar to the one which I have kept it in some particular location.
For example the file name is "MM_MTL_IM_20180726123109", only last two digits of the file name should keep on changing.
Can we achieve this with batch script or please suggest me any other technique
Here is a basic example script which is designed not for efficiency or to be the best method but to show some elementary batch file commands with their syntax in a logical progression.
#Echo Off
Set "FullFileName=MM_MTL_IM_20180726123109.ext"
If Not Exist "%FullFileName%" Exit /B
For %%A In ("%FullFileName%") Do (
Set "FileNameOnly=%%~nA"
Set "FileExtension=%%~xA"
)
Set "FileNameBarLastTwoChars=%FileNameOnly:~,-2%"
Set "LastTwoFileNameChars=%FileNameOnly:~-2%"
For /L %%A In (100,1,159) Do (
Set "Seconds=%%A"
If Not "%%A"=="1%LastTwoFileNameChars%" (
Call Copy "%FullFileName%" "%FileNameBarLastTwoChars%%%Seconds:~-2%%%FileExtension%"
)
)
Pause
You only requested 'multiple' files with no explanation, so you will need to make adjustments to the code if you don't wish this script to copy your file 59 times so that you have sixty matching files with different names in the same directory, each having valid seconds.Your question also states 'generate' not copy, so you will have to adjust the code if the copy assumption was incorrect.
#echo off
setlocal
set "name=%~1"
set "stem=%name:~,-2%"
for /L %%i in (10,1,59) do copy %name% %stem%%%i
This batchfile takes the original filename as parameter and creates 49 copies of it, appending "10" to "59" instead of the last 2 characters.
In this simple form this will work only if original file and copies are in the same, the current, directory.
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.
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.
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 ).
I'm not sure whether this is officially documented, but you can simulate delayed expansion using the call statement:
#echo off
FOR /R %%X IN (*.txt) DO (
set temp=%%~nX
call echo directory %%temp:~0,7%%
)
Doubling the percent signs defers the variable substitution to the second evaluation. However, delayed expansion is much more straightforward.
Our current build system is a mass of batch files that are called from a "master" batch file. How can I find all the batch files that are called from the "master" batch file?
Unfortunately the repository also contains a lot of other batch files and I cannot just list all the batch files in the working copy.
I don't care much about "simulating" a real execution. It would be fine if I got a list of all the files potentially called from the master batch file.
There are 299 files, so I'd rather not add an echo "" to each one of them.
Referenced batch files are called using "call xyz.bat", i.e. relative paths. Sometimes the batch files change the current working directory though, like so:
cd client
call mk.bat
cd ..
or
pushd client\install
call prepare.bat
popd
EDIT: added examples
Yuck - this problem is nearly impossible to solve perfectly for several reasons:
1) Batch files may be "called" by multiple mechanisms:
CALL scriptName
pipes, for example: scriptName | findstr ...
FOR /F, for example: for /f ... in ('scriptName ...') do ...
CMD, for example: cmd /c scriptName ... or %comspec% /c scriptName
START, for example start "" scriptName ...
2) Any of the above constructs may be present without being a "call" to a batch script. For example, CALL can be used to call a label, or to execute a command. FOR /F could be used to parse a string. etc.
3) No matter which "call" mechanism is used, the "call" target may be represented by a variable instead of the string literal. For example:
set "myScript=ScriptName"
REM the SET may not be anywhere near the CALL
call %myScript%
4) The script name and path might not appear in the code. It could be dynamically read from the file system, or derived via logic.
5) The actual call itself may be embedded as a value in a variable. This is true for any call mechanism. For example:
set "myCommand=CALL"
%myCommand% ScriptName
6) As you have noted in your question, the path to the script may be relative, and the script may change the current directory. Or the "call" may be relying on the PATH environment variable.
7) Any "called" script could itself "call" another script.
You could use FINDSTR to look for any of the call mechanisms, and you will likely find most of the "calls". But there will likely be many false positives. You could add the /N switch to prefix each matching line with the line number. Then you would need to check each matching line manually in your text editor to see if it is a "call" that you are interested in.
findstr /nir /c:"\<call\>" /c:"|" /c:"for */f " /c:"\<cmd\>" /c:"\<%comspec%\>" /c:"\<start\>" *.bat
There may be so many false positives that you might be better off manually tracing the logic of the entire script :-( This is especially true since there is no guarantee that FINDSTR will find all "calls", since the call itself could be masked behind a variable.
You could prepend logging to each of the batch files. I know you said you'd rather not, but it's not as hard as you might think.
#echo off
:: addlogging.bat patch|unpatch
::
:: addlogging.bat patch
:: finds every .bat file in the current directory and every subdir
:: beyond, and patches each, prepending an echo to a log file
::
:: addlogging.bat unpatch
:: finds every .bat file in the current directory and every subdir
:: beyond, and removes the logging patch
setlocal
if #%1==#patch goto next
if #%1==#unpatch goto next
goto usage
:next
for /f "delims=" %%I in ('dir /s /b *.bat') do (
if not "%%I"=="%~f0" (
if #%1==#patch (
set /p I="Patching %%I... "<NUL
echo #echo %%time%% %%~f0 %%* ^>^> "%%userprofile%%\Desktop\%%date:/=-%%.log">"%%~dpnI.tmp"
) else (
set /p I="Unpatching %%I... "<NUL
)
findstr /v "^#echo.*time.*~f0.*>>" "%%I">>"%%~dpnI.tmp"
move /y "%%~dpnI.tmp" "%%I">NUL
echo Done.
)
)
goto :EOF
:usage
echo usage: %~nx0 patch^|unpatch
I'm trying to create a batch file (via Windows XP Pro) that copies 2 files (.zpl) who's filename's vary in length. ZPL files relate to label printer code. File names are as follows:
FillXferDataPBHAMFill###########.zpl
FillFormatsPBHAMFill############.zpl
The pound signs represent a number associated with a particular label/job to be printed. These numbers are identical per job. From one job to the next the numbers vary in length and always change. The directory I'm trying to pull these from contains ZPL files from multiple locations, however, I just want the BHAM ones.
The batch will copy from: \Server\C:\Directory1\Directory2\Directory3
To be copied to: \Server\Directory1\Directory2
Not sure if this will complicate things further, but the batch file will be ran from a 3rd machine. Furthermore, I do not need to copy every file everytime. Whenever new print jobs are sent, supervisors will run the batch to copy the new print jobs within the last X amount of time. X being minutes. Here is what I have so far...
#echo off
SETLOCAL enableExtensions enableDelayedExpansion
SET sourceDir=Server\C:\Directory1\Directory2\Directory3
SET targetDir=Server\Directory1\Directory2
FOR %%a (FillFormatsPBHAM*.bat) DO (
SET "filename=%%a"
SET "folder=%targetDir%"
XCOPY "%%a" !folder!
)
FOR %%b (FillXferDataPBHAM*.bat) DO (
SET "filename=%%b"
SET "folder=%targetDir%"
XCOPY "%%b" !folder!
)
:END
I apologize for a lengthy post; just wanting to be as thorough as possible. I'm learning this on the fly so bare with any ignorance on my part. Thank you in advance for ANY help!!
StackOverFlow Material Reviewed: Reference1, Reference2 -- I've been looking everywhere over the past week and these were the 2 most helpful so far.
I see some ways to fix or improve your BAT script.
The FOR command syntax is FOR %%a IN (*.bat) DO (
The sourcedir variable is set Server\C:\Directory1\Directory2\Directory3, which is not a correct path in Windows.
you initialize but don't use %sourcedir% variabe neither in your FOR loop nor in your copy commnand
you should either change the current drive and dir with a pushd %sourcedir% command, or specifying it in the FOR command.
your FOR loop assigns a %filename% variable that is never used, you may skip this assignment.
you FOR loop assigns a %folder% variable that is only then used in the copy command, you can skip this assignment and simply use %targetdir%
but, to just copy all files from one folder to the other you don't need FOR to iterate over all of them, you might just copy them right.
So, take a look at this simple script to get you started..
SET sourceDir=\\servername\sharename\Directory1\Directory2\Directory3
SET targetDir=\\anotherserver\sharename\Directory1\Directory2
xcopy %sourceDir%\FillFormatsPBHAM*.bat %targetDir%
xcopy %sourceDir%\FillXferDataPBHAM*.bat %targetDir%