Get arguments without temporary file

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
Jan Antonin
Posts: 9
Joined: 20 May 2018 13:53

Re: Get arguments without temporary file

#16 Post by Jan Antonin » 23 May 2018 10:49

Here my three ideas which I didnt follow yet and which may or may not be usefull.

1) if I need to run again my script this way

Code: Select all

cmd /S /D /c""%~f0" "argument inside"
it is a problem because cmd deletes the first and th last double-quotation mark and that is a problem because of argument and also because of a path of my script (even a space is problem and even an ampersand & can be used inside a path (percent sign too, it is a bigger problem)

Because of that and because cmd command will run as a part of piping I had to use a cripled version like

Code: Select all

cmd /S /D /c"%%Q%%%~f0" "argument inside""
it is also good that inside remains inside


2) piping

Code: Select all

ver|find /V ""
means that is used this calling

Code: Select all

cmd /S /D /c
if it were possible to change it somehow to

Code: Select all

cmd /U /S /D /c
we would get one char after another

3) to find a way how to use the fact that being in a pipe means being at command line mode and it means this

Code: Select all

set aa=bb
set bb=cc
set cc=dd


echo %%%%aa%%%%
means different things depending of being in a pipe or not
Last edited by Jan Antonin on 23 May 2018 19:13, edited 1 time in total.

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Get arguments without temporary file

#17 Post by Ed Dyreen » 23 May 2018 13:21

Jan Antonin wrote:
23 May 2018 10:49
Here my three ideas which I didnt follow yet and which may or may not be usefull.

1) if I need to run again my script this way

Code: Select all

cmd /S /D /c""%~f0" "argument inside"
it is a problem because cmd deletes the first and th last double-quotation mark and that is a problem because of argument and also because of a path of my script (even a space is problem and even an ampersand & can be used inside a path (percent sign too, it is a bigger problem)

Because of that and because cmd command will run as a part of piping I had to use a cripled version like

Code: Select all

cmd /S /D /c"%%Q%%%~f0" "argument inside""
it is also good that inside remains inside
Why not run your script this way ?

Code: Select all

@echo off

cmd /S /D /C ^" "tst.cmd" /arg: "ampersand EQU /S behavior" /optionalArg ^"
cmd    /D /C ^" "tst.cmd" /arg: "&&&&&&&&& EQU /S behavior" /optionalArg ^"

pause
exit

Code: Select all

@echo off

echo.
echo.inside
echo.*=%*_

exit /b

Code: Select all

inside
*=/arg: "ampersand EQU /S behavior" /optionalArg_

inside
*=/arg: "&&&&&&&&& EQU /S behavior" /optionalArg_
Druk op een toets om door te gaan. . .
Jan Antonin wrote:
23 May 2018 10:49
2) piping

Code: Select all

ver|find /V ""
means that is used this calling

Code: Select all

cmd /S /D /c
if it were possible to change it somehow to

Code: Select all

cmd /U /S /D /c
we would get one char after another

3) to find a way how to use the fact that being in a pipe means being at command line mode and it means this

Code: Select all

set aa=bb
set bb=cc
set cc=dd


echo %%%%aaa%%%%
means different things depending of being in a pipe or not
There are ways to detect whether the input was piped thus you could adjust to that How to detect if input comes from pipe or redirected file.

Jan Antonin
Posts: 9
Joined: 20 May 2018 13:53

Re: Get arguments without temporary file

#18 Post by Jan Antonin » 23 May 2018 19:02

Ed Dyreen wrote:
23 May 2018 13:21

Why not run your script this way ?

Code: Select all

@echo off

cmd /S /D /C ^" "tst.cmd" /arg: "ampersand EQU /S behavior" /optionalArg ^"
cmd    /D /C ^" "tst.cmd" /arg: "&&&&&&&&& EQU /S behavior" /optionalArg ^"

pause
exit
Because (as I said) a name of your script may be t&st.cmd or other toxic symbol and in a path too or blah blah ...
I like thinking of general solutions and then using them without thinking instead of every time has to think whether it will work or not.
And try carrets in an argument instead of ampersands.
Unimportant.

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Get arguments without temporary file

