Batch Create Several Folders Based on Multiple Filenames, and Move Multiple Related Files to The Created Folders - windows

Every week, one of my co-workers has had to go through a folder with hundreds of demuxed video and audio files, rename each one individually for a specific city TV station and then sort them into folders based on the name of the city. I've created a .bat file to rename them all for him, and now I'd like to create a .bat file that creates new directories based on the filenames, and places the corresponding files into the new folders. I copied a few of the files to test with.
So the end result will be a "Houston" folder with all it's corresponding files, a "Compton" folder with it's files, a "Moline" folder, etc, etc... for every city, up to around 200 cities, and we're only getting more.
He's currently searching "Houston", cutting all the files that come up, creating a new folder manually, naming it "Houston" and pasting all the files into his new folder. FOR EVERY CITY. 200 TIMES. And it takes hours.
The files are ALWAYS named with this system: X### Random City, ST
With my little wee programming knowledge, I'm supposing that the script could detect all the characters after the first space, and before the comma, copy those characters (Random City), create a new folder, name it the copied characters (Random City) then move any files containing "Random City" in their filename into the newly created folder. The end result would be as such, just with a lot more folders.
Is there anyone more advanced than me who could explain the best way to to this?
I apologize in advance if I'm in the wrong place or not savvy enough. Cheers!
UPDATE: I messed around, learned about tokens and delimiters, variables etc. Here is what I have which works amazingly, except I'm not sure how to remove the comma at the end of the city name. I'm using space as the delimiter, which makes the text chunks the tokens if I understand correctly, including my comma, using tokens=2. Another problem that arises; Say there's a city with two text chunks (tokens) eg. San Fransisco, Baton Rouge. How could I grab both of them, using the comma as my stopping point? My code is below.
#echo off
setlocal enabledelayedexpansion
for %%A in (*.m2v *.mpa) do (
echo file found %%A
for /f "delims=" %%B in ("%%A") do set fname=%%~nB
for /f "delims=" %%C in ("%%A") do set fextn=%%~xC
for /f "tokens=2* delims= " %%D in ("!fname!") do set folname=%%D
echo folder name !folname!
if not exist "!folname!" (
echo Folder !folname! doesn't exist, creating
md "!folname!"
) else (
echo Folder !folname! exists
)
echo Moving file %%A to folder !folname!
move "%%A" "!folname!"
)
echo Finished
pause
UPDATE 2: I found a meh workaround to get rid of the comma, by adding it as a delimiter, but I'm still trying to wrap my head around the 2 word cities. My Baton Rouge and San Fransisco folders are being named respectively, "Baton" and "San". Here is my code so far, I'll update if I find a better way.
setlocal enabledelayedexpansion
for %%A in (*.m2v *.mpa) do (
echo file found %%A
for /f "delims=" %%B in ("%%A") do set fname=%%~nB
for /f "delims=" %%C in ("%%A") do set fextn=%%~xC
for /f "delims=," %%B in ("%%A") do set fname=%%~nB
for /f "tokens=2* delims= " %%D in ("!fname!") do set folname=%%D
echo folder name !folname!
if not exist "!folname!" (
echo Folder !folname! doesn't exist, creating
md "!folname!"
) else (
echo Folder !folname! exists
)
echo Moving file %%A to folder !folname!
move "%%A" "!folname!"
)
echo Finished
pause
UPDATE 3
Here is my code which worked. However, if the number of characters in your filename prefixes/suffixes changes, it will screw things up and you'll have to edit code.
#ECHO OFF
SETLOCAL enabledelayedexpansion
FOR %%A in (*.m2v *.mpa) do (
ECHO file found %%A
FOR /F "delims=" %%B in ("%%A") do set fname=%%~nB
SET folname=!fname:~5,-4!
ECHO folder name !folname!
if not exist "!folname!" (
ECHO Folder !folname! doesn't exist, creating
MD "!folname!"
) else (
ECHO Folder !folname! exists
)
ECHO Moving file %%A to folder !folname!
MOVE "%%A" "!folname!"
)
ECHO Finished
PAUSE
Using the SET folname=!fname:~5,-4!allows me to trim the M373 prefix, 5 characters in, and the , TX suffix, 4 characters in, removing the comma and salvaging the city name, regardless of how long it is, or how many words it is (eg. West Palm Beach, FL) . Antares mentioned this solution in his answer which worked like a charm.
BUT IT ALSO MADE ME THINK
If the number of characters in the prefix changes, which is likely, I'll either have to edit the batch file every time, or create a specific batch file for each circumstance. Not terrible, but not great either. So I went with Michael Heath's answer which works flawlessly. I'm not smart enough yet to know exactly why, but I'm gonna dissect it and find out. I have a lot of learning to do. Thanks, everyone!

