@echo off
::
:: Optional name
::
set name=MyDate
::
setlocal
::
:: Setup script execution (needs no revision)
::
set "about=about:<script language=VBS>"
set fso=CreateObject("Scripting.FileSystemObject")
set fso=%fso%.GetStandardStream(1).WriteLine
::
:: This is the part that constructs the VBScript (revise as needed)
::
set "script=d=date:"
set "script=%script% %name%=year(d)&right(month(d)+100,2)&"
set "script=%script% right(day(d)+100,2)"
::
:: Runs the script and collects result (where the magic happens)
::
for /f "delims=" %%I in (
'mshta.exe "%about%%script%:%FSO% %name%:close:resize 0,0</
script>"'
) do endlocal & set %name%=%%I
::
:: Just here to demonstrate results
::
call echo For example, the date is %%%name%%%
Multi-statement scripts are possible by separating statements with a
colon. JScript syntax can be used also, but I'm a VBS person. Just
for completeness, here is the JScript version ...
@echo off
::
:: Optional name
::
set name=MyDate
::
setlocal
::
:: Setup script execution (needs no revision)
::
set "about=about:<script>"
set fso=new ActiveXObject("Scripting.FileSystemObject")
set fso=%fso%.GetStandardStream(1).WriteLine(%name%)
::
:: This is the part that constructs the JScript (revise as needed)
::
set "script=var d=new Date();"
set "script=%script% var %name%=d.getFullYear().toString(10)+"
set "script=%script% (d.getMonth()+100).toString(10).substr(1,2)+"
set "script=%script% (d.getDate()+100).toString(10).substr(1,2);"
::
:: Runs the script and collects result (where the magic happens)
::
for /f "delims=" %%I in (
'mshta.exe "%about%%script%;%FSO%;close();resize(0,0)</script>"'
) do endlocal & set %name%=%%I
::
:: Just here to demonstrate results
::
call echo For example, the date is %%%name%%%
Note that the statement separator in JScript is the semicolon.
_____________________
Tom Lavedas
This is one more hybrid of batch and jscript. Not a new one but still
working:
@set @x=0 /*
@echo off
:: start batch part
echo Today's date is: %date%
cscript //nologo //e:jscript %~nx0
exit /b */
// start Jscript part
var d;
var s = "Today's date is: ";
d = new Date();
s += (d.getMonth() + 1) + "/";
s += d.getDate() + "/";
s += d.getFullYear();
WScript.Echo(s);
> I've been interested in the subject for quite a while. The 'holy grail"
> has always been to find a way to have the script and batch all in one
> file. The common solution is to create a temporary file to hold the
> script. Recently I ran across a technique posted in an MS forum that I
> thought might be of interest here. I'd give the author credit, but he
> is Japanese and I only have his Kanji moniker. Anyway, his approach
> uses mshta.exe as the script host, rather than cscript.exe. The beauty
> of his approach is that mshta accepts script input from its command
> line.
So does awk/gawk - that language is primary for text processing and
report generation, but is nearly a full blown HLL, except that it's
interpreted instead of compiled. (I know about awk compilers.)
Simple, short programs can go on the command line; long script need
files. Features include automatic parsing of input lines into fields,
associative arrays, regular expressions, and simple ways to do simple
things as well as complex ways to do complex things.
The manual is at http://www.gnu.org/manual/gawk/gawk.html
--
Ted Davis (tda...@mst.edu)
That's interesting Tom. Thanks.
01MDM (Dimitry?) replied to you too - this modification of his code
doesn't require exit /b to make it slightly more straight forward.
@set @x=0 /*
@echo off
:: start batch part
echo Today's date is: %date%
cscript //nologo //e:jscript %~nx0
pause
goto :EOF
*/
// start Jscript part
var d;
var s = "Today's date is: ";
d = new Date();
s += (d.getMonth() + 1) + "/";
s += d.getDate() + "/";
s += d.getFullYear();
WScript.Echo(s);
--
Regards,
Mic
And a further variation on the theme to make it fully equivalent to
the routine I posted ...
@set @x=0 /*
@echo off
:: start batch part
echo Today's date is: %date%
for /f "delims=" %%I in (
'cscript /nologo /e:jscript "%~f0"'
) do set result=%%I
echo Today's date is: %result%
goto :EOF
*/
// start Jscript part
var d = new Date();
var s = d.getFullYear() + "_";
s += (d.getMonth() + 101).toString(10).substr(1,2) + "_";
s += (d.getDate() + 100).toString(10).substr(1,2);
WSH.Echo(s)
I also realized the original might be valuable as a support utility,
if constructed to evaluate an expression input as a command line
argument ...
:: Evaluate.cmd
@echo off
setlocal
set "cmd=CreateObject("Scripting.FileSystemObject")"
set "cmd=%cmd%.GetStandardStream(1).WriteLine %~1"
mshta.exe "about:<script language=VBS>%cmd%:close:resize 0,0</
script>"
endlocal
Then it could be invoked something like this ...
setlocal
set "expression=year(date)&right("0"&month(date),2)"
set expression=%expression%&right("0"&day(date),2)"
for /f "delims=" %%a in (
'evaluate "%expression%"') do endlocal & set result=%%a
echo Today's date is: %result%
Assuming Evaluate.cmd is placed in a folder named on the path.
_____________________
Tom Lavedas
>d = new Date();
>s += (d.getMonth() + 1) + "/";
>s += d.getDate() + "/";
>s += d.getFullYear();
Gives ugly variable-width fields.
Consider something like
var s = "Today's date is: ", d = new Date();
s += d.getFullYear()*1e4 + d.getMonth()*1e2 + d.getDate() + 100
s = s.replace(/(..)(..)$/, "-$1-$2") // ... 2011-02-09
--
(c) John Stockton, nr London, UK. ?@merlyn.demon.co.uk Turnpike v6.05.
Website <http://www.merlyn.demon.co.uk/> - w. FAQish topics, links, acronyms
PAS EXE etc. : <http://www.merlyn.demon.co.uk/programs/> - see in 00index.htm
Dates - miscdate.htm estrdate.htm js-dates.htm pas-time.htm critdate.htm etc.
Hmm - and transplanted from Tom's post in
microsoft.public.win2000.cmdprompt.admin
(a CHOICE.EXE substitute)
> @set @x=0 /* Batch part
> @echo off
> setlocal
> set "Input="&set delay=10000 %/ milliseconds /%
> set /p Input=Enter data here: < nul
> for /f "delims=" %%I in (
> 'cscript //nologo //e:jscript "%~f0" %delay%'
> ) do endlocal & set "Input=%%I"
> if not defined input echo.
> echo.For example: "%Input%"
> goto :EOF
>
> Jscript part */
> var d=WSH.Arguments(0);var t=0;
> with(new ActiveXObject("WScript.Shell")){
> with(Exec('%comspec% /c(set /p _#=<con&set _#)')){
> while(Status==0){
> WSH.Sleep(50);t+=50;if(t>d)Terminate()};
> if (!StdOut.AtEndofStream) {
> WSH.Echo(StdOut.ReadLine().substr(3))}}}
--- which seems to be a useful start.
Now - suppose the Jscript (or WSH equivalent) was changed to accept a single
keypress (this script requires CR)
Then perhaps some enthusiast could, after the SETLOCAL, provide some
interpretation of %* to establish timeout, default and available-choices.
This could even be done by applying %* to the parameter-list delivered to
the Jscript - but that might resurrect the evil spectre of
encapsulated-QBASIC-described-as-batch laid to rest many years ago...)
But - the rules about this structure.
The first line - it sets up the value of "@x" to some dummy value from the
BATCH point of view. I noticed that Uncle Bill barfed at "@@" or "x" as the
variable-name. Evidently the key is to have line 1 valid syntax in both
Jscript and batch. What's the magic about Jscript's view of that line?
Then the section /* .. */ appears to be a Jscript comment. Fair enough - get
batch to go to EOF or EXIT immediately before.
But could the file contain more than one Jscript section - or would some
sleight-of-hand have to be used to achieve that (such twisted methodology
being quite beyond my experience...)
And does the Jscript have to be at the end of the file (where
@set @x=0 /* Batch part
...batch...
goto :EOF
Jscript part */
...Jscript
@set @x=0 /* More-Batch part
...more batch...
goto :EOF
*/
is silly)
Interesting questions. This approach was first suggested by 01MDM,
but my research since shows that the @SET @x=0 part is a conditional
compilation instruction for JScript. As you say, it is a statement
that is interpreted as valid by both batch and JScript. It is used to
introduce the "/*" designation that starts a JScript comment to house
the batch script part in a way that is ignored by the JScript
compiler. I forgot to think about its effect on the command console
environment.
In response to one of your questions, I explored the JScript
conditional compile instructions and found the an @IF can be used, as
well. In this way, I found that the setting of a dummy environment
variable can be avoided - AND that the order of the Jscript and batch
parts can be reversed, as in this little test script ...
@if (@X)==(@X) @goto :Batch @end
WSH.Echo("JScript")
/*
:Batch
@echo off
echo Batch
cscript //nologo //e:jscript "%~f0"
::*/
Note that making the @IF conditional false [(@X)==(@Y), for example]
in a batch file sense, means that the batch is to be placed before the
JScript, since the GOTO part would not get executed.
Regarding the single keypress issue. Unfortunately, after many years
of trying, I can't figure out how to do that. AFAIK, all of the WSH
script input functions and methods expect a new line or end-of-file to
be issued before they return control to the script. This is even true
of the Read(n) formulation that purports to read as little as a single
character at a time. My latest effort only acts to short terminate
the process after a timeout. Any incomplete keyboard entry (lacking
the Enter key) is lost. Though I consider this a shortcoming, I was
more than pleased to have figured out how to build a timeout into the
process. I just haven't figured out how to make it a single key press
(yet?).
As far as multiple JScript functionality, that would seem to me to
only be limited by the skill of the JScript programmer. Certainly
multiple functions can be programmed and command line arguments used
to access a particular function, depending on the argument provided.
However, that seems to me to be beyond the scope of this news group.
The input arguments handling is also one that can be supported, but
seems to me to be pushing the envelop of a batch ng a little.
___________________
Tom Lavedas
> @if (@X)==(@X) @goto :Batch @end
Nice work Tom. I struggled with that last year trying to come up with
a non-intrusive method but I failed.
Frank
It is possible to start the process in a new thread batch-based (in
the same window), with start /b or with a pipe.
call myBatch :timeoutThread | call myBatch :InputThread
or
start /b myBatch :InputThread
Then the timeout-thread could kill the input-thread, but in my opinion
this is not neccessary,
because the main-thread can work parallel, while receiving key-events
(with a temp-file).
But I also don't know a good (pure) way to read a single key (with/or
without timeout).
jeb
I am getting " > is unexpected at this time for the first hybrid
batch.
Andy
This line was wrapped in transmission ...
'mshta.exe "%about%%script%:%FSO% %name%:close:resize 0,0</
script>"'
It needs to be all on one line.
______________________
Tom Lavedas
The use of asynchronous threads is in fact the way I got the timeout
to work. The Exec() operation runs asynchronously to the rest of the
JScript. The timeout is achieved by terminating the Exec() process
after the timeout period has elapsed. I don't see how having the
JScript part run asynchronously from the batch part would help.
I have since figured out a way to terminate the process, while still
collecting any input provided up to the timeout period. It uses the
SendKeys method to terminate the process gracefully, rather than the
first approach ...
@if (@X)==(@Y) @goto :Dummy @end/* Batch part
@echo off
set "Input="&setlocal&set delay=3000 %/ milliseconds /%
set /p Input=Enter data here: < nul
for /f "delims=" %%I in (
'cscript //nologo //e:jscript "%~f0" %delay%'
) do endlocal & set "Input=%%I"
if not defined input echo.
echo.For example: "%Input%"
goto :EOF
Jscript part */
var d=WSH.Arguments(0),t=0,s='';
var sh=new ActiveXObject("WScript.Shell");with(sh){
with(Exec('%comspec% /cset /p _#=<con&set _#')){
while(Status==0){WSH.Sleep(50);t+=50;
if(t>d){sh.Sendkeys('{enter}');break}};
while(!StdOut.AtEndOfLine)s+=StdOut.ReadLine()}}
WSH.Echo(s.substr(3))
And, in fact, I have a routine that accepts a single character input
without requiring Enter, but it has a major flaw, which I have yet to
overcome - It causes the console to scroll with empty lines until
input is provided or the timeout has elapsed.
@if (@X)==(@Y) @goto :Dummy @end/* Batch part
@echo off
setlocal
set "Input="&set delay=3000 %/ milliseconds /%
set /p Input=Enter data here: < nul
for /f "delims=" %%I in (
'cscript //nologo //e:jscript "%~f0" %delay%'
) do endlocal & set "Input=%%I"
if not defined input echo.
echo.For example: "%Input%"
goto :EOF
Jscript part */
var d=WSH.Arguments(0);var t=0,s='';
var sh=new ActiveXObject("WScript.Shell");with(sh){
while(s==''){
with(Exec('%comspec% /cset/p_#=<con&set _#')){
while(Status==0){
WSH.Sleep(50);t+=50;sh.SendKeys('{enter}')};
while(!StdOut.AtEndOfLine)s=StdOut.Read(4);
s=s.substr(3);if(t>d){break}}}}
WSH.Echo(s)
The solution would seem to require a means of repositioning the cursor
on the console, but that cannot be accomplished without a third party
utility (AFAIK). That defeats the idea of finding an intrinsic
solution and it would seem more useful to just bite the bullet and
find a Choice.exe replacement.
_____________________
Tom Lavedas
^ That defeats the idea of finding an intrinsic
^ solution and it would seem more useful to just bite the bullet and
^ find a Choice.exe replacement.
I haven't looked in several months but I think CHOICE.EXE is again
in ...\system32.
Frank
I's not in XP.
_____________________
Tom Lavedas
OK, I found another way. Here is my routine for single character
input, without requiring an Enter ...
@if (@X)==(@Y) @goto :Dummy @end/* Batch part
@echo off
set "Input="&setlocal&set delay=3000 %/ milliseconds /%
set _d=>%temp%.\tmp1&set _d=>%temp%.\tmp2
set /p Input=Enter data here: < nul
for /f "delims=" %%I in (
'cscript //nologo //e:jscript "%~f0" %delay%'
) do endlocal & set "Input=%%I"
del %temp%.\tmp?.
echo.
echo.For example: "%Input%"
goto :EOF
Jscript part */
var con=new ActiveXObject("Scripting.filesystemobject")
.opentextfile("con",2);
var d=WSH.Arguments(0);var t=0,s='\x01';
var cmd="xcopy %temp%.\\tmp1 %temp%.\\tmp2 /p /y < con";
var sh=new ActiveXObject("WScript.Shell");
with(sh){with(Exec("%comspec% /c"+cmd)){
while(Status==0&&s=='\x01'){s='\x01';
WSH.Sleep(240);t+=250;if(t>d){s='Timeout';break};
sh.sendkeys("^A");
while(s!='?')s=StdOut.Read(1);StdOut.Read(1);
s=StdOut.Read(1)}
sh.sendkeys("n")}}
con.Write(s); // shows keyed character [optional]
WSH.Echo(s)
It uses some slight of hand with XCOPYs /P (prompt) switch - that is,
it's a kludge, of sorts. Getting the timeout feature was the hardest
part. It can handle all printable characters, plus most of the
control characters. The problem characters that I know of, so far,
are the Ctrl-A, Ctrl-C and Ctrl-M. The last one isn't really a
problem, it just acts exactly the same as the Enter key - which
returns and empty result. This is different than a time out which
returns the string "Timeout". Otherwise, it returns the character, so
so called 'poison' characters require special handling.
Note that this can be used to create an input procedure that can
obscure the input by outputting a filler character, such as an
asterisk. The following is a variation on the theme to illustrate the
point (no timeout)...
@if (@X)==(@Y) @goto :Dummy @end/* Batch part
@echo off
set "PW="&setlocal
set _d=>%temp%.\tmp1&set _d=>%temp%.\tmp2
set /p Input=Enter data here: < nul
:loop
set "Input="
for /f "delims=" %%I in (
'cscript //nologo //e:jscript "%~f0"'
) do set "Input=%%I"
if not "%Input%"=="" set "PW=%PW%%Input%" & goto Loop
endlocal & set PW=%PW%
del %temp%.\tmp?.
echo.
echo.For example: "%PW%"
goto :EOF
Jscript part */
var con=new ActiveXObject("Scripting.filesystemobject")
.opentextfile("con",2);
var cmd="xcopy %temp%.\\tmp1 %temp%.\\tmp2 /p /y < con";
var s='',sh=new ActiveXObject("WScript.Shell");
with(sh){with(Exec("%comspec% /c"+cmd)){WSH.Sleep(10);
while(s!='?')s=StdOut.Read(1);StdOut.Read(1);
s=StdOut.Read(1);
if('nyNY'.indexOf(s)==-1)sh.sendkeys("n")}};
if(s>'\x20')con.Write("*"); // echos character to screen [optional]
WSH.Echo(s);
_____________________
Tom Lavedas
I'm impressed, it's nearly a perfect solution, only the sendkey could
cause problem with other windows/focus.
I'm implement it now with pure batch, currently the timeout or end
seems to be a little bit tricky.
I never though of the xcopy /P, and your solution is so simple.
You make my day
jeb
> I'm impressed, it's nearly a perfect solution, only the sendkey could
> cause problem with other windows/focus.
>
> I'm implement it now with pure batch, currently the timeout or end
> seems to be a little bit tricky.
>
> I never though of the xcopy /P, and your solution is so simple.
>
> You make my day
> jeb
Thanks for the compliments. I remembered using that XCOPY trick to
parse strings back in the Win9X era and pressed in back into service
here in a modified form.
I also thought about your concern about losing focus and remembered
that the Exec method has a ProcessID property and that AppActivate can
use a PID, so I might propose the following to mitigate the
possibility of keystrokes going astray out of SendKeys ...
@if (@X)==(@Y) @goto :Dummy @end/* Batch part
@echo off
set "Input="&setlocal&set delay=3000 %/ milliseconds /%
set _d=>%temp%.\tmp1&set _d=>%temp%.\tmp2
set /p Input=Enter key here: < nul
for /f "delims=" %%I in (
'cscript //nologo //e:jscript "%~f0" %delay%'
) do endlocal & set "Input=%%I"
del %temp%.\tmp?.
echo.
echo.For example: "%Input%"
goto :EOF
Jscript part */
var con=new ActiveXObject("Scripting.filesystemobject")
.opentextfile("con",2);
var d=WSH.Arguments(0),t=0,s='\x01';
var cmd="xcopy %temp%.\\tmp1 %temp%.\\tmp2 /p /y<con";
var sh=new ActiveXObject("WScript.Shell");
with(sh){with(Exec("%comspec% /c"+cmd)){
while(s=='\x01'){s='\x01';
WSH.Sleep(240);t+=250;
sh.AppActivate(ProcessID);sh.sendkeys("^A");
while(s!='?')s=StdOut.Read(1);StdOut.Read(1);
s=StdOut.Read(1);if(t>d){s='Timeout';break}}
sh.AppActivate(ProcessID);sh.sendkeys("n")}}
con.Write(s); // shows keyed character [optional]
WSH.Echo(s)
__________________
Tom Lavedas