Batch to move folders that are 30 days old using FORFILES - windows

I am currently working that moves ONLY folders from one directory to a folder. The current bacth file I have created is as follows.
Set /p target=Select Target Destination:
ForFiles /P "C:\Batch test" /D -4 /C "CMD /C if #ISDIR==TRUE echo Move #FILE %target%" &Move #File %target%
pause
The problem I have is with using a target directory that has a space in it for instance "C:\Move Test". I was wondering if there was a way to still move folders to another directory that has a space in the name.

You can replace the double quotes with their hexadecimal equivalent 0x22:
ForFiles /P "C:\Batch test" /D -4 /C "CMD /C if #ISDIR==TRUE (echo Move #FILE 0x22%target%0x22 & Move #File 0x22%target%0x22)"
If you get problems with other characters you can also replace them; 0x26=&, 0x28=(, 0x29=).

Your command line looks quite strange, there are several mistakes:
ForFiles /P "C:\Batch test" /D -4 /C "CMD /C if #ISDIR==TRUE echo Move #FILE %target%" &Move #File %target%
What is wrong:
you are talking about 30 days of age, but you specify /D -4, although /D -30 was correct; note that /D checks for the last modification date, not for the creation date!
there is an echo in front of the (first) move command, so the move command is displayed rather than executed; to avoid that, simply remove echo;
there is a second move command after forfiles, as it appears behind the closing quotation mark of the forfiles /C part, and behind the commmand concatenation operator &; this is completely out of scope of forfiles, therefore #file was treated literally; the ampersand and that orphaned move command needs to be removed;
to answer your actual question: to aviod problems with spaces (or other special characters) appearing in file/directory paths, put quotation marks around them; but: since that part appears within the command string after forfiles /C, which is already quoted, you cannot simply use "", you need to escape them; there are two options in forfiles: 1. using \", and 2. using 0x22 (according to the forfiles /? help, you can specify characters by their hexadecimal codes in the format 0xHH, the code 0x22 represents a " character); I strongly recommend option 2., because in variant 1. there still appear " characters that could impact the command interpreter cmd as it is not familiar with the \" escaping (its escape character is ^);
Thus the corrected command line looks like this:
ForFiles /P "C:\Batch test" /D -30 /C "CMD /C if #ISDIR==TRUE Move #FILE 0x22%target%0x22"
For the sake of completeness, this is how option 1. would look like:
ForFiles /P "C:\Batch test" /D -30 /C "CMD /C if #ISDIR==TRUE Move #FILE \"%target%\""
Note that you do not need to quote the source here, because #FILE (like all path-related #-style variables) expand to already quoted strings.

Related

How do you run a command with a full path from the Windows forfiles command?

