Built in commands fail when used in for loop - batch-file

I have a portion of a script that is intended to remove the ":" out of the time and "/" out of the date as returned by time /t and date /t commands. If I execute the command in a command prompt (cmd.exe), the expected values are returned:
C:\Windows\system32>time /t
02:49 PM
C:\Windows\system32>date /t
Tue 07/03/2018
If I place the same commands at the beginning of my batch script, I get the results I expect. However, if I place the same command within a for statement to parse out the ":" and "/". I receive the following messages:
'date /t' is not recognized as an internal or external command,
operable program or batch file.
'time /t' is not recognized as an internal or external command,
operable program or batch file.
Running the for command outside of the batch file works fine. I have recreated the batchfile from scratch to be sure there were no random odd characters present.
Here is the script itself:
time /t
date /t
for /F "Tokens=2,3,4 delims=/ " %%a in ('date /t') Do Set _cdate=%%c%%a%%b
for /F "Tokens=1,2,3 delims=: " %%a in ('time /t') Do Set _ctime=%%a%%b%%c
pause
This same script works fine on another server as well as my desktop. I am at a loss as to what makes this new server different. (Aside from one is 32bit) The COMSPEC is correct. (C:\Windows\system32\cmd.exe) These commands are part of a larger script to provide file name prefix for logging.

You can try this batch script using WMIC to get Date and Time not depending for locale/user settings :
#echo off
Title Get Date and Time using WMIC
Call :Get_Date_Time
echo Date : %Year%-%Month%-%day%
echo Time : %Hour%:%Min%:%Sec%
Pause>nul & Exit
::********************************************************************************************
:Get_Date_Time
for /f "skip=1" %%x in ('wmic os get localdatetime') do if not defined MyDate set "MyDate=%%x"
set "Year=%MyDate:~0,4%"
set "Month=%MyDate:~4,2%"
set "Day=%MyDate:~6,2%"
set "Hour=%MyDate:~8,2%"
set "Min=%MyDate:~10,2%"
set "Sec=%MyDate:~12,2%
exit /b
::********************************************************************************************

Related

Am I the only one who have this problem: While running something like .bat, the "X:\..\..path" often becomes ""X:\..\path and producing errors?

While running something like .bat, the "X:\..\..path" often becomes ""X:\..\path and producing errors. For example, I was installing apktool, then it just appeared this:
'""C:\Program' is not recognized as an internal or external command,
operable program or batch file.
I then copy the command and put one of the double quote to the end, which is like this: "C:\Program"
And everything just went smoothly, installation was successful. Then I tried to decode an apk, and the exactly same problem occurred: '""C:\Program' is not recognized as an internal or external command, operable program or batch file. This time I have no idea how to fix it, it's not like the .bat now, I cannot get the #echo on and copy the last command and edit it. So I am here to ask: If I am the only one who met this? Any way to fix this? Thank you.
My command:
apktool d test.apk
Image of running a decode command : 1
apktool.bat content:
#echo off
setlocal
set BASENAME=apktool_
chcp 65001 2>nul >nul
set java_exe=java.exe
if defined JAVA_HOME (
set java_exe="%JAVA_HOME%\bin\java.exe"
)
rem Find the highest version .jar available in the same directory as the script
setlocal EnableDelayedExpansion
pushd "%~dp0"
if exist apktool.jar (
set BASENAME=apktool
goto skipversioned
)
set max=0
for /f "tokens=1* delims=-_.0" %%A in ('dir /b /a-d %BASENAME%*.jar') do if %%~B gtr !max! set max=%%~nB
:skipversioned
popd
setlocal DisableDelayedExpansion
rem Find out if the commandline is a parameterless .jar or directory, for fast unpack/repack
if "%~1"=="" goto load
if not "%~2"=="" goto load
set ATTR=%~a1
if "%ATTR:~0,1%"=="d" (
rem Directory, rebuild
set fastCommand=b
)
if "%ATTR:~0,1%"=="-" if "%~x1"==".apk" (
rem APK file, unpack
set fastCommand=d
)
:load
"%java_exe%" -jar -Duser.language=en -Dfile.encoding=UTF8 "%~dp0%BASENAME%%max%.jar" %fastCommand% %*
rem Pause when ran non interactively
for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" pause
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign a terminal \, Space or " - build pathnames from the elements - counterintuitively, it is likely to make the process easier. If the syntax set var="value" is used, then the quotes become part of the value assigned.
set java_exe="%JAVA_HOME%\bin\java.exe"
Should be
set "java_exe=%JAVA_HOME%\bin\java.exe"
(apply this principle throughout your code)
Then, if you require " anywhere, insert it where it's needed - don't try to include it as part of a variable's value.
This should clean up at least some of your problems.

