Page 1 of 1

Small Macros - timeSince, nullDir, prt, poz

Posted: 18 Aug 2018 01:05
by CirothUngol

Code: Select all

@ECHO OFF
:: define a newline with line continuation 
(SET \n=^^^
%= This defines an escaped Line Feed - DO NOT ALTER =%
)

:: echo text to the console without CR/LF
SET "eko=<NUL SET/P="

:: print everything but % without DelayedExpansion
SET "prt=@FOR /F %%^" in ("""")DO @ECHO(%%~""
%PRT%Print &,|,<,>, or ^ without escaping! Still gotta double %% though...

:: pause with custom (or no) message
SET "poz=@FOR %%# IN (1 2)DO @IF %%#==2 (PAUSE>NUL&ECHO.)ELSE <NUL SET/P="
%POZ%Your message and pause the script until user hits a key...

:: recursively remove all empty folders from a directory tree
SET nullDir=FOR %%# IN (1 2)DO IF %%#==2 (%\n%
	FOR /F "tokens=*" %%: IN ('DIR /AD-S/B/S !##! 2^^^>NUL ^^^|SORT/R')DO RD "%%:"^>NUL 2^>^&1%\n%
	ENDLOCAL%\n%
)ELSE SETLOCAL EnableDelayedExpansion^&SET ##=%= \Path\to\Folder =%

:: %timeSince% %TIME% [%DATE% [part whole]]
:: input  - hh:mm:ss.cc [Day MM/DD/YYYY [integer integer]]
:: output - [Wwks, ][Dday, ][Hhrs, ][Mmin, ]S.Csec
:: returns time lapsed from %TIME% [%DATE%] to present in TS_ or if given
:: part/whole will return estimate of time remaining to completion in TR_.
:: raw time and day info for given(1) and current(2) are also available.
:: TM1,TM2=# of centiseconds since last midnight.
:: DY1,DY2=# of days since noon on January 1, 4713 BCE (Julian calendar).
:: usage:
:: SET "startTime=%TIME%" or "startTime=%TIME% %DATE%" before event then:
:: %timeSince% %startTime%                      - for current elapsed time.
:: %timeSince% %startTime% %partCnt% %wholeCnt% - to estimate by count.
:: %timeSince% %startTime% %partKb%  %wholeKb%  - to estimate by size.
::
SET timeSince=FOR %%# IN (1 2)DO IF %%#==2 (%=                                  'v0.2 2018/08/06 =%%\n%
%= =%SET res=^& SET var=TS_%=                                                   'res=result,var=TS_=time elapsed since start time =%%\n%
%= =%SET/A p=t=t1=tm1=t2=tm2=d=d1=dy1=d2=dy2=y=z=0%= p=counter for parameters,y==part,z=whole =%%\n%
%=  t=t2-t1=target time,t1/t2=adjusted start/current time,tm1/tm2=raw start/current time (in centiseconds)=%%\n%
%=  d=d2-d1=target day, d1/d2=adjusted start/current day, dy1/dy2=raw start/current day  (Julian calendar)=%%\n%
%= =%IF "!inp:~1,1!"==":" SET inp=0!inp!%=                                      'add leading zero if needed =%%\n%
%= =%FOR %%@ IN ("!inp!" "!TIME: =0! !DATE!")DO (SET/A p+=1%=                   'convert input and current time/day =%%\n%
%=    =%FOR /F "tokens=1-10 delims=:./ " %%A IN ("%%~@")DO IF "%%D" NEQ "" (%=  'separate string,check for TIME =%%\n%
%=       =%SET/A"tm!p!=t!p!=(((1%%A*60)+1%%B)*60+1%%C)*100+1%%D-36610100"%=     'convert time to centiseconds =%%\n%
%=       =%IF "%%H" NEQ "" (SET/A mm=100%%F%%100,dd=100%%G%%100,yy=10000%%H%%10000%= 'check for DATE,extract MM,DD,YYYY =%%\n%
%=          =%SET/A"dy!p!=d!p!=!dd!-32075+1461*(!yy!+4800+(!mm!-14)/12)/4+367*(!mm!-2-(!mm!-14)/12*12)/12-3*((!yy!+4900+(!mm!-14)/12)/100)/4")%\n%
%=       =%IF "%%J" NEQ "" SET/A y=%%I,z=%%J))%=                  'convert Gregorian to Julian,snag part/whole parameters if available =%%\n%
%= =%IF !t1! GEQ !t2! SET/A t2+=8640000,d2-=1%=                   'if start>=current we've past midnight,convert 1day=8640000cs =%%\n%
%= =%IF !d1! EQU 0 SET d2=0%=                                     'if no start date clear current date =%%\n%
%= =%SET/A t=t2-t1,d=d2-d1%=                                      'target time/day =%%\n%
%= =%FOR %%A IN (1 2 3)DO IF !z! GTR 9999999 SET/A y/=10,z/=10%=  'reduce part/whole to 7 digits =%%\n%
%= =%IF !y! LSS 1 SET y=1%=                                       'and calculate time remaining from part/whole ratio =%%\n%
%= =%IF !z! NEQ 0 SET/A"t=(z-y)*100/y*((d*8640000+t)/100),d=t/8640000,t=t%%8640000"^& SET var=TR_%\n%
%= =%SET/A w=d/7,d=d%%7,h=t%%8640000/360000,m=t%%360000/6000,s=t%%6000/100,c=t%%100%\n%
%= =%IF !c! LEQ 9 SET c=0!c!%=                                    'reduce centiseconds to months,weeks,days,hours,minutes,seconds =%%\n%
%= =%IF !w! GTR 0 SET res=!w!wks, %=                              'w=weeks =%%\n%
%= =%IF !d! GTR 0 SET res=!res!!d!day, %=                         'd=days =%%\n%
%= =%IF !h! GTR 0 SET res=!res!!h!hrs, %=                         'h=hours =%%\n%
%= =%IF !m! GTR 0 SET res=!res!!m!min, %=                         'm=minutes =%%\n%
%= =%SET res=!var!=!res!!s!.!c!sec%=                              's=seconds,c=centiseconds =%%\n%
%= =%SET res=!res!" "tm1=!tm1!" "tm2=!tm2!" "dy1=!dy1!" "dy2=!dy2!%= 'quote-separated queue of return variables =%%\n%
%= =%FOR /F "tokens=*" %%A IN (^""!res!"^")DO ENDLOCAL^& FOR %%B IN (%%A)DO SET %%B%= 'exit & cycle through return queue =%%\n%
)ELSE SETLOCAL EnableDelayedExpansion^& SET inp=

%timeSince% 21:05:32.00 Day 09/18/2017
%timeSince% 20:34:12.00 Day 08/06/2018 5 10 'ratio of 1/2 means remainingTime=elapsedTime
ECHO timeSince v0.1 created %TS_% ago.
ECHO timeSince v0.2 created %TR_% ago.
While %prt% is super neat (and I still don't quite get it) I really never use it, and it's the one I didn't write. The others make it into nearly all of my scripts. I'd been using :timeSince for years before converting it to a macro and recently added a fairly hack-ish option to estimate time-remaining from a given ratio (completed X amount of Y total in Z amount of time) to better monitor some stuff that runs for a long time (like a vidRecode script that can take days (weeks?) to complete a job). File counts are good as part/whole for tasks with uniform running times, but Kbytes are far better to monitor video encoding where running times vary wildly and are often dependent on file size.

As always give a try if it suits your fancy, and please post if you encounter any issues.

Re: Small Macros - timeSince, nullDir, prt, poz

Posted: 18 Aug 2018 05:41
by aGerman
Well done. Although timeSince won't work for me (to be honest I didn't expect it would work :wink: ).
That's the output of

Code: Select all

ECHO %date% %time%
with my German settings:
18.08.2018 13:40:08,21
As you can see the formats are DD.MM.YYYY for the date and h:mm:ss,cc for the time. But that are by far not the only other possibilities that exist. Consider to use the output of

Code: Select all

wmic os get localdatetime /value
Steffen

Re: Small Macros - timeSince, nullDir, prt, poz

Posted: 18 Aug 2018 08:56
by dbenham
CirothUngol wrote:
18 Aug 2018 01:05
While %prt% is super neat (and I still don't quite get it) I really never use it, and it's the one I didn't write.
It's clever, but actually quite simple. It works by preceding the message with a disappearing quote. The message is quoted during phase 2 parsing. But phase 4 removes the quote so it does not appear in the message when it is finally executed in phase 7.

Here are some details about the definition:
  • The FOR /F uses a quote as the FOR variable. It must be escaped as %%^" when defining the variable
  • The iterated string value is an empty quoted string ""
  • The DO clause uses %%~" which expands to nothing - The ~ removes the quotes from the value :)
Here is the actual value of the macro that is stored:

Code: Select all

@FOR /F %^" in ("""")DO @ECHO %~"
The introductory comment before your definition is misleading. Here is how I would define the macro and demonstrate its usage:

Code: Select all

@echo off
:: print poison characters without escape or quote
set "prt=@for /f %%^" in ("""")do @echo(%%~""

%prt%Print & | < > ^ without escaping!  But "quoted  ^& ^| ^< ^> ^^ must be escaped!" Still gotta double %%
if 1==1 (
  %prt%This ) does not close the parenthesized block
)
setlocal enableDelayedExpansion
%prt%Delayed expansion requires escaped ^! ^^. "Quoted requires double escaped ^^! ^^^^"
%prt%But ^ is normal if no exclamation on the line. "Quoted ^^ needs single escape"
The two @ in the definition, coupled with the absence of SETLOCAL, make the macro suitable for use from the command prompt, while ECHO is ON.

I would think it would not have much use on the command line, except it could be handy to easily get a poison character into a text file:

Code: Select all

prompt> >ampersand.txt %prt%&
Note how the redirection must be placed before the macro so that the > is not treated as a literal.


Dave Benham

Re: Small Macros - timeSince, nullDir, prt, poz

Posted: 18 Aug 2018 12:48
by CirothUngol
aGerman wrote:
18 Aug 2018 05:41
...not the only other possibilities that exist. Consider to use the output of

Code: Select all

wmic os get localdatetime /value
Neat! I've never used WMIC. When did it appear, WinXP and up? Is the output standard on all windows versions? It required elevated permissions on my machine to operate, how does one do this automatically and will it always work when deployed in a batch file?
It makes SET startTime lightly more problematic, but that could easily be solved with a %timeSet% macro.
dbenham wrote:
18 Aug 2018 08:56
1) It works by preceding the message with a disappearing quote.
2) The introductory comment before your definition is misleading.
3) I would think it would not have much use...
1) Ah... you're right, that is clever! So the closing quote isn't needed?
2) Indeed, that's how it was presented to me when I found it somewhere years ago. Man, how problematic is ^ caret?! That's why I went with |& instead of ^^ for Logical Xor when putting %MM% v0.2 together, it just much, much easier. Thanks for the extended explanation. I've read about the phases of the command interpreter before, but where's your best resource that explains this?
3) Me neither, but but I'm glad I posted it because now I know how it works! ^_^

Re: Small Macros - timeSince, nullDir, prt, poz

Posted: 18 Aug 2018 21:14
by dbenham
CirothUngol wrote:
18 Aug 2018 12:48
I've read about the phases of the command interpreter before, but where's your best resource that explains this?
The best resource is the StackOverflow answer begun by jeb, coupled with my answer that gives more detail about phases 1 (percent expansion) and 5 (delayed expansion).

The information is all derived empirically from many, many experiments. Jeb made the initial critical discoveries and outlined most of the phases. He first published the phases on a German site, but unfortunately that post is no longer available. His original StackOverflow answer from Nov 2010 was mostly an outline - largely a translation of the original German. I started working with jeb (and others) and began maintaining the answer in 2012. After 64 edits, the main answer has reached the 30k character limit :twisted:

There are many related discussions scattered within DosTips that contributed to the knowledge base. Recently I've begun a dedicated DosTips thread for future investigations.


Dave Benham

Re: Small Macros - timeSince, nullDir, prt, poz

Posted: 19 Aug 2018 03:30
by aGerman
CirothUngol wrote:
18 Aug 2018 12:48
When did it appear, WinXP and up?
I remember that I used it on XP already. But I can't recall if it was available on every XP version.
CirothUngol wrote:
18 Aug 2018 12:48
Is the output standard on all windows versions?
Yes it is.
CirothUngol wrote:
18 Aug 2018 12:48
It required elevated permissions on my machine to operate
This is weird. There are certainly options that require elevation. But I never heard about it in connection with LocalDateTime :?
CirothUngol wrote:
18 Aug 2018 12:48
how does one do this automatically and will it always work when deployed in a batch file?
It makes SET startTime lightly more problematic, but that could easily be solved with a %timeSet% macro.
Use =+- as delimiters. That way you already get rid of the Carriage Return that WMIC erroneously appends to each line.

Code: Select all

@echo off
for /f "tokens=2 delims==+-" %%i in ('wmic os get localdatetime /value') do set "t=%%i"
echo "%t%"

set "y=%t:~,4%"
set "m=%t:~4,2%"
set "d=%t:~6,2%"
set "h=%t:~8,2%"
set "n=%t:~10,2%"
set "s=%t:~12,2%"
set "c=%t:~15,2%"
echo %y%-%m%-%d% %h%:%n%:%s%.%c%

pause
We already had long discussions about how to get date and time independent from locale settings. I think most of the possibilities can be found there:
viewtopic.php?f=3&t=4555

Steffen

Re: Small Macros - timeSince, nullDir, prt, poz

Posted: 21 Aug 2018 09:48
by dbenham
CirothUngol wrote:
18 Aug 2018 12:48
So the closing quote isn't needed?
No, it isn't needed, though it does make the quoting more balanced.

The last quote is paired with the opening quote that precedes the variable name. When the SET command has a quote before the variable name, then everything from the last quote on the line to the end is ignored. If there is no final quote, then the entire line is included in the definition.

The definition could be written as:

Code: Select all

SET "prt=@FOR /F %%^" in ("""")DO @ECHO(%%~"
or without any quoting: (note the escaped caret)

Code: Select all

SET prt=@FOR /F %%^^" in ("""")DO @ECHO(%%~"
The advantage of including the closing quote is it allows appending comments after the definition, and/or it prevents unwanted trailing white space in the definition:

Code: Select all

SET "prt=@FOR /F %%^" in ("""")DO @ECHO(%%~""    'The %prt% macro allows printing without having to escape poison chars

Dave Benham