#echo off
setlocal
rem A=Fullpath, B=Name before comma, C=B prefix, D=B without prefix.
for /f "delims=" %%A in ('dir /b *.m2v *.mpa') do (
for /f "delims=," %%B in ("%%~nA") do (
for /f "tokens=1,*" %%C in ("%%~B") do (
if not exist "%%~D\" (
echo Folder "%%~D" doesn't exist, creating
md "%%~D"
)
if exist "%%~D\" (
echo Moving file "%%~A" to folder "%%~D\"
move /y "%%~A" "%%~D\"
) else echo Folder "%%~D\" doesn't exist
)
)
)
echo Finished
pause
3 for loops to get the tokens needed.
1st will get the fullpath.
2nd to get the name before the comma.
3rd to get the name without the prefix.
In the nested for loops check if folder exists, create it if not. Then if folder exists, move file inside.

Here is my code which worked. However, if the number of characters in your filename prefixes/suffixes changes, it will screw things up and you'll have to edit code.
#ECHO OFF
SETLOCAL enabledelayedexpansion
FOR %%A in (*.m2v *.mpa) do (
ECHO file found %%A
FOR /F "delims=" %%B in ("%%A") do set fname=%%~nB
SET folname=!fname:~5,-4!
ECHO folder name !folname!
if not exist "!folname!" (
ECHO Folder !folname! doesn't exist, creating
MD "!folname!"
) else (
ECHO Folder !folname! exists
)
ECHO Moving file %%A to folder !folname!
MOVE "%%A" "!folname!"
)
ECHO Finished
PAUSE
Using the SET folname=!fname:~5,-4!allows me to trim the M373 prefix, 5 characters in, and the , TX suffix, 4 characters in, removing the comma and salvaging the city name, regardless of how long it is, or how many words it is (eg. West Palm Beach, FL) . Antares mentioned this solution in his answer which worked like a charm.
BUT IT ALSO MADE ME THINK
If the number of characters in the prefix changes, which is likely, I'll either have to edit the batch file every time, or create a specific batch file for each circumstance. Not terrible, but not great either. So I went with Michael Heath's answer which works flawlessly. I'm not smart enough yet to know exactly why, but I'm gonna dissect it and find out. I have a lot of learning to do. Thanks, everyone!
Before Image
After Image

I can give you some hints. If you have a more specific problem case, feel free to update your question again.
You can cut the last X characters of a String like this: %variablename:~0,-X%
If you know the variables with the city parts, e.g. %%D and %%E or something, you can concatenate them again like this md "%%D %%E". However, this works just for a fixed number of tokens, like the two here.
You can store this concatenations in an own variable, if you need the result outside of your for-loop. Use set myVariable=%%D %%E for example, and show it with %myVariable% or !myVariable! (when delayed expansion is needed), for example md "%myVariable%".
A nifty workaround: if there are only a small number of "special cities" to take into consideration, then you could just add some rename commands at the end of your script, like rename San "San Francisco", rename Baton "Baton Rouge", etc. Will not work well, if there are more "San" cities (e.g. "San Bernadino"), because this cannot be distinguished anymore. But in this case, the copying into separate folders would already fail as well.
In your script you make a check for an existing folder. I think you can omit that. md or mkdir either create that directory or do nothing if it exists. Well, they do print a message to the console, which can be ignored. If you do not want to see them, redirect the error message stream to nul like this md myFolder 2>nul. This will swallow any error messages, but it is unlikely that you get any other error message than that in your scenario.
You could simplify your approach like this: I reckon your file renaming works well. Your "copy" script could be just a list of commands which are stated explicitly (and also could be edited fairly quickly if new cities are to be considered).
Set the batch file up like this with entries for each city:
#echo off
mkdir "Moline"
copy "*Moline*.*" "Moline"
mkdir "San Francisco"
copy "*San Francisco*.*" "San Francisco"
...
echo done.
Side effect is, that the folders for each city will be created, not just those where files are copied into. May be it suits your needs anyhow.
Also, I would like to give you pointers to sources of help/documentation:
On the command line you can get extensive help by executing the commands used in your batch file and appending /?. For example set /? gives you a lot of useful things you can do with variables/String manipulations.
Try: for /?, if /? (regarding errorlevel for example), maybe call /?, goto /?, and others.
A very good source for all the command line commands is https://www.ss64.com. This site provides extensive help even for PowerShell and Linux Bash and others. Relevant to you in this case would be CMD, direct link: https://ss64.com/nt/
Edit/Update:
Check out symbol replacement on the set command to build something like
if "%myVariable:~0,1%" == "M" (set myvariable=%myvariable:~1%)
The first part "cuts" the first character and checks, if it is an M and if so, it keeps everything except the first character. With this you could make your filenames even out to process them inside your batch file.
You can also "remove" a letter or substring with %myVariable:, TX=% which would replace any ", TX" occurance with "nothing" for example.
Oh, this could also help to remove any spaces in the filename. Then you could extract the "SanFrancisco" without a spaces problem ;) The folder name would be without space though. This could be resolved with further rename commands at the end.

