Wildcards for directory in Windows batch command - windows

I need to copy the contents of a folder to another folder using a batch file - the problem I'm facing is that one of the parent folders will change every day, being named after today's date. So, for example, I have the following command:
xcopy /Y /S "\\auto-jenkins\Builds\2017\R1\\[0822]\EN\\*.*" "C:\Users\Administrator\Desktop\EN"
This works fine today, unfortunately tomorrow the [0822] will not exist and the files I need will be under [0823]. Does anyone know of a way I can use a wildcard in place of [0822]?
The [08**] folder will be the only folder below \R1 if that helps...

Does anyone know of a way I can use a wildcard in place of [0822]?
You don't need a wildcard. Use the current date (in the correct format) instead. Use the following batch file.
CopyFiles.cmd:
#echo off
setlocal
rem get the date
rem use findstr to strip blank lines from wmic output
for /f "usebackq skip=1 tokens=1,2" %%g in (`wmic Path Win32_LocalTime Get Day^,Month ^| findstr /r /v "^$"`) do (
set _day=00%%g
set _month=00%%h
)
rem pad day and month with leading zeros
set _month=%_month:~-2%
set _day=%_day:~-2%
xcopy /Y /S "\auto-jenkins\Builds\2017\R1[%_month%%_day%]\EN*.*" "C:\Users\Administrator\Desktop\EN"
endlocal
Further Reading
An A-Z Index of the Windows CMD command line - An excellent reference for all things Windows cmd line related.
for /f - Loop command against the results of another command.
wmic - Windows Management Instrumentation Command.

Since there is only a single folder in the R1 directory anyway, you can use for /D to get its name:
for /D %%D in ("\\auto-jenkins\Builds\2017\R1\*") do (
xcopy /Y /S "%%~D\EN\*.*" "C:\Users\Administrator\Desktop\EN"
)
The * is a global wild-card that stands for any number of arbitrary characters. Instead of it, you could also use [????] so your folder name must consist of exactly four characters in between [].

You can use the automatic date variable %date which is country specific:
xcopy /Y /S "\auto-jenkins\Builds\2017\R1\[%date:~3,2%%date:~0,2%]\EN\*.*" "C:\Users\Administrator\Desktop\EN"
Here, the month and the day are extracted from the date string. First number is the start position (starting at 0), next number is the length.

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.

How do I get a relative path out of a windows batch file using echo?

How do I get relative directories / partial paths to display as echo output from a windows .bat file?
How to split the filename from a full path in batch?
Talks about everything but.
I've found drive letters, filenames, extensions, shortened (8.3) names, and full paths - but no relative paths.
I'm running a recursive FOR /R loop; which traverses sub-directories. I would like something - that doesn't have twenty characters of useless path info - that tells me which directory each duplicate file lives in... without hardcoding the .bat file to live in a certain directory/path?
Maybe a solution would be to measure the length of the script's path and cut that off of the front of the full path? But I don't know how to manipulate that.
Script could be in many locations:
F:\a.bat<BR>
F:\Dir1\fileA.txt<BR>
F:\Dir20\fileA.txt
C:\Users\Longusername\Desktop\Container\a.bat<BR>
C:\Users\Longusername\Desktop\Container\Dir1\fileA<BR>
C:\Users\Longusername\Desktop\Container\Dir20\fileA
And right now the only options I have for output are (%%~nxG):
fileA.txt
fileA.txt
Which doesn't tell me which directory each file is in...or (%%~pnxG)
\Users\Longusername\Desktop\Container\Dir1\fileA.txt
\Users\Longusername\Desktop\Container\Dir20\fileA.txt
What I'd like, from any location:
\Dir1\fileA.txt
\Dir20\fileA.txt
Could be missing the leading \, but that's negligible. Other options than echo are permissible if they'll work on most window machines. They may lead to more questions, though - as I've figured out my other pieces with echo.
quite easy, if you think about it: just remove the current directory path (%cd%):
#echo off
setlocal enabledelayedexpansion
for /r %%a in (*.txt) do (
set "x=%%a"
echo with \: !x:%cd%\=\!
echo without \: !x:%cd%\=!
)
By the way: \folder\file always refers to the root of the drive (x:\folder\file), so it's not exactly a relative path.
This is similar to the already accepted answer, but with delayed expansion enabled only where needed. This should correctly output filenames containing ! characters.
#Echo Off
SetLocal DisableDelayedExpansion
Set "TopLevel=C:\Users\LongUserName"
For /R "%TopLevel%" %%A In ("*.txt") Do (
Set "_=%%A"
SetLocal EnableDelayedExpansion
Echo=!_:*%TopLevel%=!
EndLocal
)
Pause
You could also use Set "TopLevel=%~dp0", (the running script's directory) or Set "TopLevel=%~dp0..", (the running script's parent directory)
One potential benefit of the above method is that you can also use a location relative to the current directory for the value of %TopLevel% too, (in this case, based upon the initial example, the current directory would be C:\Users):
Set "TopLevel=LongUserName"
Although this would only work correctly if LongUserName didn't already exist as content of the path earlier in the tree.
You could use xcopy together with its /S (include sub-directories) and /L (list but do not copy) options since it returns relative paths then, so you do not have to do any string manipulation, which might sometimes be a bit dangerous, particularly when the current directory is the root of a drive:
xcopy /L /S /I /Y /R ".\*.txt" "\" | find ".\"
The appended find command constitutes a filter that removes the summary line # File(s) from the output.
To capture the output of the aforementioned command line just use a for /F loop:
for /F "delims=" %%I in ('
xcopy /L /S /I /Y /R ".\*.txt" "\" ^| find ".\"
') do (
rem // Do something with each item:
echo/%%I
)

