66

Can you redirect the output of a command to a variable with pipes?

I haven't tried much as I haven't been able to think of anything to try, but I have tried one method (with two variations)...

For example:

echo Hello|set text=

Didn't work, neither did:

echo Hello | set text=

I know you can do it fairly easily with the FOR command, but I think it would look "nicer" with a pipe.

And if you're wondering, I don't have a specific reason I'm asking this other than I'm curious and I can't find the answer.

3
  • 1
    Check dolphy's answer here: stackoverflow.com/questions/6359820/… - if you really don't want to use for you can use a temporary file. There's no way to do it with a pipe, though. Commented Feb 19, 2013 at 10:35
  • 1
    This problem is described with detail here: stackoverflow.com/questions/8192318/… Commented Feb 19, 2013 at 12:48
  • superuser.com/a/1007898/429721 perfected for pipes, stdout/err redirection, multiple executables, output storing and processing value as number and other manipulations... works quite well.. Commented Dec 2, 2015 at 0:37

6 Answers 6

58

Your way can't work for two reasons.

You need to use set /p text= for setting the variable with user input.
The other problem is the pipe.
A pipe starts two asynchronous cmd.exe instances and after finishing the job both instances are closed.

That's the cause why it seems that the variables are not set, but a small example shows that they are set but the result is lost later.

set myVar=origin
echo Hello | (set /p myVar= & set myVar)
set myVar

Outputs

Hello
origin

Alternatives: You can use the FOR loop to get values into variables or also temp files.

for /f "delims=" %%A in ('echo hello') do set "var=%%A"
echo %var%

or (and before you ask, these > and < at the beginning of the command are not in error).

>output.tmp echo Hello
>>output.tmp echo world

<output.tmp (
  set /p line1=
  set /p line2=
)
echo %line1%
echo %line2%

or, same, but on one line and one value, also deletes the temp file:

>output.tmp echo Hello World&&<output.tmp (set /p line1=)&&del output.tmp

Alternative with a macro:

You can use a batch macro, this is a bit like the bash equivalent

@echo off

REM *** Get version string 
%$set% versionString="ver"
echo The version is %versionString[0]%