Here's an alternative method, just for the sake of variety:
#Echo Off
SetLocal DisableDelayedExpansion
Set "SourceDir=.\Batch Rename\BACKUP"
If Exist "%SourceDir%\" For /F "EOL=|Delims=" %%G In (
'%__AppDir__%where.exe "%SourceDir%":"*, ??.m??" 2^>NUL'
)Do (Set "FileBaseName=%%~nG"&SetLocal EnableDelayedExpansion
For /F "EOL=|Delims=," %%H In ("!FileBaseName:* =!")Do (EndLocal
%__AppDir__%Robocopy.exe "%%~dpG." "%%~dpG%%H" "%%~nxG" /Mov>NUL))
This method filters your files with the where command. It selects for moving, only file names which end with a comma, followed by a space, followed by two letters, followed by a three letter extension beginning with the character m. It moves the files, automatically creating the destination directories if they do not exist, using the robocopy command. It uses only two for loops, the second of which, isolates the string between the first space and the next comma.
I have made it so that the script can be located anywhere, not necessarily in the directory with the files. This location is set on line 3 of the script, If you wish to modify it, please ensure that your location remains between the = and the closing double-quote, ", and does not end with a trailing back-slash, \. It is currently set to a relative directory, (based upon that visible in your screen-shot), but you could obviously use an absolute path too, e.g. Set "SourceDir=C:\Users\UserName\Videos". If you wish to keep the script in the same directory as the files to be moved, change it to read Set "SourceDir=." and just double-click it to run.