I am trying to run the following command:
forfiles /p ..\Schemas /m *.xsd /c "cmd /c ""C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe #path /classes"""
However, it fails with:
ERROR: Invalid argument/option - 'Files'.
Type "FORFILES /?" for usage.
These also don't work:
forfiles /p ..\Schemas /m *.xsd /c "cmd /c \"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe\" #path /classes"
forfiles /p ..\Schemas /m *.xsd /c "cmd /c ^"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe^" #path /classes"
It's definitely failing because the path being passed to cmd contains spaces. You normally solve this by quoting the whole argument. However, there doesn't appear to be a way to pass the double quote.
So, how do you run a command with a full path from the Windows forfiles command?
In your first attempt, you tried to put (additional) quotation marks around the entire command line after cmd /c rather than the path to the executable only, which will fail as cmd tries to find and execute the whole line as a command. Hence you needed to move the closing quotes immediately after xsd.exe instead of at the end of the command line.
Nevertheless, in general, the main problem here is that cmd and forfiles handle quotation marks differently. forfiles uses \" to escape quoting, but cmd does not care about the backslash. In addition, in case your code appears within a parenthesised block of code, the literal parentheses in your path may cause trouble additionally if they do not appear quoted to cmd. Finally, cmd may attempt to strip off quotation marks (unexpectedly), which may cause even more problems.
To solve all this, use another method of providing literal quotation marks for the command line after /C: forfiles supports specifying characters by their hexadecimal code in 0xHH notation; so stating 0x22 hides the quotation marks from the parent cmd instance until the command line is actually executed:
forfiles /p ..\Schemas /m *.xsd /c "cmd /c 0x220x22C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe0x22 #path /classes0x22"
This results in the following command line to be executed (using "D:\Data\Schemas\sample.xsd" as an example value for #path):
cmd /c ""C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe" "D:\Data\Schemas\sample.xsd" /classes"
Finally, after stripping off the outer-most quotes by cmd, the following command line is executed:
"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe" "D:\Data\Schemas\sample.xsd" /classes
Note that #path, as well as all other file-name- and path-related #-style variables of forfiles expand to already quoted values.
Initially, I completely forgot about the fact that cmd tries to strip off quotes. Klitos Kyriacou's answer reminded me of that; so if you like my answer, please do not forget to give credits to them as well!
It's not forfiles that's at fault here; it's the way cmd.exe treats quotation marks. It's essentially trying to be too clever for its own good. The CMD /C command can take any arbitrary line as a command, including one that contains spaces; however, if the command line given after CMD /C starts with a quotation mark, it removes the first and last quotation marks from the line before it tries to execute it. Thus, these two lines are exactly equivalent:
cmd /c echo hello
cmd /c "echo hello"
Now the problem is that the quotation mark gets stripped off in the following command:
cmd /c "C:\Program Files\xyz\abc.exe" one two
To make it do what you want, you need to enclose the whole thing in quotes (the inner quotes don't need to be escaped):
cmd /c ""C:\Program Files\xyz\abc.exe" one two"
Now, you can put this into a FORFILES command. FORFILES requires quotation marks to be escaped with a \, so we need to write:
forfiles /c "cmd /c \"\"C:\Program Files\xyz\abc.exe\" one two\""
Or in your specific case:
forfiles /p ..\Schemas /m *.xsd /c "cmd /c \"\"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\xsd.exe\" #path /classes\""
(Note that all quotation marks for CMD are escaped, but the outer quotation marks are for FORFILES so are not escaped.)

Forfiles, move files that were created in [*Month]

I have a problem with forfiles syntax. I want to write a simple forfiles command, that will:
Select files, that were modified in January,February,March, etc.
Move these files to another folder.
but I'm having problems to write proper syntax. Here is what I wrote at this point.
forfiles /m *.file /d (What do I write here?) /C "cmd /c move #file C:\Users\User1\Desktop\New folder"
Date modified format is: DD/MM/YYYY.
I don't think what you want is possible with forfiles unless you can process the list of months in reverse order (first move files with /d +07/08/2016, then with /d +01/07/2016, then /d +01/06/2016, and so on).
If PowerShell is an option you may have better luck using that, e.g. like this:
Get-ChildItem *.file |
Where-Object { $_.LastWriteTime.Month -eq 8 } |
Move-Item -Destination 'C:\Users\User1\Desktop\New folder'
To filter out all files modified in a certain month (June 2016 in the following example), you could use two nested forfiles loops, where the outer one passes all files modified on or after the first day of the month to the inner one, which returns all items out of them that are modified on or before the last day of the month:
> nul 2>&1 forfiles /D +01/06/2016 /C "forfiles forfiles /M #file /D -30/06/2016 /C 0x22cmd /C if #isdir==FALSE > con echo #fdate0x09#ftime0x09#path0x22"
This script returns a list of matching files with their modification dates and times. To move the files to another directory (for instance, C:\Backup\June-2016), the command line looks like this:
> nul 2>&1 forfiles /D +01/06/2016 /C "forfiles forfiles /M #file /D -30/06/2016 /C 0x22cmd /C if #isdir==FALSE move 00x7822#path00x7822 00x7822C:\Backup\June2016\\#file00x78220x22"
The redirections avoid empty lines and error messages to be output by forfiles.

xcopy returns error "Invalid number of parameters" when exclude parameter is set

Issuing:
xcopy X:\ "Y:\...\bin\9876543210\" /c /g /d /i /e /r /h /y
works as expected. However:
xcopy X:\ "Y:\...\bin\9876543210\" /c /g /d /i /e /r /h /y /exclude:"Y:\...\exclude.txt"
returns error:
Invalid number of parameters
Which also occurs when path names (containing spaces) are not enclosed by quotation marks. This however, is not the case. Paths (edited for readability) all correspond correctly. Syntax (as per Product Documentation - Xcopy) is also correct. Concerning OS is Windows XP Professional x32 SP3.
Why is second cmd returning error and how is it to be solved? I am not looking for alternatives to xcopy (robocopy etc.).
XCOPY is an old command harking back to the days of DOS. It looks like the /EXCLUDE option was never updated to support long file names. Ugh :-(
If you remove the quotes, then the text after the space is interpreted as an additional parameter, and you get the "Invalid number of parameters" error. If you keep the quotes, then it treats the quotes as part of the path, and reports it cannot find the file.
I believe you have three possible solutions:
1) Use the short 8.3 folder names in your path.
Of course this cannot work if your volume has short names disabled.
2) Use the SUBST command to create a drive alias for your troublesome path.
subst Q: "Y:\path with spaces"
xcopy X:\ "Y:\...\bin\9876543210\" /c /g /d /i /e /r /h /y /exclude:Q:exclude.txt
subst Q: /d
This could be a problem if you don't know a drive letter that is free.
3) (my favorite) Simply PUSHD do the troublesome path and run the command from there :-)
pushd "Y:\path with spaces"
xcopy X:\ "Y:\...\bin\9876543210\" /c /g /d /i /e /r /h /y /exclude:exclude.txt
popd
See https://sevenx7x.wordpress.com/2009/01/02/xcopy-with-exclude-option-shows-cant-read-file/ and http://forums.majorgeeks.com/showthread.php?t=54300 for more information.
/EXCLUDE:file switch will not exclude the file specified. As per xcopy command reference:
/exclude:FileName1[+[FileName2][+[FileName3](…)] Specifies a list of
files. At least one file must be specified.
Each file will contain search strings with each string on a separate line in the file. When any of the strings match any part of the
absolute path of the file to be copied, that file will be excluded
from being copied.
It took me some time to get this right as well (I had the same errors), but ultimately, this format worked for me. As with all things DOS, absolute precision is critical, so feel free to copy and paste the below.
xcopy /t /e "C:\Users\username\Your Folder" "C:\Users\user\Your Folder"

Find recently modified files on windows console

I need to check in cmd if there are specific files recently modified (90 min).
On linux, my command works well:
find /home/my_folder -type f -mmin -90 -name *.txt
On MS-DOS, I cannot find a way to filter regarding modification time:
forfiles /P directory /S
You can use the /C parameter:
/C command Indicates the command to execute for each file.
Command strings should be wrapped in double
quotes.
The default command is "cmd /c echo #file".
The following variables can be used in the
command string:
#file - returns the name of the file.
#fname - returns the file name without
extension.
#ext - returns only the extension of the
file.
#path - returns the full path of the file.
#relpath - returns the relative path of the
file.
#isdir - returns "TRUE" if a file type is
a directory, and "FALSE" for files.
#fsize - returns the size of the file in
bytes.
#fdate - returns the last modified date of the
file.
#ftime - returns the last modified time of the
file.
To include special characters in the command
line, use the hexadecimal code for the character
in 0xHH format (ex. 0x09 for tab). Internal
CMD.exe commands should be preceded with
"cmd /c".
Ex:- forfiles /S /M *.txt /C "cmd /c echo #path #file #fdate #ftime"
Think this might be helpful. You can use forfiles /? to see more help details.

How to get a list of files that are older than 3 days and may be several directories deep using a batch file

We have a backup directory that could have a folder structure up to 4 folders deep. Backups are automatically purged dependant on a sucessfull backup. The issue is that sometimes the backup routine falls over and we don't know that it has and no files are being backed up in some of the folders.
Can anyone shed some light on a script to be run via a batch file that would:
1) loop through all the folders
2) provide a list of all files that are older than 3 days
3) have the ability to state the file or folder names to be excluded from the report.
Thanks in advance,
Jonathan
You can pipe the FORFILES result to FINDSTR with the /V and /G:file options to filter out the files you want to ignore. You can embed the files to ignore directly in your batch script. The full path of each file should be used, with enclosing quotes to match the FORFILES output.
I use the /L option to force the search to use literal strings, the /X option to make sure the filter uses an exact match, and the /I option to make it case insensitive.
Note that #path represents the full path, including the file name. So #file is not needed.
Also, FORFILES will list folders, so you should exclude them using #ISDIR
#echo off
forfiles /p n:\ /m * /s /c "cmd /c if #isdir==FALSE echo #path" /d -3 | findstr /vixlg:"%~f0" > c:\temp\output.txt
exit /b
"n:\somePath1\someFileToIgnore1.ext"
"n:\somePath2\someFileToIgnore2.ext"
etc.
If you want to exclude all files within a specific folder, then you will need to modify the above script to use regular expressions, using \R instead of \L. You can construct a regular expression to specify a specific file, all files within a specific folder, or all files within a folder tree.
Backslash literals must be escaped as \\, and period literals escaped as \.. Excluding files within a folder uses [^\\]* to represent the files - it matches any string of characters except except backslash. Excluding a folder tree uses .* to match any string of characters to match both folders and files.
#echo off
forfiles /p n:\ /m * /s /c "cmd /c if #isdir==FALSE echo #path" /d -3 | findstr /vixrg:"%~f0" > c:\temp\output.txt
exit /b
"n:\\somePath1\\someFileToIgnore1\.ext"
"n:\\somePath2\\ignoreFilesInThisFolder\\[^\\]*"
"n:\\somePath3\\ignoreFilesInThisFolderTree\\.*"
etc.

Resources