Automatically generated title for a TXT file for the ">" command

I'm trying to write a short batch file to monitor the data traffic hourly and write it in TXT files. My problem is that I want to give the TXT files the date and time of their creation as their filename.
I already tried to use the %date% and %time% commands, the "date /t" and "time /t" commands or to generate a file beforehand and access it afterwards, but I'm simply not well-versed enough in batch programming to access this file.
netstat -e > C:\Users\User1\Documents\%date%.%time%.txt
exit
There should be a file with the name of e.g. "20.02.2016 , 12:06:12.txt" , but I'm either getting a &date file or an error in cmd: "The syntax for filename, directory name, or volume label is incorrect.".
For /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set filedate=%%c%%a%%b)
For /f "tokens=1-2 delims=/:" %%a in ('time /t') do (set filetime=%%a%%b)
netstat -e > C:\Users\User1\Documents\%filedate=%.%filetime%.txt exit
The first for loop does date /t then is set "/" as a delimiter and reassembles the date without a delimiter.
The second for loop does time /t then using ":" as a delimiter and reassembles the time without a delimiter.
Then we do your netstat command, placing our variables into the filename.
This will not work for all locales, but the general idea is the same. If you live a locale where this solution does not work, then just adapt the code to your own needs. This works for me on my own system, in North America.

Versioning up existing files using batch