When I had to develop a script for the task at hand I would probably implement a few safety features in order to not move wrong files. Your sample data show pairs of .m2v and .mpa files, but I would likely not consider that as granted. Also would I not rely on a fixed-length prefix. Finally, I would perhaps also account for lit's comment.
So here is my attempt (see all the explanatory rem-remarks in the code):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (root directory containing the files to be processed)
set "_MASK=M??? *, ??.m2v" & rem // (mask to find the files to be processed)
set _EXTS=".m2v" ".mpa" & rem /* (list of extensions that must all be present;
rem extensions are not checked if this is empty;
rem `_MASK` should then be changed to end with `.m*`) */
set "_FILT=^M[0-9][0-9][0-9] [^,][^,]*, [A-Z][A-Z]\.[^\.][^\.]*$"
rem // (additional filter to find files; deactivate by `.*`)
set "_SEPS=," & rem /* (defines (a) separator character(s) to derive the
rem sub-directory; leave it blank to use full name) */
rem // Change into the root directory:
pushd "%_ROOT%" && (
rem /* Loop through all files matching the mask as well as the additional filter;
rem if the post-filtering is not needed, remove `^|` and everything behind: */
for /F "delims= eol=|" %%F in ('
dir /B /A:-D-H-S "%_MASK%" ^| findstr /I /R /C:"%_FILT%"
') do (
rem // Get the portion in front of the `,` of the file name:
for /F "delims=%_SEPS% eol=|" %%G in ("%%~nF") do (
rem // Split that portion at the first space to get the city name:
for /F "tokens=1* eol=|" %%H in ("%%G") do (
rem // initialise flag that indicates whether to move the current file:
set "FLAG=#"
rem // Skip the following checks if there are no extensions defined:
if defined _EXTS (
rem // Loop through the extensions in the list:
for %%E in (%_EXTS%) do (
rem /* Reset flag if file with current name and iterated extension
rem cannot be found; this ensures that files with all listed
rem extensions do exist, otherwise no files are moved: */
if not exist "%%~nF%%~E" set "FLAG="
rem /* Reset flag if file with current name and iterated extension
rem is actually a directory (though this is very unlikely): */
rem if exist "%%~nF%%~E\*" set "FLAG="
rem /* Reset flag if file with current name and iterated extension
rem is already located in the target sub-directory: */
if exist "%%I\%%~nF%%~E" set "FLAG="
)
)
rem // Do the following steps only if the flag has not been reset:
if defined FLAG (
rem // Create target sub-directory (suppress potential error message):
2> nul md "%%I"
rem // Check if there are dedicated extensions defined:
if defined _EXTS (
rem // Loop through the extensions in the list again:
for %%E in (%_EXTS%) do (
rem /* Move file with current name and iterated extension;
rem nothing is overwritten due to the preceding checks: */
> nul move "%%~nF%%~E" "%%I\%%~nF%%~E"
)
) else (
rem /* Empty list of extensions, hence just move the current file;
rem if you do want to overwrite, remove the `if exist´ part: */
if not exist "%%I\%%F" > nul move /Y "%%F" "%%I\%%F"
)
)
)
)
)
rem // Return from root directory:
popd
)
endlocal
exit /B
The values in the Define constants here: section at the top of the script are defined to suit your sample data, but they can easily be adapted there to configure the script at one place:
_ROOT: points to the directory where your input files are; %~dp0. points to the parent directory of the script, but you may of course specify any other absolute directory path here;
_MASK: is a file pattern that matches one file per pair (only .m2v files, others are covered by _EXTS); M??? matches the four-character prefix, but you can change it to M?*, for instance, to also match prefixes like M1 or M9999; if you do so, however, also edit _FILT accordingly;
_EXTS: defines a list of extensions that all must be present; that means for a certain base file name (like M372 Houston, TX, there must exist a file per each given extension, hence M372 Houston, TX.m2v and M372 Houston, TX.mpa in our situation, otherwise these files are not going to be moved; if you do not care if such a pair is complete or not, simply state set "_EXTS=" (so clear it) and change the extension of _MASK from .m2v to .m*, so all files with an extension beginning with .m are moved;
_FILT: constitutes an additional filter for file names in order to exclude wrong files; this currently also reflects a four-character prefix, but if this is not always the case, just change M[0-9][0-9][0-9] to M[0-9]*; if you do not want to filter, set this to .*, so it matches everything;
_SEPS: defines the character(s) to split the base file name in order to derive the respective sub-directory, so everything ending before that character and beginning after the first SPACE is the resulting sub-directory name; if you do not define a character here, the whole remaining base file name (so everything after the first SPACE until but not including the (last) .) is taken;

Related

How can I remove a specific part of many folders names using .bat files?

My university is using a proprietary system that outputs data in a very specific way in which each "module" is output into folders following the structure (4 digit numeral) - (name of module)
ex: 5574-CHEM104
I need to remove the name and hyphen so that only the numeral remains:
5574-CHEM104 > 5574
The problem is that there's thousands of these folders and there's no way I could do it by hand. I'm having difficulty trying to automate the process, so if anyone could at least point out a command I could look into it would help immensely
I've tried the REN command, putting "REN 5574-CHEM104 5574", but it only works for one folder. There's thousands of folders, each with different numerals, under "CHEM104", for example, and I need for the program to rename the folder no matter the original name into the first 4 original numerals, which I can't figure out. Thanks!
#ECHO OFF
SETLOCAL
rem The following setting for the directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
FOR /f "delims=" %%e IN (
'dir /b /ad "%sourcedir%\*" '
) DO FOR /f "delims=-" %%o IN ("%%e") DO ECHO REN "%sourcedir%\%%e" "%%o"
)
GOTO :EOF
Always verify against a test directory before applying to real data.
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO REN to REN to actually rename the directories.
Using a list of the directory-names, names only (/b) and directories only (/ad), tokenise the name using - as a delimiter and execute the rename using the token assigned to %%o (default is first token)

Batch - Inner For-Loop token parameter does not accept delayed Expanded variable within Outer For Loop [duplicate]