#19 Post by Ed Dyreen » 23 May 2018 20:14

Jan Antonin wrote:
23 May 2018 19:02
Because (as I said) a name of your script may be t&st.cmd or other toxic symbol and in a path too or blah blah ...
As long as you are dealing with characters inside the codepage it works.

Code: Select all

cmd /S /D /C ^" "t&st.cmd" ^"
cmd /S /D /C ^" "t^st.cmd" ^"
Jan Antonin wrote:
23 May 2018 19:02
And try carrets in an argument instead of ampersands.
Unquoted carrets get retracted by cmd before they are passed to your batch file and quoted carrets get doubled by call.

Code: Select all

@echo off
cmd /S /D /C ^" "t^st.cmd" "^^" ^^^^^^^^ ^"
call            t^^st.cmd "^^" ^^^^^^^^
pause
exit

Code: Select all

inside
*="^^" ^_

inside
*="^^^^" ^^_
Druk op een toets om door te gaan. . .
The rules are generic and really not that hard to remember but they do differ per command and so the problem is not on the receiving part but in the very nature of the commands used to get there. This is why jeb is using

Code: Select all

cmd /c tst.cmd %%var%%
to reduce the carret effect and eliminate the quote effect but then you'll also need to parse it jeb's way because %* won't work with most special chars.

Code: Select all

cmd /c tst.cmd ^^^&^^^|^^^^^^^"
works equally well and

Code: Select all

cmd /c tst.cmd ^^^^^^^&^^^^^^^|^^^^^^^^^^^^^^^"
works with %*

Code: Select all

inside
*=&|^"_
Druk op een toets om door te gaan. . .
There are some things to say for reducing the carret effect and eliminating the quote effect but jeb's technique requires a function call and involves doskey and findstr although

Code: Select all

for /F "delims=" %%L in ('doskey /macros ^|findstr /R "^return="') do (
    set "cmdLine=%%L"
)
setlocal EnableDelayedExpansion
set "args=!cmdline:*###,=!"
can be replaced with the faster

Code: Select all

for /F delims^=^ eol^= %%r in ( 'doskey /macros' ) do set "%%r"
setlocal EnableDelayedExpansion
set "args=!return:*###,=!"
variant. The other two are notoriously slow.

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Get arguments without temporary file

#20 Post by Ed Dyreen » 24 May 2018 13:46

I optimized the code a bit for macronisation, remove labels, findstr.

Code: Select all

@echo off

echo.
set "var=getParams.cmd "^^" ^^_^&^|^<^>%%#""
set "var"

call %%var%%
set args
setlocal enableDelayedExpansion
cmd /c !var!
set args
endlocal

echo.
set "var=getParams.cmd "^^" ^^^!^&^|^<^>%%#""
set "var"

call %%var%%
set args
setlocal enableDelayedExpansion
cmd /c !var!
set args
endlocal

pause
exit

Code: Select all

@echo off &(set LF=^
%=empty=%
)

for /F "tokens=3 delims=:" %%? in (

	"%~0"

) do	if %%? EQU getParams (

	setlocal enableDelayedExpansion
	:: (
		rem echo.!cmdcmdline!
		set "line=!cmdcmdline!"
		set "line=!line:"=""!"
		set "line=!line:$=$$!"
		doskey return=!line!
	:: )
	endlocal &exit 0

)

setlocal disableDelayedExpansion
:: (
	> nul break | (

		"%~d0\:getParams:\..\%~pnx0" &REM %%LF%% ) &REM ###,%*,# %%LF%%
	)

	for /F delims^=^ eol^= %%r in (

		'doskey /macros'

	) do 	set "%%r"

	setlocal enableDelayedExpansion
	:: (
		set "args=!return:*###,=!"
		set "args=!args:""="!"
		set "args=!args:$$=$!"
		set "args=!args:~0,-10!"
		doskey return=

		rem echo args: --- !args! ---
	:: )
:: )
for /F delims^=^ eol^= %%r in (

	"!args!"

) do	endlocal &endlocal &set "args=%%r"

exit /B

Code: Select all


var=getParams.cmd "^" ^^_^&^|^<^>%#"
args="^" ^_&|<>%#"
args="^" ^_&|<>%#"