#echo off
:prep
cls
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%c-%%a-%%b)
:for /l %A in (1,1,100) do copy "C:\some folder\file.ext" "C:\some folder\file-%%A.ext"
set choice=
:: test to see if directory exists
if EXIST "../delivery_%mydate%.txt" (
goto overwrite
) else (
goto start
)
:overwrite
echo.
echo delivery note already exists - continue?
set /p choice='y / n ?'
if '%choice%'=='' ECHO "%choice%" is not valid please try again
if '%choice%'=='y' goto start
if '%choice%'=='n' goto end
echo.
:start
echo.
for /l %A in (1,1,100) do copy "C:\some folder\delivery_%mydate%.ext" "C:\some folder\delivery_%mydate%.ext"
echo Choose the following:
echo 1. Directories
echo 2. Files
echo 3. quit
echo.
set /p choice=
if '%choice%'=='1' goto directory
if '%choice%'=='2' goto file
if '%choice%'=='3' goto end
cls
ECHO "%choice%" is not valid please try again
goto start
:directory
dir /ad /on /b > ../delivery_%mydate%.txt
echo.
goto checksuccess
:file
dir /a-d /on /b > ../delivery_%mydate%.txt
echo.
goto checksuccess
:checksuccess
I need to add a line of code to this batch file I have created above. I need this code to save an existing file to a higher version without deleting the previous one. This will also need to be embedded into the code I created. For example it will start saving them like: filev001, filev002, etc.
1. Some general advice for writing batch files
A list of commands is output on executing in a command prompt window help. It is advisable to use in batch files for environment variables and labels not a string which is also a command. It is possible, but not advisable.
start is a command to start an application in a separate process. So it is better to use for example Begin instead of start as label.
choice is a command for a choice which is better for single character choices than using set /P. So it is better to use for example UserChoice instead of just choice as environment variable name.
It is better to use echo/ instead echo. to output an empty line. The reason is explained by DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/.
Environment variable names and labels are easier to read on using CamelCase and can be more easily searched case-sensitive and if necessary replaced in a batch file than a name/label which can exist as word also in comments and in strings output with echo.
The answer on question Why is no string output with 'echo %var%' after using 'set var = text' on command line? explains in detail why the usage of the syntax set "Variable=string value" is recommended in batch files on assigning a string to an environment variable.
The directory separator on Windows is the backslash character \. The slash character / is the directory separator on Unix/Linux/Mac. On Windows / is used for options/parameters. The Windows kernel functions support also directory and file paths with / as directory separator by automatically correcting them to \ internally in path. But it is nevertheless recommended to use in a batch file \ in paths.
rem is the command for a comment in a batch file. :: is an invalid label and not really a comment. Lines with a label at begin are ignored for command execution. But a label cannot be used in a command block. For that reason it is recommended to use command rem because :: in a command block results often in unexpected behavior on execution of the batch file.
2. Get current date in a specific format
Let us look on the command line:
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%c-%%a-%%b)
date /t is a command which for executes in a background command process with the command line cmd.exe /C date /t for capturing the output of this command process written to standard output handle STDOUT and process the captured output line by line.
Can this be optimized?
Yes, because on running in a command prompt window set /? and reading the output help from first to last page it can be read that there is the environment variable DATE which expands to current date. So there is no need to run the command date to get current date as string.
The command date with option /t outputs the current date in the format defined for the used user account in Windows Region and Language settings. In your case it looks like the region dependent date format is MM/dd/yyyy with the weekday abbreviation at beginning (with no comma) before the date. The date format on my computer is just dd.MM.yyyy without weekday. The environment variable DATE is in same region dependent format as output of command date /t.
So the region dependent date in format ddd, MM/dd/yyyy could be also modified to yyyy-MM-dd using the command line:
for /F "tokens=2-4 delims=/, " %%a in ("%DATE%") do set "MyDate=%%c-%%a-%%b"
It is also possible to use string substitution:
set "MyDate=%DATE:~-4%-%DATE:~-10,2%-%DATE:~-7,2%"
String substitution is also explained by help output on running set /? and read the answer on
What does %date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2% mean?
But if yyyy-MM-dd is the wanted date format for current date independent on region settings of the used user account is advisable to use the command lines
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "MyDate=%%I"
set "MyDate=%MyDate:~0,4%-%MyDate:~4,2%-%MyDate:~6,2%"
This region independent solution is really much slower than the above command lines. It is explained in detail by the answer on Why does %date% produce a different result in batch file executed as scheduled task? But it has the big advantage of being region independent.
3. Prompting user for a single character choice
The usage of set /P variable=prompt is not recommended for a single character choice because
the user can just hit RETURN or ENTER without entering anything at all resulting in variable keeping its current value or still not being defined if not defined before set /P command line;
the user can make a typing mistake and presses for example Shift+2 instead of just 2 resulting (on German keyboard) to enter " as string which most batch files using set /P breaks because of a syntax error on next command line evaluating the user input;
the user can enter anything instead of one of the characters asked for including strings which on next command line results in deletion of files and folders.
The solution is using the command choice if that is possible (depends on Windows version). choice waits for the key press of a character specified in the command options and immediately continues after one of these keys is pressed. And choice exits with the index of the pressed character in list as specified in batch file. This exit code is assigned to ERRORLEVEL which can be evaluated next also within a command block without using delayed expansion or used directly in a single goto instruction.
4. Rewritten batch file
Here is the rewritten batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem set "Folder=C:\some folder"
set "Folder=F:\Temp\Test"
:Prepare
cls
rem Get current date region independent in format yyyy-MM-dd.
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "MyDate=%%I"
set "MyDate=%MyDate:~0,4%-%MyDate:~4,2%-%MyDate:~6,2%"
set "FileNumber=0"
for %%I in ("%Folder%\file-*.ext") do call :GetFileNumber "%%~nI"
goto IncrementNumber
rem Subroutine to find out highest file number without using delayed
rem environment variable expansion for number range 0 to 2147483647.
rem Numbers starting with 0 are interpreted as octal number in number
rem comparison which makes it necessary to remove leading 0 from the
rem number string get from file name starting with 5 characters.
:GetFileNumber
set "Number=%~1"
set "Number=%Number:~5%
:RemoveLeadingZero
if "%Number%" == "" goto :EOF
if "%Number:~0,1%" == "0" set "Number=%Number:~1%" & goto RemoveLeadingZero
if %Number% GTR %FileNumber% set "FileNumber=%Number%"
goto :EOF
rem Make sure the file number has at least 3 digits.
:IncrementNumber
set /A FileNumber+=1
if %FileNumber% GEQ 100 goto ExistDelivery
set "FileNumber=00%FileNumber%"
set "FileNumber=%FileNumber:~-3%"
rem Test to see if file exists already.
:ExistDelivery
if not exist "..\delivery_%MyDate%.txt" goto Begin
echo/
%SystemRoot%\System32\choice.exe /C YN /N /M "Delivery note already exists, continue (Y/N)? "
if errorlevel 2 goto :EOF
:Begin
set "FileName=file-%FileNumber%.ext"
copy "%Folder%\file.ext" "%Folder%\%FileName%" >nul
echo/
echo Choose the following:
echo/
echo 1. Directories
echo 2. Files
echo 3. Quit
echo/
%SystemRoot%\System32\choice.exe /C 123 /N /M "Your choice? "
if errorlevel 3 goto :EOF
if errorlevel 2 goto GetFileList
dir * /AD /ON /B >"..\delivery_%MyDate%.txt"
echo/
goto CheckSuccess
:GetFileList
dir * /A-D /ON /B >"..\delivery_%MyDate%.txt"
echo/
:CheckSuccess
rem More commands.
endlocal
It was not really clear for me what the entire batch code is for at all.
It would have been also easier to write the determination of highest number in a file name on knowing the possible number range like 001 to 100. So I wrote a general solution for 001, 002, ..., 099, 100, 101, ..., 1000, ..., 2147483647.
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.
call /?
cls /?
copy /?
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
rem /?
set /?
setlocal /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?
See also answer on Single line with multiple commands using Windows batch file for an explanation of & operator and read the Microsoft article about Using command redirection operators.