This question already has answers here:
Issue with *For loop* and *delayed expansion*
(2 answers)
Closed 4 months ago.
I have numerous mp4 files inside a folder with can take the form below
Dancing_2022-10-30_00-05-32_015_Paris.mp4
Eating_in_the_Restaurant_2022-07-03_04-21-25_497_London.mp4
My goal is to create two folders and move the respective files
I want the last underscore (_) delimited item (in this case Paris and London) extracted to create the first folders and then extract the date part (2022-10-30 and 2022-07-03) and create the subfolder. Then move the files accordingly.
My biggest challenge is to extract the tokens. Usually does allows tokens to be extracted from left to right but this assumes the file names are consistent. However in my case the files names are not consistent. There is some consistency from right to left with regards to the placement of the _ delimited parts I want to extract.
I have a draft batch script but the issue I have is that CMD does not allow delayed expanded variable to be passed to the inner For-Loop within the Outer For loop. Any clues to the solution. Thanks
This is my draft batch script.
#echo off
Setlocal EnableDelayedExpansion
for %%F in ("*.mp4") do (
set file=%%~nF
echo !file!
set files="!file:_=" "!"
echo file=!files!
set cnt=-4
for %%a in (!files!) do set /a cnt+=1
echo !cnt!
for /F "tokens=!cnt!* delims=_" %%a in ("!file!") do set output=%%b
echo Output=%output%
for /F "tokens=1-4 delims=_" %%a in ("!output!") do echo %%d
)
endlocal
pause
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following setting for the directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
FOR %%e IN ("%sourcedir%\*.mp4") DO (
SET "fname=%%~ne"
SET "fname=!fname:_=,!"
SET "fname=!fname: =#!"
FOR %%y IN (!fname!) DO SET "fdate=!ftime!"&SET "ftime=!fmsec!"&SET "fmsec=!flocation!"&SET "flocation=%%y"
ECHO MD "!flocation:#= !\!fdate!"
ECHO MOVE "%%e" "!flocation:#= !\!fdate!\"
)
GOTO :EOF
Always verify against a test directory before applying to real data.
The required MD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO MD to MD to actually create the directories. Append 2>nul to suppress error messages (eg. when the directory already exists)
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
Relatively easy to follow. Foe each filename, take the name part. replace each _ with , and each Space with #
The result in fname would thus be a comma-separated list of elements, so process each one, cascading them through the variables.
Finally, replace the # with Space when creating the subdirectory and filename.
Note that the switch between Space and # is intended to take care of places like New York and Los Angeles
== Supplemental == in response to comment:
FOR %%e IN ("%sourcedir%\*.mp4") DO (
SET "fname=%%~ne"
SET "fname=!fname:_=,!"
SET "fname=!fname: =#!"
FOR %%y IN (!fname!) DO SET "fdate=!ftime!"&SET "ftime=!fmsec!"&SET "fmsec=!flocation!"&SET "flocation=%%y"
SET "fname=!fname:,=_!"
CALL SET "fname=%%fname:_!fdate!_!Ftime!_!fmsec!_!flocation!=%%
ECHO MD "!flocation:#= !\!fname!\!fdate!"
ECHO MOVE "%%e" "!flocation:#= !\!fname!\!fdate!\"
ECHO MD "!flocation:#= !\!fname:_= !\!fdate!"
ECHO MOVE "%%e" "!flocation:#= !\!fname:_= !\!fdate!\"
)
After locating the required parts, replace each , with _ in fname, then replace the part from the date onwards with nothing, yielding the activity in fname.
Two sets of reports generated now - one for activity with underscores, the second with spaces.

Searching for partial path\filename in bat