var=getParams.cmd "^" ^^^!^&^|^<^>%#"
args="^" ^!&|<>%#"
args="^" ^!&|<>%#"
Druk op een toets om door te gaan. . .
When I wrote the return code I was expecting that returning to delayed state would give different results than notDelayedState, but it didn't so I left it like that.

Jan Antonin
Posts: 9
Joined: 20 May 2018 13:53

Re: Get arguments without temporary file

#21 Post by Jan Antonin » 24 May 2018 19:46

here is a final version of my script
i really think it is bulletproof
I wont even have a look here at least a couple of weeks, I need not seeing cmd prompt for a while
@jeb, @Aacini, @dbenham: thanks, it brought to my mind happy memories from my university years. Lambda calculus, algoritm theory, godels theorems, (and girls), ...
I am sad that names like Alonzo Church, S. C. Kleene, Martin Lob, Leon Henkin are not almost known and families of that great guys have a little if even something from the whole many billions dollar IT business today. It is really sad. Without them Mark Zuckerberg sticks some photos into a paper book now.

Code: Select all

@echo on
@setlocal ENABLEextensions
@setlocal DISABLEdelayedexpansion
@prompt -$G$S
@rem I just like this prompt, it plays no rule at all
@cd "%~dp0."
rem %cmdcmdline%
rem %*#
@echo(
@echo(
@set LfPromptSP=^

prompt 
@rem previous three lines cannot be deleted nor changed. One space character follows a word 'prompt' at the end
@(
echo "%%cmdcmdline:"=""%%"
rem %*#%%LfPromptSP%% prompt 
)|find /V /N ""
@echo(
@echo(
@echo the end
@pause

jeb
Expert
Posts: 1042
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Get arguments without temporary file

#22 Post by jeb » 25 May 2018 02:32

@JanAntonin

I'm missing the part where the arguments are transfered to a variable.

@Ed Dyreen
Ed Dyreen wrote:
24 May 2018 13:46
I optimized the code a bit for macronisation, remove labels, findstr.
That looks quite good, BUT to remove the findstr after 'doskey /macros' isn't a good idea, as it sets any amount of variables, for each defined macro, and it can even collide with predefined variables like CD/path ...

Code: Select all

doskey cd=echo the new dir will be $1 $T cd $1
To remove the :getParams label works, but I like to use the pattern

Code: Select all

for /F "tokens=3 delims=:" %%F in ("%~0") do goto %%F
To use it anywhere in my batch to make self-calls in sub cmd instances to different labels.

I suppose the doubling of the dollar signs is superflous, I'm expecting problems from doskey, but I tested it now and I can't find any problems.
Only the doubling of the quotes seems to be necessary, as doskey doesn't set a macro with an odd number of quotes

One thing I'm still unsatisfied with is the macro (re)definition of "return", it's possible to override an existing macro.
It would be better to take another name or even better to store the macro definition and restore it later

A revised version

Code: Select all

@echo off
for /F "tokens=3 delims=:" %%F in ("%~0") do goto %%F

REM Here the arguments will be fetched
setlocal DisableDelayedExpansion
for /F "delims=" %%L in ('doskey /macros ^|findstr /R "^return="') do (
    set "pushMacro=%%L"
)

(set LF=^
%=empty=%
)

> nul break | (%~d0\:GetParams:\..\%~pnx0
REM %%LF%% )
REM ###,%*,# %%LF%%
)

for /F "delims=" %%L in ('doskey /macros ^|findstr /R "^return="') do (
    set "cmdLine=%%L"
)
setlocal EnableDelayedExpansion
doskey !pushMacro!
set "args=!cmdLine:*###,=!"
set "args=!args:""="!"
set "args=!args:~0,-10!"

REM Now the content of %* is in args
:Main 
echo args: --- !args! ---
exit /b

REM This will be called in a cmd.exe subprocess
:GetParams
setlocal EnableDelayedExpansion
set "line=!cmdcmdline!"
set "line=!line:"=""!"
doskey return=!line!
exit

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Get arguments without temporary file

#23 Post by Ed Dyreen » 25 May 2018 10:25

jeb wrote:
25 May 2018 02:32
Ed Dyreen wrote:
24 May 2018 13:46
I optimized the code a bit for macronisation, remove labels, findstr.
That looks quite good, BUT to remove the findstr after 'doskey /macros' isn't a good idea, as it sets any amount of variables, for each defined macro, and it can even collide with predefined variables like CD/path ...
I know, I said A BIT. :P

I could have written doskey /m:macroName, but I have not decided yet on what macroName should be.

I can't implement it into my library like this because I use doskey variables to let scripts share memory asynchronously. I have doskit load, start a secondary process and then write the variable names of variables that could not be loaded from disk cache because it's missing or the macro definition changed and is out of sync to a doskey variable. When this happens unitTest is also enabled for that function, so I don't even need to enable debug when I change a macro definition. It must pass the selfTest only then selfTesting is no longer done. But no intervention is required script continues unless assertFails.

Then the secondary process detects this variable, builds the cache async, and removes it from the list. When doskit is loaded it signals the secondary process no more variables are needed and that it is safe for the child process to stop monitoring for update jobs and terminate itself if the list is completely processed. This all happens through doskey variables, in the background in low priority transparent to the calling script.

What I mean is I use disk cache loading, which is infinitely faster than defining macro's in delayed expansion ( which has the benefit that I can reuse previously defined macro's in the new macro definition easily unlike defining them notDelayed ) and then pushing them over endlocal. The cache is written twice, one version to be loaded in disDelayed and the other to be loaded in delayed, making it insensitive to the caller's delayedState.

Code: Select all

for /R "%$doskit.fullPath%\lib\notDelayed\kernel\" %%§ in ("*.CMD") do call "%%~§"
The 'Inter Script Global Variable' characteristic of doskey variables lends itself beautifully for inter-script communication and memory sharing.

Disk caching allows for "path" based classification. It helps reducing cohesion while maintaining high abstraction.
jeb wrote:
25 May 2018 02:32

Code: Select all

doskey cd=echo the new dir will be $1 $T cd $1
To remove the :getParams label works, but I like to use the pattern

Code: Select all

for /F "tokens=3 delims=:" %%F in ("%~0") do goto %%F
To use it anywhere in my batch to make self-calls in sub cmd instances to different labels.

I suppose the doubling of the dollar signs is superflous, I'm expecting problems from doskey, but I tested it now and I can't find any problems.
Only the doubling of the quotes seems to be necessary, as doskey doesn't set a macro with an odd number of quotes

One thing I'm still unsatisfied with is the macro (re)definition of "return", it's possible to override an existing macro.
It would be better to take another name or even better to store the macro definition and restore it later
In doskit I encode all variables that need to be stored in doskey memory. I can't exactly see in my code what characters exactly should be encoded because I use a function for that that basically encodes all characters that could become a problem. It's been a long time but I remember that quotes and linefeeds obviously are a problem. I really don't remember all those low level dos coding details :D

Jan Antonin
Posts: 9
Joined: 20 May 2018 13:53

Re: Get arguments without temporary file

#24 Post by Jan Antonin » 25 May 2018 16:46

jeb wrote:
25 May 2018 02:32
@JanAntonin

I'm missing the part where the arguments are transfered to a variable.
I am not sure whether I can understand the question. I cannot see any trap in doing this.
I am not able now to write a method not depending on the path and name (but I would appriciate it).
Problems are with deleted DQM (double quotation mark) after cmd command.
An universal method has to count that a name of a script or its path can contain anything allowed for NTFS. Percent signs, carrets and others.
I definitely wont code the method now.
@Ed Dyreen thanks for your effort, but it doesnt work. It is not a good example if you hard type the path. It has to be supposed that the script can be anywhere and we have information about its location inside %0 (or rather %~f0). Anything other is useless.
And I would need the method not only for a cmd command but for a start command and a left side and a right side of a pipe too.
The goal is this: we have a variable ourScript (which contains full path to the script not the script itself) and a variable ourData with data for this script (data are really inside the variable).
We would like to be able to launch the script %ourScript% for %ourData% by cmd and start with no loss or danger that something different is launched.
Something like
cmd /S /D /c""%ourScript%" "%ourData%""
start "title" "%ourScript%" "%ourData%"
%leftScript% %leftData% | %rightScript% %rightData%
- how it is for sure in many alternative universes where a density of idiots is much smaller than in our universe

and here is my really final version of the script (I dont claim that the work is done, I claim that I am done)

Code: Select all

@prompt -$G$S
@rem I just like this prompt, it plays no rule at all
@rem following is hard coded
@goto :jump
------------------------------------------------------------------------------------------
It would be prettier not using this hard coding. But I have realised that it is very very time consuming (in the meaning of my time not a performance). Because of that I will suppose this:
1) we are in a directory
c:\HH\zZ\
name.ext of this script is:
getArgumentsWithoutTemporaryFile_.cmd
measuring will be saved into a file:
params.txt
2) there is no metachars in the path - I mean percent signs, spaces and other nasty creatures
3) a command:
@(
echo "%%cmdcmdline:"=""%%"
)|find /V ""
results to:
"C:\WINDOWS\system32\cmd.exe /S /D /c"" ( echo ""%cmdcmdline:""=""""%"" )"""
what is 76 chars long  -  without a tail >)"""< and plus a new tail >& rem < one gets 76-4+6=78 chars
4) a string >###%LfPromptSP% prompt )"""< is 28 chars long
------------------------------------------------------------------------------------------
:jump
@rem it will be marked where "the original script" begins - I mean the script in the case of no complications of reading an input
@set /A runNumber=runNumber+0
@set /A runNumber=runNumber+1
@goto :thisIsRunNumber%runNumber%
@echo problem
@pause
exit
:thisIsRunNumber1
@setlocal ENABLEextensions
@setlocal DISABLEdelayedexpansion
@goto :JebsMethodForOriginalInput
:weAreBackFromMeasuringOriginaInput
@set LfPromptSP=^

