loop through a folder with special characters in the folder name - windows

I'm trying to write a batch file to move files from a folder into another folder. The folder has a special character in the name. I can't change that name.
Here is my script
set "MM=%Date:~4,2%"
set "DD=%Date:~7,2%"
set "thisDate=%MM%%DD%"
set baseDir="T:\R ^& D files received"
set backupDir=%baseDir%\2022\%thisDate%
if not exist "%backupDir%\NUL" mkdir "%backupDir%"
for %%f in ("%baseDir%\*.pdf") do (
echo %%f
move %%f "%backupDir%\%%f")
This is not working. I get
R
&
D
files
received\*.pdf
No file is moved.
Any idea or help is appreciated.

#ECHO OFF
SETLOCAL
SET "thisdate=0419"
set "baseDir=U:\R & D files received"
set "backupDir=%baseDir%\2022\%thisDate%"
if not exist "%backupDir%\NUL" mkdir "%backupDir%"
for %%f in ("%baseDir%\*.pdf") do (
echo "%%f"
move "%%f" "%backupDir%\%%~nxf" >nul
)
DIR /s "%basedir%"
GOTO :EOF
Intriguingly. you've used the set "var=value" format for setting thisdate but not for the directory names. I've used a constant for thisdate as I use a YYYYMMDD date format.
Also, my test drive is U:, not T:.
Tips : Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign a terminal \, space or quotes - build pathnames from the elements - counterintuitively, it is likely to make the process easier.
Your for %%f resolved to for %%f in (""T:\R ^& D files received"\*.pdf") do ( hence the strange result.
Note that %%f contains the full pathname to the .pdf files - which includes spaces. Hence you need to "quote the sourcefile name" and use just the name and extension of that file (%%~nxf) concatenated onto the backupdirectoryname string with separator - all of which again contains a space and hence needs to be quoted.
>nul appended to suppress 1 file(s) moved messages.

Related

Batch File Variable to copy specific files

I'm trying to copy specific files from C: to "X: for example". The files are named with the same format.
A1234_ZZabc123_DT1_F1.tst
A4567_ZZdef4567_DT2_F2.tst
A8901_ZZghi1289_DT1.tst
A2345_ZZjfkbu12_to_modify.tst
A6789_ZZlmny568_F1_to_modify.tst
A1234_ZZabc478_DT1.txt
I want to copy only the .tst files, and with the same format as the first 3 Axxxx_ZZyyyxxx_DTx_Fx.tst where x=number and y=letter.
After ZZ, it might be 4 letters and 3 numbers, or 5 letters and 4 numbers, like a "namecode".
Example: ZZkusha122 or ZZkus1551.
I need to copy the folders along with the files too.
I'm new to coding and really need some help.
I need to find and copy all those files along 10k+ files together
You claim that the first 3 of your example filenames fit the pattern you describe. I believe that only two do.
#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 /a-d "%sourcedir%\A*.tst"^|findstr /X /I /R "A[0-9][0-9][0-9][0-9]_ZZ[a-z][a-z][a-z]_DT[0-9]_F[0-9].tst" '
) DO ECHO COPY "%sourcedir%\%%e" X:
)
GOTO :EOF
Always verify against a test directory before applying to real data.
The required COPY commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO COPY to COPY to actually copy the files. Append >nul to suppress report messages (eg. 1 file copied)
Simply execute a dir command that reports only filenames matching the *.tst mask. Filter this list with findstr which /X exactly matches the regular expression provided. findstr only has a limited implementation of regular expressions. The /I forces a case-insensitive match. If you want case-sensitive, remove the /I and change each [a-z] to [a-zA-Z] (leave as-is if you want lower-case only in these positions.)
See findstr /? from the prompt for more documentation, or search for examples on SO.
---- revision to cater for multiple filemasks and subdirectories ---
#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"
SET "maskfile=%sourcedir%\q74442552.txt"
FOR /f "tokens=1*delims=" %%e IN (
'dir /b/s /a-d "%sourcedir%\*.tst"^|findstr /E /I /R /g:"%maskfile%" '
) DO ECHO COPY "%%e" X:
)
GOTO :EOF
Changes:
Establish a file, name irrelevant, as maskfile
The dir command requires the /s switch to scan subdirectories
The filemask for the dir command loses the initial A
The findstr command replaces the /X switch with /E
The findstr command loses the regex expression. These are transferred to a file and the file is nominated by the /g: switch.
The copy command loses the source-directory as the directory will be included in %%e
The file "q74442552.txt" contains lines that are of the form
A[0-9][0-9][0-9][0-9]_ZZ[a-z][a-z][a-z]_DT[0-9]_F[0-9].tst
A[0-9][0-9][0-9][0-9]_ZZ[a-z][a-z][a-z]_to.*.tst
This time, %%e acquires the full pathname of the files found. Since the filemask ends .tst, the only filenames to pass the dir filter will be those that end .tst.
The /e switch tells findstr to match string that End with the regex strings in the file specified as /g:.
The strings in the file must comply with Microsoft's partial regex implementation, one to a line.
In summary, findstr uses as regex
Any character,literally
[set] any character of a set of characters
[^set] any character not in a set of characters
. any character
.* any number of any character
prefix any of the special characters with\ to use it literally
a set may include a range by using low-high
So - you then need to brew-your own using the examples I've supplied. The second line matches Axxxx_ZZyyy_to{anything}.tst for instance.
--- Minor revision to deal with maintaining destination-tree -----
(see notes to final revision for why this doesn't quite work)
#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"
SET "maskfile=%sourcedir%\q74442552.txt"
SET "destdir=u:\your results"
FOR /f "tokens=1*delims=" %%e IN (
'dir /b/s /a-d "%sourcedir%\*.tst"^|findstr /E /I /R /g:"%maskfile%" '
) DO ECHO "%%~nxe"&XCOPY /Y /D /S "%sourcedir%\%%~nxe" "%destdir%\">nul
)
GOTO :EOF
This version adds the destination root directory as destdir.
The dir ... findstr... works as before to list the filenames to copy.
The prior version used echo copy to report the proposed copy operation, but the destination was always the same directory.
The replacement XCOPY line maintains the directory structure at the destination.
Note : the XCOPY is "live". The files will be copied to the destination if run as-is. Always verify against a test directory before applying to real data.
To "defuse" the XCOPY, add the /L switch and remove the >nul. This will cause XCOPY to report the source name that would be copied instead of copying it. (The >nul suppresses the report)
The /D only copies source files that eitherr do not exist in the destination of have a later datestamp in the source.
The action is to xcopy each filename found (%%~nxe) from the source directory tree to the destination. Therefore, any file xyz.tst found anywhere in the source tree will be xcopyd to the destination tree. The /D means that once xyz.tst is encountered on the source tree, it will be skipped should it be encountered again.
--- Final (I hope) revision ---
#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:\Users\tocil\Desktop\aoi"
SET "maskfile=%sourcedir%\q74442552.txt"
SET "destdir=u:\your results"
FOR /f "tokens=1*delims=" %%e IN (
'dir /b/s /a-d "%sourcedir%\*.tst"^|findstr /E /I /R /g:"%maskfile%" '
) DO (
rem drive and path to 'dirname' - has terminal "\"
SET "dirname=%%~dpe"
rem remove the sourcedir from dirname
FOR %%y IN ("%sourcedir%") DO CALL SET "dirname=%%dirname:%%~y=%%"
rem copy or xcopy the file to the destination.
FOR /f "tokens=2delims==" %%y IN ('set dirname') DO XCOPY /Y "%%e" "%destdir%%%y">nul
)
)
GOTO :EOF
Always verify against a test directory before applying to real data.
Note to self: Only if the filemask provided to XCOPY is ambiguous (ie. contains ? or *) will XCOPY obey the /s switch unless the target file exists in the starting source directory.
hence
xcopy /s sourcedir\myfile destdir
will copy myfile from the entire tree ONLY if sourcedir\myfile exists.
xcopy /s sourcedir\myf?le destdir
will copy myfile from the entire tree regardless. Unfortunately it will also copy myfale and myfule as well.
Hence, the new approach.
First, perform a dir /b /s to get all of the filenames and filter as before. This is being assigned to %%e.
Take the drive and path only of the file and assign to dirname.
The next step is a little complex. First, set the value of %%y to the name of the source directory. Next, use a parser trick to remove that name from dirname. The mechanics are: Parse the %%dirname:%%~y=%% (because the call causes the set to be executed in a sub-shell) whuch does the normal left-to-right evaluation. %% is an escaped-%, so is replaced by %; %%y is an active metavariable so is replaced by (the name of the source directory) and the ~ causes the quotes to be stripped from that name. The resultant command executed is thus SET "dirname=%dirname:nameofsourcedirectory=%"
So now we can construct a copy-class instruction. dirname now contains the relative directory for the destination, which we can extract from the environment by parsing a set listing (Could also be done with delayed expansion) where %%y gets set to the relative directory and has both a leading and trailing backslash, so the destination directory is simply "%destdir%%%y". XCOPY then knows to create that directory if necessary (%%y has a trailing backslash) and we know the source filename is in %%e.
You could also use a copy to do the same thing, but you'd need to create the destination directory first. Another advantage of XCOPY is that you can also specify the /d switch to not copy files that have an earlier date over files that have a later date.