Ok, so I've been bating (hehe) my head against a wall here.
I am looking for an option/code that would allow me to search for a partial path and/or filename from a .bat script that I would export to an outside file.
Now, "search", "export" and "outside file" is something I am fine with. The part that is giving me a headache is the "partial".
To elaborate.
I am looking for a folder called DATA and a file called userinfo.txt inside DATA.
Those are constant. So the path I have is DATA\userinfo.txt
I am also 99% certain that this folder will be in D:\ but thats not a concern right now. Where ever it is I'll find it.
But I cannot figure out how to look for a partial path\filename for the life of me.
Reason I have specified that DATA\userinfo.txt is a constant is due to other folders ability to be named arbitrarily. So in my below example 01-12-2016 does not have to be named according to that convention. For USA it would most likely be named 12-01-2016. It is also sometimes named 20161201 or 20160112 or on top of all that has a letter prefix such as d01-12-2016. On that note DATA is always DATA, which is why I said DATA is constant in my search. Another thing that will be the same is the grandparent folder. When i say "same" i mean "shared" between the two applications. It does not mean it will always be named "program" as in my example below.
Googling this and using things I know has got me nowhere.
Reason I cannot simply use
where /r d: userinfo.txt
is that that specific command will return hundreds of results as there is a userinfo.txt created for every.single.day the program was running and is stored separately.
Alternatively - if there would be a way to comb trough those hundreds of results and find the matching part that would also resolve my issue.
This however brings up another headache as there is usually more than one program with this exact file.
so in the example of
d:\users\path\program\storage\01-12-2016\userinfo.txt
d:\users\path\program\otherstorage\01-12-2016\userinfo.txt
d:\users\path\program\storage\02-12-2016\userinfo.txt
d:\users\path\program\otherstorage\02-12-2016\userinfo.txt
d:\users\path\program\storage\03-12-2016\userinfo.txt
d:\users\path\program\otherstorage\03-12-2016\userinfo.txt
d:\users\path\program\storage\04-12-2016\userinfo.txt
d:\users\path\program\otherstorage\04-12-2016\userinfo.txt
d:\users\path\program\storage\05-12-2016\userinfo.txt
d:\users\path\program\otherstorage\05-12-2016\userinfo.txt
d:\users\path\program\storage\06-12-2016\userinfo.txt
d:\users\path\program\otherstorage\06-12-2016\userinfo.txt
d:\users\path\program\storage\data\userinfo.txt
d:\users\path\program\otherstorage\data\userinfo.txt
Note: storage, otherstorage, storageother, storage2, storagegh are all arbitrary names as these folders are named accoring to end-user wishes.
I would want to export two separate variables for
d:\users\path\program\storage
and
d:\users\path\program\otherstorage
I would also need to do this for \data\userinfo.txt
So if searching for \data\userinfo.txt it would return
d:\users\path\program\storage\data\userinfo.txt
d:\users\path\program\otherstorage\data\userinfo.txt
I would also want to isolate both
d:\users\path\program\storage
and
d:\users\path\program\otherstorage
and use it as (separate) local variables.
I would need to note that installing/downloading any external scripting tools/aids would not be a suitable solution as I work on a lot of computers, most of which I do not have internet access and/or sufficient permissions for external downloads/installations so anything that is not integrated into the bat and needs to be imported separately is a bad idea.
Also, I am working on Windows XP SP3 but I would need this bat to be able to run on XP SP2, XP SP3, Windows 7, Windows 10, Windows NT, Windows 2000.
Any help would be appreciated.
Please note that
d:\users\path\program
would also be an acceptable variable. In this case I would manually amend the remainder of the path or would rely on end-user (my coworkers) input to complete the path correctly. The last has proven to be a fools errand.
The way that I've been handling it until now is to look for a .exe that I KNOW will be in both folders. This is a part of my code below edited to match the current example.
#echo off
SETLOCAL
echo Program will now look for program.exe and programgh.exe. Please input, when asked, matching part of the path for these files.
echo Example:
echo d:\users\path\program\storage\bin\program.exe
echo d:\users\path\program\otherstorage\bin\programgh.exe
echo In above example matching part is d:\users\path\program so you would enter that when prompted
echo Please do not input the last pathing mark: \ (backslash)
echo -------------searching---------------
::I am exporting errors to nul as I don't want them to be spammed by errors and other data that they would think is their fault
where /r c: program*.exe 2>nul
where /r d: program*.exe 2>nul
where /r e: program*.exe 2>nul
where /r f: program*.exe 2>nul
set /p dualpath="Please enter matching paths for program folder: "
After that I would proceed to work with %dualpath% variable.
As it usually happens (to me at least) most people would just copy the example path without taking a look at what the program has spat out and would be confused as to why the program did not work. Either that or would copy everything up to program.exe and programgh.exe - including the otherstorage\bin\ without noticing that \storage\ and \otherstorage\ do not match.
I think this now covers all the comments or additional questions and clarifies a bit better what I need. Thank you all for help so far and I hope that this is easier to understand.
If a Windows cmd command allows wildcards in a (partially or fully qualified) path then wildcards must be used only in the path leaf (i.e. the last item or container in the path). However, you could apply findstr regex to narrow command output e.g. as follows:
where /r d:\ userinfo.txt | findstr /I "\\storage2*\\data\\userinfo.txt"
above command wold narrow output to paths ending with \storage\data\userinfo.txt and \storage2\data\userinfo.txt
Another example - narrow output to paths ending with \storageX\data\userinfo.txt where X is either nothing or any decimal cipher [0-9]:
dir /B /S d:\userinfo.txt | findstr /I "\\storage[0-9]*\\data\\userinfo.txt"
Put the paths to environment variables (with _var prefix for easier next identification), e.g. _varstorage, _varstorage2, …
#ECHO OFF
SETLOCAL EnableExtensions
for /F "delims=" %%F in ('
dir /B /S "d:\userinfo.txt" ^| findstr /I "\\storage[0-9]*\\data\\userinfo.txt"') do (
for /D %%D in ("%%~dpF..") do (
set "_var%%~nxD=%%~fD"
rem %%~fD path
rem %%~nxD last item in above path
rem _var variable name prefix
)
)
rem show result:
set _var
See also next %%~nxD and %%~D explanation: Command Line arguments (Parameters): Parameter Extensions
If I got your intention right, the following script should do what you want:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=D:\" & rem "D:\", "D:\users",..., or "D:\users\path\program"
set "_FILE=userinfo.txt"
rem // Initialise index:
set /A "INDEX=1"
rem // Search for the specified file in the given root directory:
for /F "delims=" %%F in ('dir /B /S "%_ROOT%\%_FILE%"') do (
rem // Iterate once over the grandparent directory itself:
for /D %%D in ("%%F\..\..") do (
rem // Resolve the path of the grantparent directory;
set "ITEM=%%~fD"
rem // Initialise flag (non-empty means not yet stored):
set "FLAG=#"
rem // Toggle delayed expansion to avoid trouble with exclamation marks:
setlocal EnableDelayedExpansion
rem // Iterate over all currently stored grantparent paths:
for /F "tokens=1,* delims==" %%V in ('2^> nul set $ARRAY[') do (
rem // Clear flag in case current grandparent has already been stored:
if /I "!%%V!"=="!ITEM!" set "FLAG="
)
rem // Check flag:
if defined FLAG (
rem // Flag is empty, so current grandparent needs to be stored:
set "$ARRAY[!INDEX!]=!ITEM!"
rem // Transfer stored grandparent over localisation barrier:
for /F "delims=" %%E in ("$ARRAY[!INDEX!]=!ITEM!") do (
endlocal
set "%%E"
)
rem // Increment index
set /A "INDEX+=1"
) else endlocal
)
)
rem // Retrieving final count of grandparent directories:
set /A "INDEX-=1"
rem // Return stored grandparent paths:
set $ARRAY[
endlocal
exit /B
This should return D:\users\path\programs\otherstorage and D:\users\path\programs\storage in your situation, which are stored in the variables $ARRAY[1] and $ARRAY[2], respectively. Due to the array-style variables, this approach is flexible enough to cover also cases where more than two grandparent directories are present.
Based on your above sample this batch
#Echo off
Set Search=\\data\\userinfo.txt
pushd "D:\Users\path\program
For /f "Delims=" %%A in (
'Dir /B/S/A-D userinfo.txt ^|findstr "%Search%$"'
) Do Call :Sub "%%~fA" "%%~dpA.."
Popd
Goto :Eof
:Sub FullName DrivePath
Echo Found %~nx1
Echo in %~dp1
Echo Granny %~nx2
Set "Granny=%~nx2"
Echo in %~dp2
Echo -------
Should give this output (only partially tested)
Found userinfo.txt
in D:\Users\path\program\storage\data\
Granny storage
in D:\Users\path\program\
-------
Found userinfo.txt
in D:\Users\path\program\storage2\data\
Granny storage2
in D:\Users\path\program\
-------
The backslash in Search has to be doubled as it is an escape char for findstr

Finding a string within 2 files and then moving the files. Batch

I recently asked a question about finding files with the same ID and then moving them.
Although the files I am moving do not have the same ID. Instead they have the same string within the files.
Here is the code I have for finding the same ID of files
ECHO OFF
setlocal enableDelayedExpansion
set "source=C:SORCEFOLER"
set "destBIAK=C:BIAKFOLER"
set "destBIPO=C:BIPOFOLDER"
set "POfile=BIPO"
SET "AKfile=BIAK"
pushd "%source%"
for %%F in ("%POfile%*") do (
set "setID=%%F"
set "setID=!setID:%POfile%=!"
if exist "%AKfile%!setID!" robocopy /mov "%source%" "%destbiak%" "%%F"
if exist "%AKfile%!setID!" robocopy /mov "%source%" "%destbipo%" "%AKfile%!setID!"
)
popd
Like I said, I need to actually find a string that matches in the two different and then move them based on the the string.
Here is a example of the beginning of the first file
H|0003341369|20131123
I can not post the entire PO file because it contains customer names and addresses that are proprietary information of the person I am making the file for.
here is the entire AK file
0003341369|SO-02052|20131124|A|
That just a bunch of numbers. Notice that digits 3 through 12 of the PO file match digits 1 though 10 of the AK file.
There are multiple AK files and PO files in one folder.
I was thinking that the script would find the ID of the AK file, search all the PO files until it finds the one that matches and then moves them.
I have looked at several scripts that search for a string, they then output the file name with the string to a certain file. I cant figure out how to output this as a variable.
Thanks
This should take the first term in every AK.* file (the characters before the first | character such as 0003341369 ) and search for it in every PO.* file.
If it finds the term inside two || characters (such as |0003341369| ) then it will move those files to a folder - and after comparing all PO.* files it will move the AK.xxx file to the same folder.
#echo off
for %%a in (AK.*) do (
MD "folder %%~na"
for /f "delims=|" %%b in ('type "%%a" ') do (
for %%c in (PO.*) do findstr "|%%b|" "%%c" >nul && move "%%c" "folder %%~na" >nul
)
move "%%a" "folder %%~na"
)

For loop in batch file reading a file of File Paths

I want to write a Windows batch file script that will loop through a text file of FILE PATHS, do some work using data from each file path, then ultimately delete the file.
I started by running the FORFILES command and sending its output (the #PATH parameter is the full path of any file it matches) to a text file (results.txt).
I end up with a results.txt file like this:
"C:/Windows/Dir1/fileA.log"
"C:/Windows/Dir1/fileA.log"
"C:/Windows/Dir2/fileC.log"
"C:/Windows/Dir3/fileB.log"
What I want to do is:
Use a FOR loop and read each line in the results.txt file
For each line (file path), strip out the directory name that the log file is sitting in (ie: Dir1, Dir2, etc..) and create a directory with that SAME name in a different location (ie. D:/Archive/Backups/Dir1, D:/Archive/Backups/Dir2, etc..) -- assuming the directory doesn't exist.
Move the actual .log file to a zip file in that directory [I have code to do this].
Delete the .log file from its original location. [Pretty straightforward]
I'm having trouble figuring out the best way to accomplish the first 2 steps. My FOR loop seems to stop after reading the very first line:
FOR /F "tokens=1,2,3,4,5,6,7,8,9,10 delims=\" %%G in ("results.txt") DO (
...
)
You don't want to parse the path with the tokens/delims options because you don't know how many directory levels you are dealing with. You want to preserve each line in its entirety. TO parse the path you want to use the FOR variable modifiers. (type HELP FOR from the command line and look at the last section of the output)
%%~pG gives the path (without the drive or file name). If we then strip off the last \, we can go through another FOR iteration and get the name (and possible extension) of the file's directory by using %%~nxA.
The toggling of delayed expansion is just to protect against a possible ! in the path. If you know that no path contains ! then you can simply enable delayed expansion at the top of the script and be done with it.
EDIT - this code has been modified significantly since Aacini pointed out that I misread the requirements. It should satisfy the requirements now.
for /f "usebackq delims=" %%G in ("results.txt") do (
set "myPath=%~pG"
setlocal enableDelayedExpansion
for /f "eol=: delims=" %%A in ("!myPath:~0,-1!") do (
endlocal
if not exist d:\Archive\Backups\%%~nxA md d:\Archive\Backups\%%~nxA
rem ::zip %%G into zip file in the D: location
rem ::you should be able to create the zip with the move option
rem ::so you don't have to del the file
)
)
I wrote this to timestamp files before offloading to SFTP.
Hope you find it useful.
The timestamp coding may seem irrelevant to your issue, but I left it because it's a good example of dissecting the filename itself.
I suggest you put an ECHO in front of the REN command for testing. Different shells may have different results.
In the end, the delayedexpansion command wasn't necessary. It was the sub-routine that fixed my issues with variables inside the loop. That could possibly be because of my OS ver. (Win 8.1) - It wouldn't hurt to leave it.
#echo off
cls
setlocal enabledelayedexpansion
if %time:~0,2% geq 10 set TIMESTAMP=%date:~10,4%%date:~4,2%%date:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%
if %time:~0,2% leq 9 set TIMESTAMP=%date:~10,4%%date:~4,2%%date:~7,2%_0%time:~1,1%%time:~3,2%%time:~6,2%
echo TimeStamp=%TIMESTAMP%
echo.
for %%G in (*.txt) do (
set OLDNAME=%%G
call :MXYZPTLK
)
dir *.txt
goto :EOF
:MXYZPTLK
echo OldName=%OLDNAME%
ren %OLDNAME% %OLDNAME:~0,-4%_%TIMESTAMP%%OLDNAME:~-4,4%
echo.
:END
You have two minor problems:
The path separator in the file is '/' but you use '\' in the for loop.
The quotes around "results.txt" stop it working.
This works. Don't write quotes to results.txt and you won't get a quote at the end of the filename.
#echo off
FOR /F "tokens=3,4 delims=/" %%I in (results.txt) DO (
REM Directory
echo %%I
REM File
echo %%J
)

Resources