How to fetch file time which is last created using batch script - batch-file

I have one folder which contain another subfolder and that subfolder contains different extensions file.
Now I want using batch script how to extract name and time of that file in a subfolder.
I tried but failed to retrieve time of that file which is located in subfolders.
I want only this code in the batch script only.

using substitution of variable reference for /?, echo can be changed by any other command or call
command-line
for %f in (filepath\pattern) do echo "%f : %~tf"
in a script the percent sign must be double
for %%f in (filepath\pattern) do echo "%%f : %%~tf"
EDIT after comment
here an example of how to use labels to simulate function and to avoid expansion tricks, note that labels are not exactly functions they are executed in normal flow, that's why there's goto :main to skip :process and goto :eof to return.
#echo off
goto :main
:process
echo file name is: %~1 and time is %~2
goto :eof
:main
for %%x in (AppData\Local\*.*) do call :process "%%x" "%%~tx"
but with batch script command set is very limited, also should be noted that date and time output can't be formatted and are dependent of system language settings.
If powershell can be used, it's worth taking the time to learn.
Different file time properties :
$file.CreationTime
$file.CreationTimeUtc
$file.LastAccessTime
$file.LastAccessTimeUtc
$file.LastWriteTime
$file.LastWriteTimeUtc

Related

How can i call resgen in a bat file if the file is edited?

I need to create a resource file using resgen.exe only if the file is edited.
I've found a way to do it, and i need to loop through all the Language available.
This is my script.
#echo on
echo ------------------------------
echo -- Starting a run of resgen --
echo ------------------------------
Set resourcesPath=%~1Resources\
Set configuration=%~2
Set platform=%~3
set landingPath=%~1bin\%configuration%\Resources\
echo %landingPath%
IF exist %landingPath% ( echo %landingPath% exists ) ELSE ( mkdir %landingPath% && echo %landingPath% created)
set obj[0].Resource="%landingPath%Strings.en-GB.resources"
set obj[0].Text="%resourcesPath%Strings.en-GB.txt"
set obj[1].Resource="%landingPath%Strings.ru-RU.resources"
set obj[1].Text="%resourcesPath%Strings.ru-RU.txt"
set obj[2].Resource="%landingPath%Strings.es-ES.resources"
set obj[2].Text="%resourcesPath%Strings.es-ES.txt"
FOR /L %%i IN (0 1 2) DO (
for %%x in ("%%obj[%%i].Text%%") do set date_of_filenameTxt=%%~tx
for %%x in ("%%obj[%%i].Resource%%") do set date_of_filenameRes=%%~tx
ECHO %date_of_filenameTxt:~0, 16%
ECHO %date_of_filenameRes:~0, 16%
IF "%date_of_filenameTxt:~0, 16%" == "%date_of_filenameRes:~0, 16%" call :same
call :notsame
:same
(ECHO "No Copy for the :" %%obj[%%i].Text%% )
call :end
:notsame
"%resourcesPath%resgen.exe" %%obj[%%i].Text%% %%obj[%%i].Resource%%
:end
)
The problem is on getting the string from the obj[], how should be the sintax?
i've found if i do as below, it works.
call echo resource = %%obj[0].Resource%%
The task could be done much easier with the following code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
echo ------------------------------
echo -- Starting a run of resgen --
echo ------------------------------
set "resourcesPath=%~1Resources\"
set "configuration=%~2"
set "platform=%~3"
set "landingPath=%~1bin\%configuration%\Resources\"
if exist "%landingPath%" echo "%landingPath%" exists.& goto ProcessFiles
mkdir "%landingPath%"
if exist "%landingPath%" echo "%landingPath%" created.& goto ProcessFiles
echo ERROR: "%landingPath%" could not be created!& goto EndBatch
:ProcessFiles
for %%I in ("%resourcesPath%Strings.*-*.txt") do (
set "RunResgen=1"
for %%J in ("%landingPath%%%~nI.resources") do if "%%~tJ" == "%%~tI" set "RunResgen="
if defined RunResgen "%resourcesPath%resgen.exe" "%%I" "%landingPath%%%~nI.resources"
)
:EndBatch
endlocal
The outer FOR loop searches in the directory Resources for non-hidden files matching the wildcard pattern Strings.*-*.txt. There could be used also the wildcard pattern Strings.*.txt or the pattern Strings.??-??.txt.
For a found text file there is first defined the environment variable RunResgen with string value 1 whereby the value does not matter.
Next is executed one more FOR processing the resource file in landing Resources directory. If that file does not exist at all, %%~tJ expands to an empty string which means the IF condition compares "" with something like "10.11.2021 19:00" and so the condition is not true. If the appropriate .resources file exists, its last modification time is compared with the last modification time of the .txt file. If the two file time stamps are equal, the environment variable RunResgen is undefined for the next IF condition because of no need to run resgen.exe for this pair of text and resources files.
The second IF condition checks the existence of the environment variable RunResgen and if this variable still exists because of .resources file does not exist at all or has not the same last modification time as the .txt file, the executable resgen.exe is executed with the two file names.
Please note that the file date/time format depends on the country setting of the used account. On my Windows machine the date/time format is dd.MM.yyyy hh:mm and for that reason the simple string comparison works with these sixteen characters plus the two surrounding quotes taken also into account on comparing the two strings.
resgen.exe must create the .resources file with last modification date/time explicitly set to last modification date/time of the .txt file or this code does not work at all.
There is usually used on Windows the archive file attribute do determine if a source file was modified since last processing it. But I suppose this is not possible here as one .txt file could be used for multiple .resources files for multiple configurations and platforms (wherever the environment variable platform is used in real batch file).
Well, the main code could be even more optimized by using just following single line:
for %%I in ("%resourcesPath%Strings.*-*.txt") do for %%J in ("%landingPath%%%~nI.resources") do if not "%%~tJ" == "%%~tI" "%resourcesPath%resgen.exe" "%%I" "%landingPath%%%~nI.resources"
The executable resgen.exe is executed on .resources file not existing at all or its last modification date/time is different to the last modification date/time of the .txt file.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
for /?
goto /?
if /?
mkdir /?
set /?
setlocal /?
See also single line with multiple commands using Windows batch file for an explanation of the operator & to execute always a GOTO after an ECHO.

