foolproof counting of arguments

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: foolproof counting of arguments

#31 Post by dbenham » 15 Feb 2012 11:36

Liviu wrote:
jeb wrote:But piping do the same as the exit, as the pipe itself creates two seperate sub-tasks for each side.
The prompt disappears in the child task which is never visible.
Haven't followed this in any depth, but it doesn't look quite the same. The pipe closes both ends without any obvious 'exit' being typed in and/or piped through.
jeb wrote:So the pipe doesn't solve the problem this way.
Streams 3/4 redirection is foggy enough to me that I can't even spell out what the "problem" might be, let alone solve it ;-)

I think I understand what jeb is saying. The pipe is not restoring the streams. It forces the stream manipulation to occur in a child CMD.EXE process that leaves the parent CME.EXE streams alone.

--------------------

Redirection is one wacky world :lol:

I haven't fully analyzed the results to arrive at a pattern yet. But here are some interesting test cases with results.

Code: Select all

@echo off
if "%~1" neq "" goto :test
prompt $g
cls
for /l %%n in (1 1 21) do (
  del file?.stream 2>nul
  echo(
  echo =====================================================
  cmd /c "test %%n"
  echo(
  for %%f in (file*) do (
    echo(
    echo %%f contents
    echo -----------------------------
    type %%f
  )
)
del file?.stream 2>nul
prompt
exit /b


:test
echo on & call :test%1
echo echo to stream1
>&2 echo echo to stream2
>&3 echo echo to stream3
>&4 echo echo to stream4
exit /b

:test1
echo off
exit /b

:test2
echo off 1>file1.stream
exit /b

:test3
echo off 2>file2.stream
exit /b

:test4
echo off 3>file3.stream
exit /b

:test5
echo off 4>file4.stream
exit /b

:test6
echo off 1>file1.stream 2>file2.stream 3>file3.stream 4>file4.stream
exit /b

:test7
echo off 1>file1.stream 2>file2.stream 3>file3.stream
exit /b

:test8
echo off 2>file2.stream 3>file3.stream 4>file4.stream
exit /b

:test9
echo off 1>file1.stream 3>file3.stream 4>file4.stream
exit /b

:test10
echo off 1>file1.stream 2>file2.stream 4>file4.stream
exit /b

:test11
echo off 1>file1.stream 2>file2.stream
exit /b

:test12
echo off 1>file1.stream 3>file3.stream
exit /b

:test13
echo off 1>file1.stream 4>file4.stream
exit /b

:test14
echo off 2>file2.stream 3>file3.stream
exit /b

:test15
echo off 2>file2.stream 4>file4.stream
exit /b

:test16
echo off 3>file3.stream 4>file4.stream
exit /b

:test17
echo off 1>nul 2>nul 3>file3.stream 4>file4.stream
exit /b

:test18
echo off 1>nul 2>nul 3>file3.stream
exit /b

:test19
echo off 1>nul 2>nul 4>file4.stream
exit /b

:test20
echo off 1>nul 3>file3.stream
exit /b

:test21
echo off 2>nul 4>file4.stream
exit /b


Results:

Code: Select all

=====================================================

>echo off
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


=====================================================

>echo off  1>file1.stream
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


file1.stream contents
-----------------------------

=====================================================

>echo off  2>file2.stream
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


file2.stream contents
-----------------------------

=====================================================

>echo off  3>file3.stream
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


file3.stream contents
-----------------------------

=====================================================

>echo off  4>file4.stream
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


file4.stream contents
-----------------------------

=====================================================

>echo off  1>file1.stream 2>file2.stream 3>file3.strea
echo to stream3
echo to stream4


file1.stream contents
-----------------------------

file2.stream contents
-----------------------------

file3.stream contents
-----------------------------
echo to stream1

file4.stream contents
-----------------------------
echo to stream2

=====================================================

>echo off  1>file1.stream 2>file2.stream 3>file3.strea
echo to stream2
echo to stream3


file1.stream contents
-----------------------------

file2.stream contents
-----------------------------

file3.stream contents
-----------------------------
echo to stream1
echo to stream4

=====================================================

>echo off  2>file2.stream 3>file3.stream 4>file4.strea
echo to stream1
echo to stream4


file2.stream contents
-----------------------------

file3.stream contents
-----------------------------
echo to stream2

file4.stream contents
-----------------------------
echo to stream3

=====================================================

>echo off  1>file1.stream 3>file3.stream 4>file4.strea
echo to stream2
echo to stream4


file1.stream contents
-----------------------------

file3.stream contents
-----------------------------
echo to stream1

file4.stream contents
-----------------------------
echo to stream3

=====================================================

>echo off  1>file1.stream 2>file2.stream 4>file4.strea
echo to stream1
echo to stream3
echo to stream4


file1.stream contents
-----------------------------

file2.stream contents
-----------------------------

file4.stream contents
-----------------------------
echo to stream2

=====================================================

>echo off  1>file1.stream 2>file2.stream
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


file1.stream contents
-----------------------------

file2.stream contents
-----------------------------

=====================================================

>echo off  1>file1.stream 3>file3.stream
echo to stream2
echo to stream3


file1.stream contents
-----------------------------

file3.stream contents
-----------------------------
echo to stream1
echo to stream4

=====================================================

>echo off  1>file1.stream 4>file4.stream
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


file1.stream contents
-----------------------------

file4.stream contents
-----------------------------

=====================================================

>echo off  2>file2.stream 3>file3.stream
echo to stream1
echo to stream3
echo to stream4


file2.stream contents
-----------------------------

file3.stream contents
-----------------------------
echo to stream2

=====================================================

>echo off  2>file2.stream 4>file4.stream
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


file2.stream contents
-----------------------------

file4.stream contents
-----------------------------

=====================================================

>echo off  3>file3.stream 4>file4.stream
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


file3.stream contents
-----------------------------

file4.stream contents
-----------------------------

=====================================================

>echo off  1>nul 2>nul 3>file3.stream 4>file4.stream
echo to stream3
echo to stream4


file3.stream contents
-----------------------------
echo to stream1

file4.stream contents
-----------------------------
echo to stream2

=====================================================

>echo off  1>nul 2>nul 3>file3.stream
echo to stream2
echo to stream3


file3.stream contents
-----------------------------
echo to stream1
echo to stream4

=====================================================

>echo off  1>nul 2>nul 4>file4.stream
echo to stream1
echo to stream3
echo to stream4


file4.stream contents
-----------------------------
echo to stream2

=====================================================

>echo off  1>nul 3>file3.stream
echo to stream2
echo to stream3


file3.stream contents
-----------------------------
echo to stream1
echo to stream4

=====================================================

>echo off  2>nul 4>file4.stream
echo to stream1
echo to stream2
echo to stream3
The handle could not be duplicated
during redirection of handle 1.


file4.stream contents
-----------------------------


Dave Benham

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: foolproof counting of arguments

#32 Post by Liviu » 15 Feb 2012 19:27

dbenham wrote:Redirection is one wacky world :lol:

I haven't fully analyzed the results to arrive at a pattern yet. But here are some interesting test cases with results.

Interesting... Though I'd say first pattern which jumps to the eye is random chaos ;-)

Not directly related, but another >&3 odd behavior noted in my Feb 6th post at http://groups.google.com/group/alt.msdos.batch.nt/browse_thread/thread/b5d5f5e3e17f024b/c24bcb7982c42ab2?lnk=raot.

Liviu

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: foolproof counting of arguments

#33 Post by dbenham » 19 Feb 2012 18:28

jeb wrote:I create a completly other way of capturing the parameters into a file

Code: Select all

@echo on
@echo Hello1 1>stream1.txt 2>stream2.txt 3>stream3.txt 4>stream4.txt
rem # %* #
@echo off
type stream3.txt > con
exit /b


This is adapted from the code of walid2me SO: Commenting multiple lines in DOS batch file

The only problem is to restore the standard handles :!:
As after the code even the prompt isn't visible anymore

I can get close :!:
By some weird chaining effect I can redirect the output back to con. Stream 1 (stdout) is not quite normal when finished because CLS does not work, and the stream3.txt is still left open until the CMD.EXE session terminates. I imagine there are additional oddities lurking somewhere.

test.bat

Code: Select all

@echo on 1>nul 3>stream3.txt
rem # %* #
@echo off 1>nul 3>nul 4>con
type stream3.txt

command line session

Code: Select all

>cmd
Microsoft Windows [Version 6.0.6002]
Copyright (c) 2006 Microsoft Corporation.  All rights reserved.

>test Hello world

>rem # Hello world #

>cls

>del stream3.txt
C:\Users\public\utils\stream3.txt
The process cannot access the file because it is being used by another process.

>exit

>del stream3.txt

>


I can work with both stdout and stderr if I want. From the command line:

Code: Select all

>cmd
Microsoft Windows [Version 6.0.6002]
Copyright (c) 2006 Microsoft Corporation.  All rights reserved.

>echo on 1>nul 2>nul 3>stream3.txt 4>stream4.txt
echo stdout message
echo stderr message >&2
echo on 1>nul 2>nul 3>nul 4>nul 5>con 6>con

>type stream3.txt

>stdout message

>
>
>type stream4.txt
stderr message

>dir doesNotExist
 Volume in drive C is OS
 Volume Serial Number is EE2C-5A11

 Directory of C:\Users\public\utils

File Not Found

>dir doesNotExist 1>nul
File Not Found

>dir doesNotExist 2>nul
 Volume in drive C is OS
 Volume Serial Number is EE2C-5A11

 Directory of C:\Users\public\utils


>del stream3.txt
C:\Users\public\utils\stream3.txt
The process cannot access the file because it is being used by another process.

>del stream4.txt
C:\Users\public\utils\stream4.txt
The process cannot access the file because it is being used by another process.

>exit

>del stream3.txt

>del stream4.txt

>


While experimenting I learned that CLS sends a form feed character <FF> (0x0C) to the screen. If you redirect CLS to con, then you get a graphical representation of the <FF> character on the screen. I can use CLS behavior to get <FF> in a variable:

Code: Select all

@echo off
setlocal
for /f %%A in ('cls') do set FF=%%A
set FF


Dave Benham

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: foolproof counting of arguments

#34 Post by Squashman » 19 Feb 2012 18:55

dbenham wrote:
jeb wrote:While experimenting I learned that CLS sends a form feed character <FF> (0x0C) to the screen. If you redirect CLS to con, then you get a graphical representation of the <FF> character on the screen. I can use CLS behavior to get <FF> in a variable:

Code: Select all

@echo off
setlocal
for /f %%A in ('cls') do set FF=%%A
set FF


Dave Benham

Sweet. Now we have all 3 covered. CR LF and FF

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: foolproof counting of arguments

#35 Post by dbenham » 26 Mar 2012 16:29

StackOverflow user Erbert has discovered that reversing the order of the redirection, (high numbered file handles before low numbers), prevents the "permanent" redirection: http://stackoverflow.com/a/9880156/1012053


Dave Benham

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: foolproof counting of arguments

#36 Post by Liviu » 28 Mar 2012 22:38

dbenham wrote:StackOverflow user Erbert has discovered that reversing the order of the redirection, (high numbered file handles before low numbers), prevents the "permanent" redirection: http://stackoverflow.com/a/9880156/1012053

Interesting, thanks for the pointer. That said, I don't think the case is closed. For one thing, replacing "3" with some other handle number e.g. "4" or "5" gives different output/behavior. What's observed there may well be the result of some crosstalk with cmd's own internal usage of handle 3.

Liviu

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

Re: foolproof counting of arguments

#37 Post by jeb » 29 Mar 2012 00:20

In my opinion, stream3 and stream4 are the destinations of stdout and stderr, they will be used when stream1 or stream2 are not redirected.

So if you write

Code: Select all

echo test1
echo test1r 1> stream1.txt


In the first case the text will be "redirected" to stream3, and as the destination of stream3 is CON, the text will be displayed
And in the second case stream3 will not be used, as there is an explicit redirection

(Only a guess) :)
jeb

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: foolproof counting of arguments

#38 Post by dbenham » 29 Mar 2012 13:26

I believe I have figured it out :!: :D

When a stream is redirected, the current definition is held in (transfered to) the 1st available (undefined) stream. When the redirection ends, the held definition is restored back to the original stream, and the holding stream is undefined again.

For example

Code: Select all

1>out.txt echo Hello world!
While the redirection is in effect, 1 is directed to the file, and 3 holds the original definition of 1 (stdout). Stream 2 couldn't be used to hold the stdout definition because it is already being used by stderr. Once redirection is over, 1 is restored to the definition held in 3 (stdout), and 3 is undefined again. (Edit - typos fixed on 2012-04-03)

The weirdness starts when a chain of redirections occurs. The redirection chaining works fine on the way in. But on the way out, restoration is only performed 1 level deep.

Code: Select all

1>out1.txt 3>out2.txt echo Hello world!
First 1 is directed to file out1.txt and 3 holds the definition of stdout, but then 3 is directed to out2.txt and 4 holds the definition of stdout. But once redirection is over, 1 is restored to the definition of 3(out2.txt), 3 is restored to the definition of 4(stdout), and 4 is undefined.

Here is a script I used to "prove" my theory

Code: Select all

@echo off
2>nul 3>nul 1>nul 5>nul 6>nul (
  REM while redirection is in effect
  REM 2=nul, 3=nul, 4=stderr
  REM 1=nul, 5=nul, 6=nul, 7=stdout
  1>&7 echo stdout via stream7
  2>&4 1>&2 echo stderr via stream4
  1>&7 echo -------------------------
)
REM Prior redirection is now over,
REM but handles are only restored one level deep
REM 2=nul, 3=stderr, 4=undefined
REM 1=nul, 5=nul, 6=stdout, 7=undefined
1>&6 echo stdout via stream6
2>&3 1>&2 echo stderr via stream3
1>&6 echo -------------------------

1>nul 4>&6 2>nul 8>&3 (
  REM while redirection is in effect
  REM 5=nul(unchanged)
  REM 1=nul, 4=6=stdout, 7=nul(from 4 from 1 before redirection)
  REM 2=nul, 8=3=stdout, 9=nul(from 8 from 2 before redirection)
  1>&6 echo stdout via stream6
  1>&4 echo stdout via stream4 (via stream6^)
  2>&3 1>&2 echo stderr via stream3
  2>&8 1>&2 echo stderr via stream8 (via stream3^)
  1>&6 echo -------------------------
)
REM 1=6=stdout, 4=nul, 7=undefined
REM 2=3=stderr, 8=nul, 9=undefined
echo restored stdout (via stream6)
1>&6 echo stdout via stream6
1>&2 echo restored stderr (via stream3)
1>&3 echo stderr via stream3
1>&6 echo -------------------------

And here are the results

Code: Select all

stdout via stream7
stderr via stream4
-------------------------
stdout via stream6
stderr via stream3
-------------------------
stdout via stream6
stdout via stream4 (via stream6)
stderr via stream3
stderr via stream8 (via stream3)
-------------------------
restored stdout (via stream6)
stdout via stream6
restored stderr (via stream3)
stderr via stream3
-------------------------

-----------------------------------------------------------

Now we can use jeb's idea to safely capture the arguments, and have stdout restored to almost normal when done. (remember the original topic of this thread :) :!:)

Code: Select all

@echo on 1>args.txt 3>&1
rem # %* #
@echo off 1>nul 4>&3
type args.txt
echo -------------
1>&3 echo stream1 and stream3 both point to stdout
echo -------------
del args.txt
1>&4 echo stream4 is left open pointing to args.txt
type args.txt
exit /b

stdout is normal except that both stream1 and stream3 point to stdout, whereas normally stream 3 is undefined. This might not be so bad.

More problematic is the fact that stream4 is left open pointing to args.txt, and it will remain open until the CMD shell is terminated. :(


Dave Benham
Last edited by dbenham on 03 Apr 2012 11:24, edited 2 times in total.

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: foolproof counting of arguments

#39 Post by dbenham » 29 Mar 2012 21:44

My prior post had a test case that enabled me to derive the rules, but it wasn't very definitive proof.

Here is a new test case that better proves the theory.

Code: Select all

@echo off
2>file2.txt 3>file3.txt 1>file1.txt 5>file5.txt 6>file6.txt (
  REM while redirection is in effect
  REM 2=file2.txt, 3=file3.txt, 4=stderr
  REM 1=file1.txt, 5=file5.txt, 6=file6.txt, 7=stdout
  dir /b nonExistentFile
       echo Inside 1st redirection: stream1
  1>&2 echo Inside 1st redirection: stream2
  1>&3 echo Inside 1st redirection: stream3
  1>&4 echo Inside 1st redirection: stream4
  1>&5 echo Inside 1st redirection: stream5
  1>&6 echo Inside 1st redirection: stream6
  1>&7 echo Inside 1st redirection: stream7
  1>&7 (
    echo -----------------------
    echo file1:
    type file1.txt
    echo -----------------------
    echo file2:
    type file2.txt
    echo -----------------------
    echo file3:
    type file3.txt
    echo -----------------------
    echo file5:
    type file5.txt
    echo -----------------------
    echo file6:
    type file6.txt
    echo =============================
  )
)
REM Prior redirection is now over,
REM but handles are only restored one level deep
REM 2=file3.txt, 3=stderr, 4=undefined
REM 1=file5.txt, 5=file6.txt, 6=stdout, 7=undefined
dir /b nonExistentFile
     echo After 1st redirection: stream1
1>&2 echo After 1st redirection: stream2
1>&3 echo After 1st redirection: stream3
1>&4 echo After 1st redirection: stream4
1>&5 echo After 1st redirection: stream5
1>&6 echo After 1st redirection: stream6
1>&7 echo After 1st redirection: stream7
1>&6 (
  echo -----------------------
  echo file1:
  type file1.txt
  echo -----------------------
  echo file2:
  type file2.txt
  echo -----------------------
  echo file3:
  type file3.txt
  echo -----------------------
  echo file5:
  type file5.txt
  echo -----------------------
  echo file6:
  type file6.txt
  echo =============================
)

1>&6 4>&6 2>&3 8>&3 (
  REM while redirection is in effect
  REM 3=stderr(unchanged), 5=file6.txt(unchanged), 6=stdout(unchanged),
  REM 1=6=stdout, 4=6=stdout, 7=file5.txt(from 4 from 1 before redirection)
  REM 2=3=stderr, 8=3=stderr, 9=file3.txt(from 8 from 2 before redirection)
  dir /b nonExistentFile
       echo Inside 2nd redirection: stream1
  1>&2 echo Inside 2nd redirection: stream2
  1>&3 echo Inside 2nd redirection: stream3
  1>&4 echo Inside 2nd redirection: stream4
  1>&5 echo Inside 2nd redirection: stream5
  1>&6 echo Inside 2nd redirection: stream6
  1>&7 echo Inside 2nd redirection: stream7
  1>&8 echo Inside 2nd redirection: stream8
  1>&9 echo Inside 2nd redirection: stream9
  1>&6 (
    echo -----------------------
    echo file1:
    type file1.txt
    echo -----------------------
    echo file2:
    type file2.txt
    echo -----------------------
    echo file3:
    type file3.txt
    echo -----------------------
    echo file5:
    type file5.txt
    echo -----------------------
    echo file6:
    type file6.txt
    echo =============================
  )
)
REM Prior redirection is now over,
REM 3=stderr(unchanged), 5=file6.txt(unchanged), 6=stdout(unchanged),
REM 1=6=stdout, 4=file5.txt, 7=undefined
REM 2=3=stderr, 8=file3.txt, 9=undefined
dir /b nonExistentFile
     echo After 2nd redirection: stream1
1>&2 echo After 2nd redirection: stream2
1>&3 echo After 2nd redirection: stream3
1>&4 echo After 2nd redirection: stream4
1>&5 echo After 2nd redirection: stream5
1>&6 echo After 2nd redirection: stream6
1>&7 echo After 2nd redirection: stream7
1>&8 echo After 2nd redirection: stream8
1>&9 echo After 2nd redirection: stream9
echo -----------------------
echo file1:
type file1.txt
echo -----------------------
echo file2:
type file2.txt
echo -----------------------
echo file3:
type file3.txt
echo -----------------------
echo file5:
type file5.txt
echo -----------------------
echo file6:
type file6.txt
echo =============================

And here are the results

Code: Select all

>test
Inside 1st redirection: stream4
Inside 1st redirection: stream7
-----------------------
file1:
Inside 1st redirection: stream1
-----------------------
file2:
File Not Found
Inside 1st redirection: stream2
-----------------------
file3:
Inside 1st redirection: stream3
-----------------------
file5:
Inside 1st redirection: stream5
-----------------------
file6:
Inside 1st redirection: stream6
=============================
After 1st redirection: stream3
After 1st redirection: stream6
-----------------------
file1:
Inside 1st redirection: stream1
-----------------------
file2:
File Not Found
Inside 1st redirection: stream2
-----------------------
file3:
Inside 1st redirection: stream3
File Not Found
After 1st redirection: stream2
The handle could not be duplicated
during redirection of handle 1.
-----------------------
file5:
Inside 1st redirection: stream5
After 1st redirection: stream1
After 1st redirection: stream4
-----------------------
file6:
Inside 1st redirection: stream6
After 1st redirection: stream5
=============================
File Not Found
Inside 2nd redirection: stream1
Inside 2nd redirection: stream2
Inside 2nd redirection: stream3
Inside 2nd redirection: stream4
Inside 2nd redirection: stream6
Inside 2nd redirection: stream8
-----------------------
file1:
Inside 1st redirection: stream1
-----------------------
file2:
File Not Found
Inside 1st redirection: stream2
-----------------------
file3:
Inside 1st redirection: stream3
File Not Found
After 1st redirection: stream2
The handle could not be duplicated
during redirection of handle 1.
Inside 2nd redirection: stream9
-----------------------
file5:
Inside 1st redirection: stream5
After 1st redirection: stream1
After 1st redirection: stream4
Inside 2nd redirection: stream7
-----------------------
file6:
Inside 1st redirection: stream6
After 1st redirection: stream5
Inside 2nd redirection: stream5
=============================
File Not Found
After 2nd redirection: stream1
After 2nd redirection: stream2
After 2nd redirection: stream3
After 2nd redirection: stream6
After 2nd redirection: stream7
The handle could not be duplicated
during redirection of handle 1.
-----------------------
file1:
Inside 1st redirection: stream1
-----------------------
file2:
File Not Found
Inside 1st redirection: stream2
-----------------------
file3:
Inside 1st redirection: stream3
File Not Found
After 1st redirection: stream2
The handle could not be duplicated
during redirection of handle 1.
Inside 2nd redirection: stream9
After 2nd redirection: stream8
-----------------------
file5:
Inside 1st redirection: stream5
After 1st redirection: stream1
After 1st redirection: stream4
Inside 2nd redirection: stream7
After 2nd redirection: stream4
-----------------------
file6:
Inside 1st redirection: stream6
After 1st redirection: stream5
Inside 2nd redirection: stream5
After 2nd redirection: stream5
=============================

>del file1.txt

>del file2.txt

>del file3.txt
....file3.txt
The process cannot access the file because it is being used by another process.

>del file5.txt
....file5.txt
The process cannot access the file because it is being used by another process.

>del file6.txt
....file6.txt
The process cannot access the file because it is being used by another process.

>

The results exactly match my expectations. There are 2 cases that took a while for me to figure out.

After the 1st redirection is over, stream 4 is undefined. Yet when I redirect 1 to &4, I get the output in file5.txt. What I believe happens is this:
- Before the redirection, 1 is directed to file5.txt, and 4 is undefined.
- CMD gets the instruction to redirect 1 to &4
- - first the current definition (file5.txt) is stored in the first available undefined stream, which happens to be 4.
- - then the redirection is completed and 1 is redirected to the current definition of 4, which is now file5.txt

After the 2nd redirection is over, stream 7 is undefined. Yet when I redirect 1 to &7, I get the output on stdout.
- Before the redirection, 1 is going to stdout, and 7 is undefined
- CMD gets the instruction to redirect 1 to &7
- - first the current definition (stdout) is stored in the first available undefined stream, which happens to be 7.
- - then the redirection is completed and 1 is redirected to the current definition of 7, which is now stdout.

When all is done, file1.txt and file2.txt are closed. But files 3, 5 and 6 are left open and locked until the CMD session is terminated.


Dave Benham

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

Re: foolproof counting of arguments

#40 Post by jeb » 04 Apr 2012 10:43

It's really awkful :shock: :!:

I like this type of analyses, it's a really new insight into batch/cmd mechanics.

But I don't understand why someone (MS) create it this way.
Even when I was a poor beginner I wouldn't produce such solutions.

But it was a great investigation of Dave to figure out how it works. :D

jeb

Astyan
Posts: 1
Joined: 25 Aug 2012 21:33

Re: foolproof counting of arguments

#41 Post by Astyan » 25 Aug 2012 21:53

Hi !

What about this code :

set tmp=%1
set tmp=%tmp:~0,1%
:: Double the doublequote seems to be better with if using [ ].Characters " = [ & ^ etc are handled
set tmp=%tmp:"=""%
if ["%tmp%"] == ["~0,1"] GOTO :ARGUMENT_NOT_DEFINED
if ["%tmp%"] == [""] GOTO :ARGUMENT_NOT_DEFINED

:ARGUMENT_DEFINED
REM your stuff
GOTO :EOF

:ARGUMENT_NOT_DEFINED
REM your other stuff
GOTO :EOF

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: foolproof counting of arguments

#42 Post by Liviu » 25 Aug 2012 23:03

Astyan wrote:What about this code

Fails if you pass it for example either

=
^&
^<

and similar others, too.

Liviu

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: foolproof counting of arguments

#43 Post by dbenham » 26 Aug 2012 00:15

And it is much more complicated than need be for the cases that don't fail. The code could be simplified to:

Code: Select all

set test=%1
if defined test (echo arg1 is defined) else echo arg1 is not defined

BUT, as Liviu has already pointed out, the SET statement can fail under many circumstances.


Dave Benham

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: foolproof counting of arguments

#44 Post by Aacini » 02 Nov 2013 21:10

I would like to add some technical information to this thread just to complete it. I copied this info from "Microsoft MS-DOS Programmer's Reference" manual for DOS Version 5 (Microsoft Press, 1991). Although the manual is (old!) DOS specific, the point stated here is performed in the same way in modern Windows cmd.exe command-line window, that is: how the redirection of a standard handle to/from a disk file is achieved?

When any command is executed in a command-line window or Batch file, it inherits from its parent process these three standard handles: 0 (STDIN) connected to keyboard, 1 (STDOUT) connected to screen, and 2 (STDERR) connected to screen (although in original DOS there are also 3=STDPRN and 4=STDAUX standard handles open). In Windows/DOS Batch files, the handles 3 to 9 are available to open additional files (in normal programs the last number is the value of FILES= in CONFIG.SYS file, usually 20). There are two DOS file handle management functions important for this topic:

Code: Select all

Int 21H, Function 45H: Duplicate File Handle

    mov     bx, OldHandle       ;handle to duplicate, for example: 1 (STDOUT)
    mov     ah, 45H             ;function: Duplicate File Handle
    int     21H                 ;call DOS function
    mov     NewHandle, ax       ;store new handle

This function creates a new file handle that can be used to read from or write to the same file or device that is associated with the original handle.

The number of the new handle is the first available number (the same way than in Open File function), for example: 3.

Code: Select all

Int 21H, Function 46H: Force Duplicate File Handle

    mov     bx, OpenHandle      ;handle of file or device
    mov     cx, DuplicateHandle ;new handle for same file or device
    mov     ah, 46H             ;function: Force Duplicate File Handle
    int     21H                 ;call DOS function

This function forces the specified duplicate handle to identify the same open file or device identified by the OpenHandle parameter.
DuplicateHandle must not exceed the current limit of available handles.
After a program uses this function, both handles can be used to read from or write to the file or device specified by OpenHandle.
If DuplicateHandle identifies an open file, MS-DOS closes that file.

Lets pass to review the method that the programmer's manual specify to achieve a redirection of STDOUT in a similar way than this command-line: ANYCOMMAND > DISKFILE.TXT

Code: Select all

5.4.3 Standard-Device Redirection

A parent program can redirect a standard device for the child program by associating the standard-device handle with a new device or file before it starts the child program. To do this, the parent program should follow these steps:

1  Duplicate the standard-device handle by using Duplicate File Handle (Function 45H).

    mov     bx, 1               ;handle to duplicate: STDOUT
    mov     ah, 45H             ;function: Duplicate File Handle
    int     21H                 ;call DOS function

2  Save the duplicate handle.

    mov     NewHandle, ax       ;store new handle               ;NewHandle = 3 (associated with the screen)

3  Open the new file or device.

    mov     dx, offset FileName ;dx points to name of file or device (like "diskfile.txt",0)
    mov     ah, 3DH             ;function: Open File with Handle
    int     21H                 ;call DOS function
    mov     handle, ax          ;handle of file or device       ;handle = 4 (next available handle)

4  With the new handle retrieved in step 3, modify the standard-device handle by using Force Duplicate File Handle (Function 46H).

    mov     bx, handle          ;handle of file or device       ;bx = 4 (associated with diskfile.txt)
    mov     cx, 1               ;new handle for same file or device ;standard STDOUT
    mov     ah, 46H             ;function: Force Duplicate File Handle
    int     21H                 ;call DOS function              ;now STDOUT is associated with diskfile.txt

The standard-device handle should now identify the same file or device as the new handle.                               

5  Load and run the child program.

A parent program can restore the original standard-device handle by using Force Duplicate File Handle and specifying the duplicate handle saved in step 2.

    mov     bx, NewHandle       ;handle of file or device       ;bx = 3 (associated with the screen)
    mov     cx, 1               ;new handle for same file or device ;standard STDOUT
    mov     ah, 46H             ;function: Force Duplicate File Handle
    int     21H                 ;call DOS function              ;now STDOUT is re-associated with the screen
                                                                ;at the same time, handle 1 of diskfile.txt is closed
                                                                ;but handles 4 and 3 remains open until the program ends


Antonio

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

Re: foolproof counting of arguments

#45 Post by jeb » 28 Aug 2018 08:05

I have some new thoughts about this old topic.

Two keypoints are relevant.
jeb wrote:The REM technic currently fails with embedded linefeeds in the parameters %*.
And it can't fetch carriage returns, this seems to be impossible as CR's are removed directly after the percent expansion!
dbenham wrote:
29 Mar 2012 13:26
Now we can use jeb's idea to safely capture the arguments, and have stdout restored to almost normal when done. (remember the original topic of this thread :) :!:)

Code: Select all

@echo on 1>args.txt 3>&1
rem # %* #
@echo off 1>nul 4>&3
type args.txt
echo -------------
1>&3 echo stream1 and stream3 both point to stdout
echo -------------
del args.txt
1>&4 echo stream4 is left open pointing to args.txt
type args.txt
exit /b
:idea: But I found a bullet proof solution to fetch all characters, even line feeds and :!: carrige returns :!:

Code: Select all

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

cls

REM *** Clear params.tmp
break > params.tmp

start "" /b cmd /c "%~d0\:StayAlive:\..\%~pnx0 params.tmp"

(set LF=^
%=empty=%
)
REM *** Change prompt for better recognition
prompt #PROMPT#


REM *** Change streams permanently
REM *** stream1 redirects to params.tmp
REM *** stream2 redirects to nul
echo on >nul 2>nul 3>params.tmp 4>nul

@REM *** This is the magic part, it forces a syntax error, the error message itself shows the expanded %asterix without ANY modification
( Prepare ) PARAMS:%LF%%*%LF%

echo Works
exit /b


REM *** Second thread to fetch and show the parameters
:StayAlive

:__WaitForParams
if %~z1 EQU 0 (
    goto :__WaitForParams
)
REM *** Show the result
findstr /n "^" %1 
exit
The tricky line is
( Prepare ) PARAMS:%LF%%*%LF%
It creates a syntax error at ") PARAMS" while parsing, therefore the content of %* is not relevant.
BUT when a syntax error occours the complete block will be printed (ECHO ON required).
And in this case even carriage returns are preserved.

Okay, so far so good, but now the drawbacks: :(
- The thread stops immediately, I found no way to execute any commands in the batch for that thread after the error (tried &, &&, ||, code blocks, call, FOR loops)
- I found no way to redirect the syntax error output other than with the permanent redirection, but I can't restore the streams, as the main thread stops immediately
- Because the main thread stops, the user input goes to the command line, even when the StayAlive thread is still working
- The params.tmp file is still open and can't be deleted until the cmd window is closed

It could be useful when someone found a solution to some of the above points, or even how to close the current cmd window :?:

jeb

Post Reply