prompt 
@rem previous three lines cannot be deleted nor changed. One space character follows a word 'prompt' at the end
@(
echo "%%cmdcmdline:"=""%%"
rem %1###%%LfPromptSP%% prompt 
)|cmd /S /D /c"c:\HH\zZ\getArgumentsWithoutTemporaryFile_.cmd"
@goto :EOF
:thisIsRunNumber2
@set /P inputA=
@set inputB="%inputA:~78,-28%"
@goto :JebsMethodForInputB
:weAreBackFromMeasuringInputInVariableInputB
@start "" c:\HH\zZ\getArgumentsWithoutTemporaryFile_.cmd %inputB%
@goto :EOF
:thisIsRunNumber3
rem the original script begins here
@echo "Hello World"


exit
@goto :EOF
***********************************************************************************************************
***********************************************************************************************************
:JebsMethodForOriginalInput
prompt X
(
  @echo on
  for %%b in (2) DO (
    REM original parametres plus double quotation marks on both ends are: "%*"$
)
  @echo off
)>>c:\HH\zZ\params.txt
@prompt -$G$S
@goto :weAreBackFromMeasuringOriginaInput
:JebsMethodForInputB
prompt X
(
  @echo on
  for %%b in (2) DO (
    REM          parametres                            in a variable are: %inputB:""="%$
)
  @echo off
)>>c:\HH\zZ\params.txt
@prompt -$G$S
@goto :weAreBackFromMeasuringInputInVariableInputB

sst
Posts: 93
Joined: 12 Apr 2018 23:45

Re: Get arguments without temporary file

#25 Post by sst » 25 May 2018 23:00

jeb wrote:
25 May 2018 02:32
One thing I'm still unsatisfied with is the macro (re)definition of "return", it's possible to override an existing macro.
It would be better to take another name or even better to store the macro definition and restore it later
Why not use /EXENAME switch.

Code: Select all

set "MyDosKeyNameSpace=SomeUniqueString"
doskey /exename:%MyDosKeyNameSpace% return=!line!

for /F "delims=" %%L in ('doskey /macros:%MyDosKeyNameSpace%') do (
    set "cmdLine=%%L"
)
BTW, this is the shortest example of an arg which will fail all of the proposed codes:

Code: Select all

^%LF%%LF%)

Post Reply