batch - execute command for every file with specific creation date

got this piece of code:
forfiles /P %ParentFolder% /S /M %Format% /C "cmd /c %exeFile% #path"
executing some exe for every file matching format as parameter.
any way to add "creation date" as a condition to run command via CMD?
something like :
for all files in directory (recursive) X if creation date newer then 1 day ago do (run) some exe with this file's path as param
This is not possible with forfiles, because, when the /D option is provided, it only regards the last modification date only (not even the modification time).
Unfortunately, there are no native commands for date/time maths, so I suggest to switch to a language that is capable of that; for instance, PowerShell, VBScript, JavaScript (which are all native to Windows past XP).
In case the modification date could be used, and a simple check with the date of today is sufficient, the following forfiles command line could be used:
forfiles /S /P "%ParentFolder%" /M "%Format%" /D +0 /C "cmd /C \"%exeFile%\" #path"
The /D option with a non-negative number lets forfiles return files that have been modified the given number of days after today or later (although you would need a time-machine; hence I consider this a design flaw). For +0 as the given number of days, all matching files modified today are returned, because forfiles /D only checks the modification date but does not care about the modification time.
If a simple equality check of the creation date with the date of today is fine for you, it can be done in batch-file scripting quite easily though (see all the explanatory rem remarks for how the following script works):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "ParentFolder=."
set "Format=*.*"
set "exeFile=" & rem // (full path to executable file)
set "tmpFile=%TEMP%\%~n0_%RANDOM%.tmp"
rem // Create a temporary file and retrieve its creation date:
2> nul del "%tmpFile%" & > "%tmpFile%" break & set "TODAY="
for /F "skip=5" %%J in ('dir /N /4 /-C /T:C "%tmpFile%"') do (
if not defined TODAY set "TODAY=%%J" & del "%tmpFile%"
)
rem // Change to predefined parent directory:
pushd "%ParentFolder%" && (
rem // Return all files recursively:
for /F "delims=" %%I in ('dir /B /S /A:-D "%Format%"') do (
rem // Determine the creation date for the current file:
set "FIRST=#"
for /F "skip=5" %%J in ('dir /N /-C /T:C "%%I"') do (
rem // Regard line listing file only, ignore summary lines:
if defined FIRST (
set "FIRST="
rem // Check creation date against today:
if "%%J"=="%TODAY%" (
rem // Return files created today:
echo "%%I" has been created today.
rem // Run external program on found file:
if defined exeFile "%exeFile%" "%%I"
)
)
)
)
rem // Restore previous working directory:
popd
)
endlocal
exit /B
I am using two dir command lines here:
the first one returns a bare list of files recursively (/S; no directories because of /A:-D) without any dates/times, headers and footers, due to switch /B; not using this switch would lead to header and footer lines for the whole output and for every iterated sub-directory also, so the output would be quite complicated to be parsed;
the second one receives each file returned by the first one; since there is no /B but the /N option, the file creation date/time is returned (/T:C); for every file the output looks like this:
Volume in drive D is DATA
Volume Serial Number is 0000-0000
Directory of D:\Data
2016/09/29 16:00 1024 current_file.txt
1 File(s) 1024 bytes
0 Dir(s) 1099511623680 bytes free
the first token of the sixth line constituting the creation date is split off and compared against the current date %DATE%; to ignore the header, the skip=5 option of the for /F loop is used; to ignore the summary lines, the variable FIRST is used;
Note that the date format is locale-dependent; as long as there appear no spaces in the date, this is no problem as the current date is also determined by the dir command applied on a temporary file.
Looking at the forfiles /? shows the switch /D that will exclude files that are younger (/d -<days>) or older (/d +<days>) than the given value for <days>.
As you want the files from today, you would set <date> to 0.
Notice, that this will look on the changed date!
Other way would be to use a for /r-loop and get a list of the creation date with dir /T:C ; to sort it add /O-D. Then separate that using a for /f loop to get the lines of the output of dir and another one to separate it (possibly easier without the nested loops).
You can than compare the creation date with %date% or when using one day=24 hrs compare the creation time with %time% additionally.

