My Problem
As there are a ton of threads that address 'using a batch file to return file modify date' (or delete files older than, etc) - let me first specify my issue.
I'm looking to create a batch (not PowerShell, etc) that will return the last modify date of a specific file given UNC path and filename.
Code, Attempt 1
I've taken a peek at a few potential solutions on other threads, but I've run into a number of unique issues. The first and most obvious solution for this would be the "ForFiles" command in batch. For example:
set myPath=\\myUNCpath
set myFile=myFileName.csv
forfiles /p "%myPath%" /m %myFile% /c "GoTo OldFile" /d -6
Thus, if the file is older than I want -- jump to a specific section of my batch for that. However, this yields the error:
ERROR: UNC paths (\machine\share) are not supported.
However, this cmd won't work due to the use of UNC (which is critical as this batch is called by system's task scheduler). So it seems like the 'ForFiles' cmd is out.
Code, Attempt 2
I could go a more round about way of doing it, but simply retrieving the last modified date of the file (which in batch would return a string). I can truncate the string to the necessary date values, convert to a date, and then compare to current date. To do that, I've also looked into just using a for loop such as:
set myFile=\\myUNCpath\myFileName.csv
echo %myFile%
pause
FOR %%f IN (%myFile%) DO SET myFileDate=%%~tf
echo %myFileDate%
pause
Though my first debug echo provides the proper full file name, my second debug just returns ECHO is off., which tells me it's either not finding the file or the for loop isn't returning the file date. Not sure why.
I've also tried minor changes to this just to double check environmental variable syntax:
FOR %%f IN (%myFile%) DO SET myFileDate=%%~ta
Returns %ta
And finally:
FOR %%f IN (%myFile%) DO SET myFileDate=%~ta
Which without the extra '%', just crashes the batch.
I'm really at a loss at this point. So any tips or guidance would be greatly appreciated!
Using forfiles to a UNC path can be used using PushD
For just echoing the file older than x in UNC path, simply just use
PushD %myPath% &&(
forfiles -s -m %myFile% -d -6 -c "cmd /c echo /q #file
) & PopD
Here is one way how to use goto if file older than x found in UNC path using your examples.
if not exist "C:\temp" mkdir C:\temp
if not exist "C:\temp\test.txt" echo.>"c:\temp\test.txt"
break>"c:\temp\test.txt"
set myPath=\\myUNCpath
set myFile=myFileName.csv
PushD %myPath% &&(
forfiles -s -m %myFile% -d -6 -c "cmd /c echo /q #file >> c:\temp\test.txt"
) & PopD
for /f %%i in ("C:\temp\test.txt") do set size=%%~zi
if %size% gtr 0 goto next
:next
Huge thanks to both #Squashman and #MadsTheMan for the help. Luckily this batch finally is the piece of code that I can take to my boss to push switching over to at least using PowerShell!
Anyway, for those of you looking for the best way to get a UNC path file's modify date, here is what I've come up with. Not very different than other threads (and a thanks goes out to Squashman for spotting my missing " " in the for loop).
set myFile=\\myUNCpath\myFileName.ext
FOR %%f IN ("%myFile%") DO SET myFileDate=%%~tf
::And if you'd like to try to do long winded calculations you can further break the dates down via...
set fileMonth=%myFileDate:~0,2%
set fileDay=%myFileDate:~3,2%
set fileYear=%myFileDate:~6,4%
set currentDate=%date%
set currentMonth=%currentDate:~4,2%
set currentDay=%currentDate:~7,2%
set currentYear=%currentDate:~10,4%
The plan was to then convert the strings to integers, and then use a switch-case block to determine if the variance between dates was acceptable... blah blah blah.
Long story short - if you are limited to batch (and not creating secondary files -- such as a csv or the temp file suggested by MadsTheMan) you're going to have a really long script on your hands that still might have flaws (like leap year, etc unless you're adding even MORE code), when you could just make the comparison calculation using one line of code in other programs.
Related
I used this script to rename files in bulk with a unique time stamp in a batch file. The idea is to use the second and millisecond to create a unique number, which is added to the filename. The why? The filenames being used is so similar in the naming convention that it is triggering errors on the import program into the database. What doesn't trigger the error is the uniqueness of the filename, which is why I thought of a second and millisecond stamp to the prefix. Now, the code works as intended, but the stamp is not unique. It is as if the code pulls the value from the system and applies to all of the files in the folder instead of one file at a time.
Assume my file names are like this:
File1.txt
File2.txt
File3.txt
File4.txt
When the script is run, it renames the files like this:
1410File1.txt
1410File2.txt
1410File3.txt
1410File4.txt
I was hoping the script would do it like this:
1410File1.txt
1411File2.txt
1412File3.txt
1413File4.txt
The code I used to help me with this problem:
#echo off
for /f "delims=" %%a in ('wmic OS Get localdatetime ^| find "."') do set dt=%%a
set YYYY=%dt:~0,4%
set MM=%dt:~4,2%
set DD=%dt:~6,2%
set HH=%dt:~8,2%
set Min=%dt:~10,2%
set Sec=%dt:~12,2%
set Mil=%dt:~14,6%
set stamp=%Sec%%Mil%
CHDIR /D "c:\TEST\FILES\"
for %%a in (*.*) do ren "%%a" "%stamp%%%a"
Is there an additional programming code that is needed to ensure each filename is renamed differently? I'm on Windows 10 using a simple .bat file. On any given day, I would end up renaming anywhere from 600 up to 1000 files in one sitting so any help would be greatly appreciated!
Thanks in advance!
You answered your own question:
Now, the code works as intended, but the stamp is not unique. It is as if the code pulls the value from the system and applies to all of the files in the folder instead of one file at a time.
Take a look at your code. You
Compute the timestamp once at the start of the run:
for /f "delims=" %%a in ('wmic OS Get localdatetime ^| find "."') do set dt=%%a
set YYYY=%dt:~0,4%
set MM=%dt:~4,2%
set DD=%dt:~6,2%
set HH=%dt:~8,2%
set Min=%dt:~10,2%
set Sec=%dt:~12,2%
set Mil=%dt:~14,6%
set stamp=%Sec%%Mil%
cd down to the desired directory:
CHDIR /D "c:\TEST\FILES\"
iterate over the files in the directory, adding the same timestamp to each file:
for %%a in (*.*) do ren "%%a" "%stamp%%%a"
You need to recompute the timestamp on every iteration of the final loop.
I would also add a pause of a few milliseconds in the loop to ensure that at least 1 millisecond has elapsed between computations of the timestamp.
I am trying to redirect a PS output to a file and process it further.
For this I am using the Printer Port Redirection RedMon which is sending the output to CMD.exe
C:\Windows\system32\cmd.exe
As arguments I expected that something like the following should work, but it does not. "%1" contains the user input for filename.
/c >"%1"
or
/c 1>"%1"
or
/c |"%1"
or
/c > "%1" 2>&1
What almost works if I send the output to a batch file which writes it then to file.
/c WriteOutput.bat "%1"
However, the batch file is somehow altering the file (skipping empty lines, and ignoring exclamation marks and so on...)
If possible I want to avoid a batch file. Is there a way to get it "directly" to a file?
Select "Print to FILE" in the printer options is not an option for me. I want the same end result but via cmd.exe being able to process it further.
Any ideas?
Edit:
Well, that's the batch file I used. It neglects empty lines and space at the beginning.
#echo off
setlocal
set FileName=%1
echo(>%FileName%.ps
for /F "tokens=*" %%A in ('more') do (
echo %%A>>%FileName%.ps
)
Well, so far I still haven't found a direct way to write STDIN via RedMon via CMD.exe to a file. As #aschipfl wrote, all the versions with for /F will skip lines and ignore certain characters.
However, with the following batch script (via RedMon) I end up with a "correct looking" file on disk.
C:\Windows\system32\cmd.exe /c WritePS.bat "%1"
"%1" contains the user input for filename without extension.
The Batch-File WritePS.bat looks as simple as this:
#echo off & setlocal
set FileName=%1.ps
more > "%FileName%"
However,
the resulting Postscript file is different from a file which I "Print to FILE" via the Postscript-Printer setup. I am pretty sure that all the printer settings which I can set are the same in both cases.
If anybody has an idea why there might be a difference, please let me know.
I want to rename 2 files "Data1.txt" to "Log1.rtf" and "Data2.txt" to "Log2.rtf"
I am currently using this code
#ECHO OFF
FOR %%I IN ("%~dp0*.txt") DO (
SET "ext=%%~xI"
SETLOCAL EnableDelayedExpansion
RENAME "%%I" "%%~nI!Log.rtf"
ENDLOCAL
)
But it give output "data1log.rtf" and "data2log.rtf"
of course:
RENAME "%%I" "%%~nI!Log.rtf"
But it give output data1log.rtf and data2log.rtf
btw. what do you try to achive with setlocal delayedexpansion and that single ! ?
EDIT: if you insist in doing it with for (because perhaps, you have many files to rename):
#echo off
setlocal enabledelayedexpansion
for %%i in (*.txt) do (
set file=%%i
set file=!file:Data=Log!
set file=!file:.txt=.rtf!
echo ren %%i !file!
)
the first set sets the variable file to the filename
the second one replaces Data with Log
the third one replaces .txt with .rtf
then rename original filename (%%i) to the changed filename (!file!)
(the echo is there to just show the command on screen instead of really renaming files. Good for testing. If you are sure, that the code does, what you want, just remove the echo)
the setlocal enabledelayedexpansion is needed to use a variable, that is changed inside a block (between ( and )) inside the same block. Therefore you have to use the variable as !var! instead of %var%
Note: of course this code could be improved, but as a beginner: keep it as simple (and readable) as possible. For example, this code will replace Data, but not data or DATA, it will not handle spaces in filenames, ...
It might work better if you used separate code to rename each of the files, or does that defeat the object?
this website shows how to rename a file using command line:
http://www.sevenforums.com/tutorials/56013-file-folder-rename-command-prompt.html
for %%a in ("%~dp0data*.txt"
) do for /f "delims=DdAaTt" %%b in ("%%~na"
) do echo ren "%%~fa" "log%%b.rtf"
It just iterates over the indicated list of files, and for each filename, uses the unnecesary characters as delimiters for an aditional for loop. That way only the number is left and used to rename the file.
Commands to rename are echoed to console. If output is correct, remove the echo command to rename the files.
User mv command to rename files in windows (Using CLI)
As mentioned in above answer, mv command can be used in windows as well to rename the filename. I tried rename and ren commands s suggested, but I was getting error: bash: ren: command not found.
Use below to change the filename:
mv filename new_filename
I'm required to write a batch file to do a few things
Initially I thought my problem was very simple - capture the modified date of a txt file located in a specified directory, compare that date to the current date and if they are the same do something. If they are not then do something else.
The line I use to capture the current date is:
%date%
The lines I use to capture the modified date of my specified file is:
SET filename="C:\New Folder\New.txt"
FOR %%f IN (%filename%) DO SET filedatetime=%%~tf
ECHO %filedatetime:~0,-6% >> %destination%
In the above case I'm simply using echo to see what is returned and it seems as if the date is returned but I get extra information:
2012/02/19 02
I would like to know how to get the above values where they are comparable as well as how to compare them properly.
Working with dates is much harder in batch then it ought to be.
There is one command that can make your job easy in this case. FORFILES has the ability to process files that have been modified since a particular date. Use FORFILES /? from the command line to get documentation on its use.
This simple command will list all files that have been modified today:
forfiles /m * /d 0
If at least one file is found, then ERRORLEVEL is set to 0, else ERRORLEVEL is set to 1.
You have a specific file, so you can use
forfiles /m %filename% /d 0
if %errorlevel% == 0 (
echo The file was modified today
REM do whatever else you need to do
) else (
echo The file has not been modified today
REM do whatever else you need to do
)
There is a more concise way to do the above. The && operator is used to conditionally execute commands if the prior command was successful, || is used to conditionally execute commands if the prior command failed. However, be careful, the || commands will also execute if the && command(s) failed.
forfiles /m %filename% /d 0 && (
echo The file was modified today
REM do whatever else you need to do
) || (
echo The file has not been modified today
REM do whatever else you need to do
)
I like dbenham's way, but if you want to make your code work you can do like this:
set currentDate=%date%
SET filename="C:\MyFile.txt"
FOR %%f IN (%filename%) DO SET filedatetime=%%~tf
IF %filedatetime:~0, 10% == %currentDate% goto same
goto notsame
:same
echo Dates the same, do some code here
goto next
:notsame
echo Dates NOT the same, do some code here
goto end
:next
Thought it would be worth knowing how to make yours work just in case you need it again.
Apart from the issues raised in the comments appended to the TechNet article that documents ForFiles ForFiles Documentation on Microsoft TechNet, there is another issue that is mentioned, but only if you read between the lines regarding time zone being ignored. Since ForFiles evaluates the Modified date reported in Local time, it will treat a file modified at 02:01 EST as older than a file modified at 02:59 EDT.
dbenham way worked but I noticed forfiles errorlevel is supposed to be 1 for errors so revised to be this:
forfiles /p C:\Users\[path] /m "special_file.txt" /d 0
IF ERRORLEVEL 1 (echo boo unsucessful and old file) else (echo yay sucessful and updated today)
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/forfiles
https://ss64.com/nt/errorlevel.html
I need to make a script that automates the following:
Read comma or tab separated values from the input file
Construct a command from them and run it
Get the command output, looks for substrings in it and update the log file based on the existence of a substring
I do this in Windows Server 2008, so I can do this in CMD or PowerShell but I am not sure they provide such possibilities. I can try Perl or C#, but I want to try a minimalistic approach first.
Minimalistic as far as coding - Perl
Minimalistic as far as installing new software - PowerShell (IIRS W.S.2008 included that?)
So many answers, and none providing a solution that would meet the requirements...
You didn't say what are the conditions to be checked against each CSV row, and what the CSV would be like, and what the log would be like - so I made it all up... Here's an example in BATCH:
#echo off
set csvfile=input.csv
set logfile=output.log
for /F "tokens=1,2,3 delims=," %%a in (%csvfile%) do call :processline "%%a" "%%b" "%%c"
exit /B 0
:processline
set param=%~3
set check=%param:um=%
rem if they are not equal - substring 'um' exists in it.
if not "$%check%" == "$%param%" (
rem this passes all params to output.
rem I'm not calling echo directly here, because there might be symbols, that will confuse CMD at the end of the %*.
call :output %*>> %logfile%
)
exit /B 0
:output
set colA=%~1
set colB=%~2
set colC=%~3
rem output to log
echo [%DATE% %TIME%] (%colB%) %colA% %colC%.
exit /B 0
Here's the example input file that I tested it with:
foo,1,dum
bar,3,dim
baz,15,dirum
And here's the resulting log messages:
[2009-10-14 14:57:35.87] (1) foo dum.
[2009-10-14 14:57:35.89] (15) baz dirum.
I hope this shows clearly, that BATCH is not nasty nor it is hard to use. :P
If you have further question about BATCH - don't be shy, post them all on SO. ;)
I would recommend going with Python (or Perl if you swing that way). These are very minimal tools to have to install on a machine and add all the functionality you need.
The string handling you describe is unpleasant in any shell (Bash included) unless you are using sed or awk... and that just gets esoteric. In the end you'll retain more hair if you go straight to a scripting language first.
Perl was called into existence to quickly solve these kind
of tasks. It should not take more 20 lines for this
particular problem.
It is also really easy to install:
Download ActivePerl (17.7 MB, Perl 5.10.)
Run the installer.