Batch: Timestamp to UNIX Time

For all I know, Batch does not have a command that gives the UNIX time. The closest one I can find is %time%, which only displays the timestamp.
Is there a command, or set of commands in Batch with which you can get the UNIX time?
There's Richie Lawrence's batch library that has all those nifty handy scripts. The one you need is DateToSec (which uses GetDate and GetTime).
Here's a simplified script, that employs a little WMI:
#echo off
setlocal
call :GetUnixTime UNIX_TIME
echo %UNIX_TIME% seconds have elapsed since 1970-01-01 00:00:00
goto :EOF
:GetUnixTime
setlocal enableextensions
for /f %%x in ('wmic path win32_utctime get /format:list ^| findstr "="') do (
set %%x)
set /a z=(14-100%Month%%%100)/12, y=10000%Year%%%10000-z
set /a ut=y*365+y/4-y/100+y/400+(153*(100%Month%%%100+12*z-3)+2)/5+Day-719469
set /a ut=ut*86400+100%Hour%%%100*3600+100%Minute%%%100*60+100%Second%%%100
endlocal & set "%1=%ut%" & goto :EOF
The result will be returned into the first parameter passed to GetUnixTime, i.e. %UNIX_TIME%.
For example:
1341791426 seconds have elapsed since 1970-01-01 00:00:00
Hope it helps!
create a .bat file called "getUtime.bat"
#echo off
echo WScript.Echo(new Date().getTime()); > %temp%\time.js
cscript //nologo %temp%\time.js
del %temp%\time.js
and call like this
"C:\>getUTime"
1430894237616
What about simple 1-line long C program returning UNIX timestamp? You can retrieve value from %errorlevel% in batch script.
#include <stdio.h>
#include <time.h>
int main(void)
{
return (int) time(NULL);
}
In my test in command prompt it worked:
C:\Users\dabaran\Desktop\cs50\src\C>.\time || echo %errorlevel% && set mytstamp=%errorlevel%
1419609373
C:\Users\dabaran\Desktop\cs50\src\C>echo %mytstamp%
1419609373
There's a really simple way of doing this in a single batch file with no external scripts, files, libraries, etc.
<!-- :
for /f "tokens=* usebackq" %%a in (`start /b cscript //nologo "%~f0?.wsf"`) do (set timestamp=%%a)
echo %timestamp%
pause
exit /b
-->
<job><script language="JavaScript">
WScript.Echo(new Date().getTime());
</script></job>
The way it works is, code for a batch script AND code for a JS file are contained in the same .cmd or .bat file. you can force cscript to run the batch file as a script by commenting out the batch code and the : after the first line means batch will ignore it and run the batch code directly. so there you go!
There is no batch command for returning UNIX time. Your only options would be to write a program which could be run from a batch file that would return the UNIX time, or you could use the Windows PowerShell.
By far best solution is to download a freestanding date.exe unix-port.
Recommend that you rename it to unixdate.exe, to avoid conflict with MS Date command.
Get it from here
download & unzip. As far as I know, all the utilities are 'portable', that is, they don't require any installation program for them to work. I've only used a few (rm.exe & date.exe).
Also, you can ReName the exe's in case they might match other system utils you already have
Find the actual executable ports in the subfolder (usr\local\wbin)
To get HELP info type the command followed with --help (must spell help in lowercase)
Example:
((PROMPT)):unixdate +%Y%M%d
20141704
((PROMPT)):unixdate +%Y%b%d
2014Sep04