How to set a variable within a batch program into the first line of a text file and overwrite this line upon the batch file having an updated variable

Basically I want to make it so that when the player within my games makes a name, it saves the name variable into a text file. Then within the txt file, upon the loading the save file, the game will look at the text file labled "name.txt" for instance, and inside will contain the player name upon which will be extracted by some means so the new save can be loaded, but for other player variables such as wealth or location, those are dynamic variables and may need to be changed often, so what I was suggesting is that how would I constantly update this "weath.txt" file and/or "location.txt" file for instance and overwrite the previous text lines containing there previous location. As for how I would set the variable of any given player characteristic such as its name, I have used this patch of code to no avail:
#echo off
:start
set /p name=">"
(#echo off %name%)>"C:\Users\%username%\Desktop\gamedir\name.txt"
SET name=
for /f "delims=" %%i in (name.txt) do call :setname %%i
echo final name = %name%
goto exit
:Setname
Set name=%name% %1%
goto :eof
:exit
pause
rem showing result which appears to do nothing
echo %name%
As for extracting this variable upon a load game, I have absolutely no idea how to do that at all. Would appreciate any help!
A simple enough way to handle saving and loading is to store game data in a bat file, then call it when it's time to load.
The below subroutine example prefixes any variable to be saved with Save. so that it can be output using the set command within a for loop, to ensure only the desired variables are saved.
:Save
REM refresh the save file
>"%name%.bat" Echo #Echo off
REM output all requied values prefixed with Save. using the Set Command to allow easy loading
For /F "UsebackQ Tokens=* Delims=" %%S in (`"Set Save."`) Do >>"%name%.bat" Echo Set "%%~S"
REM Finalise the save with a return to the calling script
>>"%name%.bat" Echo Goto :EOF
Exit /B
:Load
REM Calls the save file which contains commands to set all relevant variables
CALL "%name%.bat"
Exit /B
You can easily support multiple characters by storing them in their own folder and using DIR to display them and an input method to select them.
`DIR "%TEMP%\GameName\characters" /B /P`

How to get a batch file only processed if it's called from another batch file?

I am coding a batch file and it needs some more files. But they files should only be able to run using the call function from another batch file. My code looks like this:
call compileData.bat
pause
I want the compilerData.bat just starts when it's called from this one, not if its just started from Explorer or something other.
Can you please help me?
I have tried to find a solution on this problem in a whole hour!
You can use a parameter.
compileData.bat:
if "%1" neq "somestring" exit /b
REM rest of your code
Another.bat:
call compileData.bat somestring
pause
I cannot think of any way that would prevent the bare "run" of the called script. Possibly that might only be done using NTFS permissions.
What you can do quickly is something like this:
MOTHERBATCH.bat
call compileData.bat SomePASSPHRASE
compileData.bat
#echo off
if not "%1"=="SomePASSPHRASE" (
echo "You can not run this script directly, please run MOTHERSCRIPT.bat."
exit /B 1
)
echo "Passphrase is correct, code is executed..."
Set an environment variable in the parent script, then if that variable is not set or doesn't have the correct value in the children, they just exit with an error message explaining they aren't intended for standalone use. You really can't prevent someone from reverse engineering the code and forcing it to run.
You could put the children in a password protected zip file and have the parent unpack it just before calling them. Then when the parent is done, it deletes the unpacked scripts.
Do all of the above.
You can use a not so well known system variable named cmdcmdline.
I will explain a brief usage for you.
For brevity's sake we will have two very simple batch files.
Parent.bat
#echo off
call compiledata.bat
And compiledata.bat
#echo off
echo %cmdcmdline%
pause
When compiledata.bat is executed on its own this variable's value is the batch file itself.
C:\WINDOWS\system32\cmd.exe /c ""C:\Batch\CALL\compiledata.bat" "
But when compiledata.bat is called from parent.bat the variable's value is that of the calling parent.bat.
C:\WINDOWS\system32\cmd.exe /c ""C:\Batch\CALL\parent.bat" "
My suggestion is putting all your batch code into a single batch file and use subroutines. Open a command prompt window and run call /? for help on how to use subroutines which is nothing else than calling a batch file being embedded in current batch file.
A simple example:
#echo off
echo Running %~f0 %*
call :compileData %*
call :WaitForUser
rem The next line results in exiting processing of this batch file
goto :EOF
:compileData
echo/
echo Running subroutine compileData with the arguments: %*
rem Exit processing subroutine compileData and continue above
rem after the command line calling the subroutine compileData.
goto :EOF
:WaitForUser
echo/
pause
rem Exit processing subroutine WaitForUser and continue above
rem after the command line calling the subroutine WaitForUser.
goto :EOF
See also Where does GOTO :EOF return to? And take a look on DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ for the explanation on using echo/ to output an empty line.
Here's my solution:
when launched from the command line, %cmdcmdline% inherits the name from the base calling program, so it wouldn't be the name of the "middle man" calling your batch file
this is what I came up with. I had to use the "subroutine" method to get the variables properly expanded
Note: Edge Case: if you use complex paths with the batch files having the same name in different folders, you could run into an "Edge Case". If that is important to you, then you might have to further parse the file names. I'm not totally sure, it wasn't my use case so I didn't go further.
#echo OFF
setlocal EnableDelayedExpansion
call :myGetFileName "%CmdCmdLine%"
if /I "%sRet%"=="%~nx0" (
echo ************** Pause
) else (
echo ************** NO Pause
)
echo finished test
pause
exit
:myGetFileName
set "sRet=%~nx1"
exit /b

How to create a unique temporary file path in command prompt without external tools? [duplicate]

This question already has answers here:
Create unique file name Windows batch
(12 answers)
Closed 7 years ago.
I am trying to create the path to a temporary file to be used in a batch file.
There are the environment variables %TEMP% and %TMP% to get the temporary directory for the current user. But how to build a file name that does surely not yet exist?
Of course I can use the built-in variable %RANDOM% and create something like bat~%RANDOM%.tmp, but this method does not ensure that the file is currently inexistent (or that it will be created coincidentally by another application, before I first create it on disk and write to it) -- although this all is very unlikely.
I know I could just reduce the probability of such collisions by appending also %DATE%/%TIME%, or by just adding multiple %RANDOM% instances, but this is not what I want...
Note: According to this post, there is a method in .NET (Path.GetTempFileName()) which does exactly what I am asking for (besides the wrong programming language obviously).
Try next code snippet:
#echo off
setlocal EnableExtensions
rem get unique file name
:uniqLoop
set "uniqueFileName=%tmp%\bat~%RANDOM%.tmp"
if exist "%uniqueFileName%" goto :uniqLoop
or create procedures
:uniqGet: create a file of a fix filename template (bat~%RANDOM%.tmp in your case);
:uniqGetByMask: create a file of a variable filename template. Note quadrupled percent signs of %random% reference in a procedure call: prefix%%%%random%%%%suffix.ext. Also note advanced usage: CALLing internal commands in call set "_uniqueFileName=%~2" inside the procedure.
The code could be as follows:
#ECHO OFF
SETLOCAL enableextensions
call :uniqGet uniqueFile1 "%temp%"
call :uniqGet uniqueFile2 "%tmp%"
call :uniqGet uniqueFile3 d:\test\afolderpath\withoutspaces
call :uniqGet uniqueFile4 "d:\test\a folder path\with spaces"
call :uniqGetByMask uniqueFile7 d:\test\afolder\withoutspaces\prfx%%%%random%%%%sffx.ext
call :uniqGetByMask uniqueFile8 "d:\test\a folder\with spaces\prfx%%%%random%%%%sffx.ext"
set uniqueFile
pause
goto :continuescript
rem get unique file name procedure
rem usage: call :uniqGetByMask VariableName "folderpath\prefix%%%%random%%%%suffix.ext"
rem parameter #1=variable name where the filename save to
rem parameter #2=folder\file mask
:uniqGetByMask
rem in the next line (optional): create the "%~dp2" folder if does not exist
md "%~dp2" 2>NUL
call set "_uniqueFileName=%~2"
if exist "%_uniqueFileName%" goto :uniqGetByMask
set "%~1=%_uniqueFileName%"
rem want to create an empty file? remove the `#rem` word from next line
#rem type nul > "%_uniqueFileName%"
exit /B
goto :continuescript
#rem get unique file name procedure
#rem usage: call :uniqGet VariableName folder
#rem parameter #1=variable name where the filename save to
#rem parameter #2=folder where the file should be about
:uniqGet
#rem in the next line (optional): create the "%~2" folder if does not exist
md "%~2" 2>NUL
set "_uniqueFileName=%~2\bat~%RANDOM%.tmp"
if exist "%_uniqueFileName%" goto :uniqGet
set "%~1=%_uniqueFileName%"
#rem want to create empty file? remove the `#rem` word from next line
#rem type nul > "%_uniqueFileName%"
exit /B
:continueScript
Output:
==>D:\bat\SO\32107998.bat
uniqueFile1=D:\tempUser\me\bat~21536.tmp
uniqueFile2=D:\tempUser\me\bat~15316.tmp
uniqueFile3=d:\test\afolderpath\withoutspaces\bat~12769.tmp
uniqueFile4=d:\test\a folder path\with spaces\bat~14000.tmp
uniqueFile7=d:\test\afolder\withoutspaces\prfx26641sffx.ext
uniqueFile8=d:\test\a folder\with spaces\prfx30321sffx.ext
Press any key to continue . . .
I suggest you one of two methods. The "technical approach" is to use JScript's FileSystemObject.GetTempName method. JScript is a programming language that comes pre-installed in all Windows versions from XP on, and its use in Batch via a "Batch-JScript" hybrid script is very simple:
#if (#CodeSection == #Batch) #then
#echo off
setlocal
for /F "delims=" %%a in ('CScript //nologo //E:JScript "%~F0"') do set "tempName=%%a"
echo Temp name: "%tempName%"
goto :EOF
#end
// JScript section
var fso = new ActiveXObject("Scripting.FileSystemObject");
WScript.Stdout.WriteLine(fso.GetTempName());
However, the simplest approach is to store a number in a data file and every time that you want a new name, get the number, increment it and store it back in the same file. This will work for "just" 2147483647 times!
rem Get next number
set /P "nextNum=" < NextNumber.txt
set /A nextNum+=1
echo %nextNum% > NextNumber.txt
set "tempName=File%nextNum%.txt"
echo Temp name: %tempName%
Firstly, using a separate folder will significantly reduce the chances of other programs intruding. So lets store the temp file in a private folder that's really long and specific to prevent any name competition.
When generating the name, you could always use %random% and try generating a name which does not exist, however the more times this operation is use, the more ineffective it becomes. If you plan to use this process 10's of thousands of times as the random function is limited to 32000 (approximately) your program will spend forever generating random attempts.
The next best approach is to start a counter at one and increase it till you have an unused name. That way you can guarantee your program will eventually find a name in a reasonable amount of time, however again your files will pile up (which is never a good experience)
What some people do (and I would recommend for your situation) is combine these to processes to effectively cut the fat in selecting a name, while using a reliable method (the best of both worlds):
#echo off
:: Set Temp Folder Path
set "tp=%temp%\Temporary Name Selection Test"
:: File name will be XXXXXX_YY.txt
:::: X's are randomly generated
:::: Y's are the incremented response to existing files
set x=%random%
set y=0
set "filename=Unexpected Error"
:loop
set /a y+=1
set "filename=%tp%\%x%_%y%.txt"
if exist %filename% goto loop
:: At this point, filename is all good
Echo %filename%
pause

Batch move "%1" to location

I'm writing a script that function as a shortcut, lets i wanna send the input to My Pictures
#echo off
echo drop input
pause
move "%1" "C:\Users\%username%\Pictures"
pause
The problem seems to be that the %1 / input needs to be described in some way.
cause when i drop a image it says
The system cannot find the file specified.
#echo off
rem Prepare environment
setlocal enableextensions disabledelayedexpansion
:loopParameters
rem Variable to hold file name
set "file="
rem If a file has been dropped on batch file, retrieve if from parameters
rem If no file dropped, ask for one
if "%~1"=="" (
set /p "file=Drop file here:"
) else (
set "file=%~1"
)
rem Check if there is something to do. If not, the process has ended
if not defined file goto endProcess
if not exist "%file%" goto endProcess
rem Do the work
echo move "%file%" "C:\Users\%username%\Pictures"
rem Determine if there is something more to process
if not "%~1"=="" if not "%~2"=="" ( shift & goto :loopParameters )
:endProcess
rem Wait to see output of commands
echo.
pause
rem Clean and exit
endlocal
exit /b
It will process files dropped into the batch file (without first starting batch file). If batch file is started without dropping any file on it , it will ask for one file to process.
If files are dropped into batch file, explorer generates the list of files to be processed as separate arguments to batch file. So first file will be in %1, second in %2, ... In this code, the file to be processed is retrieved from %1. Once the first file is processed, if there are more files to process, shift command is used to "shift" the argument list, so the content of %2 will be in %1, %3 will be in %2, ... That way, the next file to process will be again in %1 in each iteration.
BUT there is a limit of 8191 characters in the length of a command line. As explorer passes the full list of selected files in the same command line, if there is a lot of files selected, the final part of the list will not be handled.
You need the tilda in the command as shown below, as dropping quoted iems on it will fail if it doesn't have the tilda.
move "%~1" "C:\Users\%username%\Pictures"

Resources