List subdirectory contents with date created/modified in CMD

In short: In Windows CMD, I need to list all the contents in all of the sub-directories of a folder, and their date-created or date-modified timestamps.
In long:
I want to regularly run a command which outputs the creation datetimes of files we upload to our FTP. I'm able to do this for one folder at a time and get the output in a text file (dir . h:\uploadtimes.txt). I'd like to do this for over 100 folders in one go. Filename and datetime-created is all I need.
THANKS!
dir /? could help (or read command reference); one could use /TC switch instead of /TW one:
dir X:\folder\*.* /S /A-D /-C /TW > h:\uploadtimes.txt
Another approach giving fully qualified file names but always date-modified timestamp regardless of /T switch unfortunately:
for /F "delims=" %G in ('dir X:\folder\*.* /S /B /A-D') do #echo %G %~TG > h:\uploadtimes.txt
Resources (required reading):
(command reference) An A-Z Index of the Windows CMD command line
(additional particularities) Windows CMD Shell Command Line Syntax
(%~G etc. special page) Command Line arguments (Parameters)

Windows batch looping through subfolders and running a command

I'm trying to have a nested for loop in windows command prompt where I go to each subfolder in a specified folder to concatenate all the text files in there into one text file. I'm trying to use
FOR /F IN (.) DO (for %f in (*.dat) do type “%f” >> aggregate.txt)
but its not working. Someone help me!
I'm not sure if you want to concatenate .txt or .dat files, but this should work for .dat
(for /r %f in (*.dat) do #type "%f")>aggregate.txt
Type HELP FOR from the command line for more info on the many forms of the FOR statement.
It is more efficient to enclose the whole command in parentheses and do redirection just once instead of doing append redirection for each file.
EDIT - solution for refined requirement in comment: one aggregate per folder
If needed, first delete any existing aggregate.txt files
del /s aggregate.txt
Then the following should give you your desired result
for /r %F in (*.dat) do #type "%F" >>"%~dpF\aggregate.txt"
Note that if you put the commands in a batch file you must double the percents before the FOR variables. For example, %F on the command line would need to be %%F in a batch file.
Simply use for /r ... to walk all subdirectories of a given directory. Example:
for /r %f in (*.dat) do #type %d >> aggregate.txt
no nesting needed. Alternately:
(for /r %f in (*.dat) do #type %d) >> aggregate.txt
You can use /d instead of /r to go only one level deep in the hierarchy and list only folders (not files matching a certain wildcard). Which could be used as a piece of the puzzle when doing the directory traversal "manually". However, given the issues with variable expansion (inside loops, with and without subs) in NT scripts I would recommend you stick to the straightforward solution. I've used this for cleanup scripts in the past, but moved to for /r for some time now.
This should get you started
#echo off
for /r %%i in (*.dat) do #call :ConcatFolder "%%i"
:ConcatFolder
if "" == "%1" goto :EOF
setlocal ENABLEEXTENSIONS
set FLDR=%~dp1
type "%1" >> "%FLDR%aggregate.txt"
endlocal & goto :EOF

Resources