Create bat or script file to delete files

I would like to know how to create a bat file which on its first run would store the system date and on subsequent run delete a particular file 30 days later.I think if a bat file can be created that would store system date on its first run and the second bat files reads the first file for the date would be better.But how?
As #devio commented, PowerShell is definitely more fully featured: PowerShell Quick Reference
If it has to be a Batch file, this reference explains most commands.
I love powershell, and it is certainly more powerful than batch files, but for this it shouldnt really matter what you use, so if you're comfortable with your batch files you should be able to stick with them.
The only way you'll be able to later recover that date is to store it somewhere (or have your task running the whole time which is unrealistic - think reboots among other things)
You could write the deletion date to the registry or a text file or somewhere else that is 'known', but then you need to have something else running to check 'if its time to act'.
I'd be inclined to just create a scheduled task for the delete during the original script so that I wouldn't have to check up on it. You could even have the delete script you've scheduled clean up the task when it's done.
You could use something like a windowscripting host vb script or js script file. Also scripting languages such as php, python or perl would allow you to do something like this easily and possibly give you much greater flexibility than a shell script.
It's going to take a while to answer this one, but here's the first thing to suggest.
When you want to have a single .BAT (or .CMD) which does something and also does something later based on the first something, one can use the "flag parameter" technique. For example, in a script which accepts a wildcarded list of files to manipulate one could do as follows:
::foo.cmd
#echo off
if %1#==# goto fail
set f=%1
if %1==! goto inner
for %%x in (%f%) do cmd /c %0 ! %%x
goto done
:inner
set f=%2
echo do something with %f%
goto done
:fail
echo %0 {wildcard}
:done
The script is actually written such that it can be called anything, and it will call itself (using %0).
Now how to do date arithmetic is going to take some time to figure out. I hope that much at least gets you started in the right direction.
my setup after after completion of installation would run a bat file(once) that should get the system date(install date) and store in a text file.the main program would be called by another batch file that would read the text file every time for the date assisting it to delete particular files after "N" number of days referencing the install date in the text file.
HI MARK BRACKETT,
when I run batch1 the date is MM\DD\YYYY.But when I run batch2 the startdate is DD and the startmonth is also DD.The final equation is if rundate==nowdate execute command,it should be rundate=>nowdate,cause if pc not switched on rundate.secondly months with 31days the rundate would 31st next month
VBScript, PowerShell, or C# (I use CS-Script to run my C# scripts) would be much cleaner - but sometimes I enjoy a little batch file challenge.
So - this is for 1 month from the current date and time, but it gives you the idea. To actually figure 30 days, I suspect you'd need about 50 lines of IF statements. Or, a single external EXE to calculate it for you.
I think there's a cleaner way to use SET itself to split out the date parts, which would cut this down by about 3 lines - but I don't recall the syntax ATM.
Batch1
ECHO %DATE% > start.txt
Batch2
: Get start date
FOR /F "tokens=1* delims= " %%i IN (start.txt) DO set startDate=%%j
FOR /F "tokens=1,2 eol=/ delims=/ " %%i IN ('echo %startDate%') DO set startMonth=%%i
FOR /F "tokens=1,2 delims=/ eol=/" %%i IN ('echo %startDate%') DO set startDay=%%j
FOR /F "tokens=2,3 delims=/ " %%i IN ('echo %startDate%') DO set startYear=%%j
: Get run month and day as YYYY-MM-DD
SET /A runMonth=%startMonth% + 1
IF %runMonth% LEQ 10 SET runMonth=0%runMonth%
SET runDay=%startDay%
SET runYear=%startYear%
SET runDate=%runYear%-%runMonth%-%runDay%
: Get current month and day as YYYY-MM-DD
FOR /F "tokens=1* delims= " %%i IN ('echo %DATE%') DO set nowDate=%%j
FOR /F "tokens=1,2 eol=/ delims=/ " %%i IN ('echo %nowDate%') DO set nowMonth=%%i
FOR /F "tokens=1,2 delims=/ eol=/" %%i IN ('echo %nowDate%') DO set nowDay=%%j
FOR /F "tokens=2,3 delims=/ " %%i IN ('echo %nowDate%') DO set nowYear=%%j
SET nowDate=%nowYear%-%nowMonth%-%nowDay%
: Compare
IF %nowDate% GEQ %runDate% ECHO Delete!
Note that this doesn't handle year changes appropriately (it'll delete on the year change).

Resources