REM *** Get all drive letters
`%$set% driveLetters="wmic logicaldisk get name /value | findstr "Name""
call :ShowVariable driveLetters

The definition of the macro can be found at
SO:Assign output of a program to a variable using a MS batch file

Sign up to request clarification or add additional context in comments.

7 Comments

So, there is no way around the limitations without a temp file or the "for" command? Even if there isn't, thanks anyway.
Obviously you can use FOR but then you don't need a pipe. The same apply to temp files
btw, it appears in my tests that you can only use 1 command in a for statement. You cannot chain more commands together with &.
@end-user You can use more than one command, but it's better to combine them with parenthesis for %%a in (a b c) do (echo %%a <NewLine>echo ----)
No, I meant in the evaluation. But it turns out I can if I escape the & sign: for /f %%i in ('first command ^&^& second command') do echo result: %%i
|
15

The lack of a Linux-like backtick/backquote facility is a major annoyance of the pre-PowerShell world. Using backquotes via for-loops is not at all cosy. So we need kinda of setvar myvar cmd-line command.

In my %path% I have a dir with a number of bins and batches to cope with those Win shortcomings.

One batch I wrote is:

:: setvar varname cmd
:: Set VARNAME to the output of CMD
:: Triple escape pipes, eg:
:: setvar x  dir c:\ ^^^| sort 
:: -----------------------------

@echo off
SETLOCAL

:: Get command from argument 
for /F "tokens=1,*" %%a in ("%*") do set cmd=%%b

:: Get output and set var
for /F "usebackq delims=" %%a in (`%cmd%`) do (
     ENDLOCAL
     set %1=%%a
)

:: Show results 
SETLOCAL EnableDelayedExpansion
echo %1=!%1! 

So in your case, you would type:

> setvar text echo Hello
text=Hello 

The script informs you of the results, which means you can:

> echo text var is now %text%
text var is now Hello 

You can use whatever command:

> setvar text FIND "Jones" names.txt

What if the command you want to pipe to some variable contains itself a pipe?
Triple escape it, ^^^|:

> setvar text dir c:\ ^^^| find "Win"

3 Comments

What does the usebackq delims= part do there?
@RichardLeMesurier: usebackq specifies to use a linux-like semantics, where a back quoted string is executed as a command and the output parsed into the for variables (here %%a). delims=x specifies the delimiter to be used in parsing (here no delimiters).
Thx for clarification. I used this pattern to great success in a build tool. This answer should be the accepted one.
12

I find myself a tad amazed at the lack of what I consider the best answer to this question anywhere on the internet. I struggled for many years to find the answer. Many answers online come close, but none really answer it. The real answer is

(cmd <arg> & echo.) >2 & (set /p <var>=)<2

The "secret sauce" being the "closely guarded coveted secret" that "echo." sends a CR/LF (ENTER/new line/0x0D0A). Otherwise, what I am doing here is redirecting the output of the first command to the standard error stream. I then redirect the standard error stream into the standard input stream for the "set /p =" command.

Example:

(echo foo & echo.) >2 & (set /p bar=)<2

6 Comments

I'm sorry to spoil your dreams, but this doesn't work via STDERR, but via a file with the name 2 (check with dir)
Oh. Thank you Stephan. It appears I still do not know if it is possible to stream the output from one command to the input of another without a file as an intermediate, apart from the rare except of pipe to more. It seems odd that in Windows and DOS that more seems one of only a few commands that can have input piped to it. After hours over the span of over a decade, I am yet to have seen anyone explain this satisfactorily.
there are a few more commands, that take input through a pipe: more, sort, find, findstr, clip, choice, date, time, diskpart, wmic, schtask, pause and (not verified but probably yes): ftp, telnet, ssh and maybe a few more.
You have saved me next 10-20 years of looking for this solution!!!
I recommend adding an UPDATE to this answer with: (cmd & echo.) >temp & (set /p =)<temp & del temp.
|
6

You can set the output to a temporary file and the read the data from the file after that you can delete the temporary file.

echo %date%>temp.txt
set /p myVarDate= < temp.txt
echo Date is %myVarDate%
del temp.txt

In this variable myVarDate contains the output of command.

2 Comments

There are already 3 old answers mentioning a tempfile. I can't see any further benefit of your answer?
It fit to my search, so+1
5

THIS DOESN'T USE PIPEs, but requires a single tempfile
I used this to put simplified timestamps into a lowtech daily maintenance batfile

We have already Short-formatted our System-Time to HHmm, (which is 2245 for 10:45PM)
I direct output of Maint-Routines to logfiles with a $DATE%@%TIME% timestamp;
. . . but %TIME% is a long ugly string (ex. 224513.56, for down to the hundredths of a sec)

SOLUTION OVERVIEW:
1. Use redirection (">") to send the command "TIME /T" everytime to OVERWRITE a temp-file in the %TEMP% DIRECTORY
2. Then use that tempfile as the input to set a new variable (I called it NOW)
3. Replace

echo $DATE%@%TIME% blah-blah-blah >> %logfile%
      with
echo $DATE%@%NOW% blah-blah-blah >> %logfile%


====DIFFERENCE IN OUTPUT:
BEFORE:
SUCCESSFUL TIMESYNCH [email protected]
AFTER:
SUCCESSFUL TIMESYNCH 29Dec14@2252


ACTUAL CODE:

TIME /T > %TEMP%\DailyTemp.txt
SET /p NOW=<%TEMP%\DailyTemp.txt
echo $DATE%@%NOW% blah-blah-blah >> %logfile%


AFTERMATH:
All that remains afterwards is the appended logfile, and constantly overwritten tempfile. And if the Tempfile is ever deleted, it will be re-created as necessary.

2 Comments

This is actually the only solution which worked when I was trying to pass a complex git command, e.g. git log --pretty=format:%ad_commit_%h -n 1 --date=short.
Simple and effective
4

In a batch file I usually create a file in the temp directory and append output from a program, then I call it with a variable-name to set that variable. Like this:

:: Create a set_var.cmd file containing: set %1=
set /p="set %%1="<nul>"%temp%\set_var.cmd"

:: Append output from a command
ipconfig | find "IPv4" >> "%temp%\set_var.cmd"
call "%temp%\set_var.cmd" IPAddress
echo %IPAddress%

1 Comment

Quite skillful. Usually, avoiding temp files is nice, though.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.