Problem with EnableDelayedExpansion, For, and filenames with special characters like ! % &

The Setup:
I have a set of files of type .type1 and want to batch convert them to .type2 using a program I have
The converting program Program.exe (in a folder called TOOLS) takes two arguments -i for InputFile and -o for OutputFile but is unable to create folders/directories (if the output folder doesn't exist it fails)
The .type1 files are in different folders. All of these folders are in a folder called InputFolder
I want the .type2 converted files to be outputted to the OutputFolder preserving the folder structure of the originals
So, In the main folder I have 4 things:-
A. InputFolder :where all the input files (divided into different folders) exist
B. OutputFolder:where all the converted files should be outputted (divided into different folders matching the InputFolder)
C. TOOLS :where the converter Program.exe exists
D. Batch Convert.bat :the batch file I wrote to batch convert the files. The contents of which are in the next section
What I tried:
rem -------Setting some general variables-------
set "BatchPath=%~dp0"
set "InputPath=%BatchPath%InputFolder\"
set "OutputPath=%BatchPath%OutputFolder\"
rem -------Saving a list of all .type1 files-------
dir /s/b/a-d "%InputPath%*.type1">"%BatchPath%TOOLS\FilesList.txt"
rem -------Starting a loop for each file listed in FilesList.txt -------
for /f "usebackq delims=" %%j in ("%BatchPath%TOOLS\FilesList.txt") do (
setlocal EnableDelayedExpansion
rem --Setting variables replacing "input string" with "output string"--
set "InputFileType2=%%~dpnj.type2"
set "OutputFile=!InputFileType2:%InputPath%=%OutputPath%!"
set "InputFilePath=%%~dpj"
set "OutputFilePath=!InputFilePath:%InputPath%=%OutputPath%!"
rem --Creating output folders--
mkdir "!OutputFilePath!"
rem --Start the converting process--
"%BatchPath%TOOLS\Program.exe" -i "%%j" -o "!OutputFile!"
endlocal
)
The Problem:
Some of the folders and the files inside the InputFolder have special characters (like ! % &) which cause problems with the converting.
Is there a way to do this with the least amount of conflict with special characters (like removing the need to EnableDelayedExpansion to remove conflict with !?
is there a way to do it without the PwerShell or the Call function?
(because I have thousands of big files and the Call function can be slow and it seems like it always tries to access the HDD first before a :Label)
As compo said, you just need to change the order of lines.
It's important to expand the FOR variables only with delayed expansion disabled.
Because expanding them inside delayed expansion enabled, destroys the exclamation marks !
for /f "usebackq delims=" %%j in ("%BatchPath%TOOLS\FilesList.txt") do (
rem --Setting variables replacing "input string" with "output string"--
set "InputFileType2=%%~dpnj.type2"
set "OutputFile=!InputFileType2:%InputPath%=%OutputPath%!"
set "InputFilePath=%%~dpj"
set "helper=%%j"
setlocal EnableDelayedExpansion
set "OutputFilePath=!InputFilePath:%InputPath%=%OutputPath%!"
rem --Creating output folders--
mkdir "!OutputFilePath!"
rem --Start the converting process--
"%BatchPath%TOOLS\Program.exe" -i "!helper!" -o "!OutputFile!"
endlocal
)

Creating input subfolder structure inside output folder

I have a batch script that:
read input files from a folder
elaborate them
store output files in another folder
Example code:
set pathTmp=D:\a\b\c
set pathIn=%pathTmp%\in
set pathOut=%pathTmp%\out
for /f %%i in ('dir /b %pathIn%') do (
java XXX.jar %pathIn%\%%i >> %pathOut%\%%i
)
Now I'd like to modify it to read files from all subfolders of pathIn and put the output file in the same subfolder but under pathOut.
Example: if input file is in pathIn\zzz, the output file must be in pathOut\zzz.
How can I recreate the input subfolder structure inside output folder?
I would use xcopy together with the /L switch (to list files that would be copied) to retrieve the relative paths. For this to work, you need to change to the directory %pathIn% first and specify a relative source path (for this purpose, the commands pushd and popd can be used).
For example, when the current working directory is D:\a\b\c\in and its content is...:
D:\a\b\c\in
| data.bin
+---subdir1
| sample.txt
| sample.xml
\---subdir2
anything.txt
...the command line xcopy /L /I /S /E "." "D:\a\b\c\out" would return:
.\data.bin
.\subdir1\sample.txt
.\subdir1\sample.xml
.\subdir2\anything.txt
3 File(s)
As you can see there are paths relative to the current directory. To get rid of the summary line 3 File(s), the find ".\" command line is used to return only those lines containing .\.
So here is the modified script:
set "pathTmp=D:\a\b\c"
set "pathIn=%pathTmp%\in"
set "pathOut=%pathTmp%\out"
pushd "%pathIn%"
for /F "delims=" %%I in ('xcopy /L /I /S /E "." "%pathOut%" ^| find ".\"') do (
md "%pathOut%\%%I\.." > nul 2>&1
java "XXX.jar" "%%I" > "%pathOut%\%%I"
)
popd
Additionally, I placed md "%pathOut%\%%I\.." > nul 2>&1 before the java command line so that the directory is created in advance, not sure if this is needed though. The redirection > nul 2>&1 avoids any output, including error messages, to be displayed.
I put quotation marks around all paths in order to avoid trouble with white-spaces or any special characters in them. I also quoted the assignment expressions in the set command lines.
You need to specify the option string "delims=" in the for /F command line, because the default options tokens=1 and delims=TABSPACE would split your paths unintentionally at the first white-space.
Note that the redirection operator >> means to append to a file if it already exists. To overwrite, use the > operator (which I used).
You could do something like this:
#setlocal EnableDelayedExpansion
#echo off
set pathTmp=D:\a\b\c
set pathIn=%pathTmp%\in
set pathOut=%pathTmp%\out
REM set inLength=ADD FUNCTION TO CALCULATE LENGTH OF PATHIN
for /f %%i in ('dir /b /s %pathIn%') do (
set var=%%i
java XXX.jar %%i >> %pathOut%\!var:~%inLength%!
)
This will strip the length of the pathIn directory from the absolute path leaving only the relative path. Then it appends the relative path onto the pathOut var
You would need to find or write a function to get the length of the the pathIn string. Check out some solutions here.

File name with vbs scripts

I want to add a charecter file name.
For Example
123456.jpg --> 1234506.jpg
ABCDEF.jpg --> ABCDE0F.jpg
orginal file 6 charecters new file name 7 charecter (I want to add 0 for all file name Please attention after 0 character has a character.)
for this proccessbatch file or vbs
Regards,
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "delims=" %%a in (
'dir /b /a-d ^|
findstr /r /b
/c:"[^.][^.][^.][^.][^.][^.]\."
/c:"[^.][^.][^.][^.][^.][^.]$"
'
) do (
set "fileName=%%a"
setlocal enabledelayedexpansion
for %%b in ("!filename:~0,5!0!filename:~5!") do endlocal & if not exist "%%~b" echo ren "%%a" "%%~b"
)
This uses dir command to retrieve the list of files and findstr to filter and only get the files with 6 characters in filename. For each file found, the file name is retrieved into a variable, and substring operation is done to compose the new name.
To avoid problems with exclamations in file names, we need to have delayed expansion enabled to read the variable, but disabled to execute the command. This is the reason for the second for, enable delayed expansion, read the required value and store it inside the for command replaceable parameter, disable delayed expansion and now, without problem with exclamations, execute the required command.
The ren command is only echoed to console. If the output is correct, remove the echo to execute the rename operation
Here's how it can be done with VBScript and a FileSystemObject. This script will need to be in the same folder as your files. Otherwise, you'll have to use the full path to your files.
' Files to update...
a = Array("123456.jpg", "ABCDEF.jpg")
With CreateObject("Scripting.FileSystemObject")
For i = 0 To UBound(a)
' Change name. Use first 5 chars, plus "0", plus rest...
.GetFile(a(i)).Name = Left(a(i), 5) & "0" & Mid(a(i), 6)
Next
End With

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