C:\\>type c:\\output.txt
abcd
C:\\>type c:\\output.txt | set /p V1=
C:\\>set
... A bunch of junk, NOT seeing \"V1\"
What happened? According
This is not a direct answer to the original question, which asked Why doesn't [..] work?
, but this answer can be useful to people looking to a̲c̲h̲i̲e̲v̲e̲ what would be the logical result of something like this:
echo:somevalue|set /p somevar=
The normal workaround, for example for the code above, would be:
echo:somevalue>tempfile.txt&set /p somevar=
which does the work perfectly, except that it creates a new file which has to be dealt with afterwards (on top of the fact that we need to take extra steps to make sure this new file doesn't overwrite an existing one we didn't create or never intended to replace).
While it is true there is no way around using a file stream to bridge the gap across both sides of the pipe |
operator as far as environment variables are concerned, being under a Windows NT environment, and provided the volume the script resides on is using the NTFS filesystem, one can use something far more elegant than temporary files: alternate data streams
Let's jump right into an example illustrating how one might use them:
echo:somevalue>".\%~nx0":temp&set /p somevar=<".\%~nx0":temp
Here %~nx0
stands for, and gets expanded to, the filename (base name + extension) of our batch file, enclosed in quotes in case it contains spaces.
Note: While one could use
%0
directly (without quotes) instead to refer to the current script,.\%~nx0
is more manageable if you ever need to look at the expanded value of the command while debugging your script, especially if the full path of your script is quite long.
As such, ".\%~nx0":temp
refers to an alternate data stream (ADS) of our own script named temp
, for example myscript.cmd:temp
, that we create (or overwrite) with the output of the echo
command. Since ADS behave the same way as the default stream of a file, the echo
and set
commands don't see any difference.
A complete script using the method could look like this:
@echo off
set __self=".\%~nx0"
set __adsname=test.dat
:: Using some input...
set l_input=@FirewallAPI.dll,-23000
echo:Input: %l_input%
echo:
:: ...we process it...
expand_str_res_id.exe %l_input%>%__self%:%__adsname%&set /p l_output=< %__self%:%__adsname%
:: ...and show the output, e.g. "File and Printer Sharing"
echo:Output: %l_output%
echo:
:cleanup
set l_input=
set l_output=
type nul> %__self%:%__adsname%
set __self=
set __adsname=
pause
Here, at the end of the script, the line type nul> %__self%:%__adsname
, which could be expanded to something like type nul> ".\myscript.cmd":test.dat
, is used to clear the content of the alternate data stream we just used. Although this sets the size of the alternate data stream to zero, it does not erase it (see notes below).
Some final notes:
Most commands cannot understand or work with alternate data streams when they are provided as source files. This means the following ones cannot be used:
type
, e.g. type myscript.cmd:data > myscript_data.txt
copy
, e.g. copy myscript.cmd:data myscript_data.txt
move
, e.g. move myscript.cmd:data myscript_data.txt
del
/erase
, e.g. del myscript.cmd:data
ren
/rename
, e.g. ren myscript.cmd:data myscript.cmd:data.txt
In fact, it's really only file redirection that supports alternate data streams with one exception (that I can think of): the start
command.
:$DATA
ones), and editing/changing the content of a file only affects the default data stream. Still, we have a few options depending on what we're trying to achieve:
type myscript.cmd > myscript_clean.cmd
, after which the original file can be deleted.dir /a-d /r
. You can then use any program to access the names (alternate) data stream of a file, e.g. notepad.exe myscript.cmd:data
I hope this helps!