Batch equivalent of Bash backticks - bash

When working with Bash, I can put the output of one command into another command like so:
my_command `echo Test`
would be the same thing as
my_command Test
(Obviously, this is just a non-practical example.)
I'm just wondering if you can do the same thing in Batch.

You can get a similar functionality using cmd.exe scripts with the for /f command:
for /f "usebackq tokens=*" %%a in (`echo Test`) do my_command %%a
Yeah, it's kinda non-obvious (to say the least), but it's what's there.
See for /? for the gory details.
Sidenote: I thought that to use "echo" inside the backticks in a "for /f" command would need to be done using "cmd.exe /c echo Test" since echo is an internal command to cmd.exe, but it works in the more natural way. Windows batch scripts always surprise me somehow (but not usually in a good way).

You can do it by redirecting the output to a file first. For example:
echo zz > bla.txt
set /p VV=<bla.txt
echo %VV%

Read the documentation for the "for" command: for /?
Sadly I'm not logged in to Windows to check it myself, but I think something like this can approximate what you want:
for /F %i in ('echo Test') do my_command %i

Maybe I'm screwing up the syntax of the standard for /f method, but when I put a very complex command involving && and | within the backticks in the limit of the for /f, it causes problems. A slight modification from the usual is possible to handle an arbitrary complexity command:
SET VV=some_command -many -arguments && another_command -requiring -the-other -command | handling_of_output | more_handling
for /f "usebackq tokens=*" %%a in (`%VV%`) do mycommand %%a
By putting your full and complex command in a variable first, then putting a reference to the variable in the limit rather than putting the complex command directly into the limit of the for loop, you can avoid syntax interpretation issues. Currently if I copy the exact command I have set to the VV variable in the example above into where it's used, %VV%, it causes syntax errors.

You could always run Bash inside Windows. I do it all the time with MSYS (much more efficient than Cygwin).

Related

Replacement for $() in Windows batch script

I am trying to convert my bash script into a Windows batch file. It's a really simple one liner that's supposed to feed the contents of a file as arguments to script.exe, and send the results to output.txt.
This is the working bash script:
./script.exe $(cat input.txt) > output.txt
I know this might be bad style, but it works. The problem is, I have no idea how to do something like $() in a windows batch file. When I use it it sends the string "$(cat input.txt)" as the argument instead of running the command.
This bash construct is called command substitution. Here is a great answer from #MichaelBurr.
You can get a similar functionality using cmd.exe scripts with the
for /f command:
for /f "usebackq tokens=*" %%a in (`echo Test`) do my_command %%a
Yeah, it's kinda non-obvious (to say the least), but it's what's
there.
See for /? for the gory details.
Sidenote: I thought that to use "echo" inside the backticks in a
"for /f" command would need to be done using "cmd.exe /c echo
Test" since echo is an internal command to cmd.exe, but it works
in the more natural way. Windows batch scripts always surprise me
somehow (but not usually in a good way).
See also, on Superuser: Is there something like Command Substitution in WIndows CLI?

Evaluate an expression and send the result to another program in Windows Batch

I don't know how can i make this clear in a short sentence, so i give this example
Bash :
./foo $(ls -a)
First, "ls -a" is evaluated and converts to its output. So we 've got this line
./foo some_script Downloads
and then that's executed.
How can i achieve the same by using the windows command line?
P.S. : I need to use it when my IDE makes a build, so using PowerShell or CygWin is not an option
Assuming that the filenames contain no embedded spaces:
#echo off
setlocal enabledelayedexpansion
set "args="
for /f "delims=" %%i in ('dir /a /b') do set "args=!args!%%i "
.\foo %args%

Windows CLI equivalent to "sort -u"

I need an equivalent way in a Windows CLI to do what I would be able to in Linux/UNIX in piping through to show unique values.
I don't think there is a command as such, from what I can gather, so is there another way to do this?
What I have to achieve is to list files from multiple directories (whether they exist or not, which I currently do using dir). The way that the script (for the requesting application) works uses multiple sources to construct a directory list and subsequently the command depending on whether the platform is Windows or UNIX but the danger here is that there is a possibility of duplicate directories in the list and this would skew the results at the other end.
The easiest way to deal with this is to do it at source, i.e. the original command being run. So in Linux, the command structure is more or less:
find [dir] [dir] [dir] | grep file_name | sort -u
Doing the same in Windows is obviously more difficult given that:
it has to be a native read only command/script string you would use from Windows Command Prompt (cmd.exe)
I can't install anything on the hosts this is run on
I can't create temporary text files as part of the process
Microsoft has added a /unique switch to sort.exe in Windows 10 but has forgotten to update its documentation. I tested this switch on Windows 7, Windows 8.1 and Windows 10 v1709. It was present in the latter but absent from the first two.
So, sort.exe /unique is the equivalent of UNIX sort -u.
PowerShell already has a Sort-Object cmdlet that supports a -Unique switch. Better still, by default, PowerShell defines the alias sort for Sort-Object so, | sort -unique is pretty much all you need.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
(
SET "line="
FOR /f "delims=" %%a IN ('sort q22351713.txt') DO (
IF "!line!" neq "%%a" ECHO(%%a
set "line=%%a"
)
)>newfile.txt
GOTO :EOF
I used a file named q22351713.txt for my testing.
Produces newfile.txt
Not bullet-proof
Always better to explain what the original does rather than assume it's obvious.
Dirty batch is dirty...
%COMSPEC% /d /v:on /c "(SET LASTLINE=) & (FOR /f "usebackq tokens=*" %L IN (`DIR /b /s /-p dir1 dir2 dir3 dir3 dir2 dir1 "dir with space" ^| %SYSTEMROOT%\system32\find.exe /i "search_file_name" ^| %SYSTEMROOT%\system32\sort.exe`) DO #((IF /i NOT "{!LASTLINE!}"=="{%~L}" ECHO %~L) & SET "LASTLINE=%~L"))"
Let's break this down a bit...
%COMSPEC% /d /v:on /s /c "..." - you wanted a single command, not a batch, and we needed delayed expansion (/v:on), so we invoke a subshell using the known executable of the Windows Command Prompt (%COMSPEC%). We don't want to run autoexec.bat (/d) and we want to run the command and move on, rather than leaving an interactive shell (/c "..."). Quoting is a funky problem, so we try for some 'standardised' behaviour (/s)
SET LASTLINE= - empty any current content of a LASTLINE environment variable
(...) & (...) - run everything before & and everything after & regardless of the exit code of the first bit. Use brackets as appropriate to group commands, where needed.
FOR /f "usebackq tokens=*" %L IN (`...`) DO ... - run the command string between backquotes (usebackq) and read all words (tokens=*) in each line of stdout (FOR /f) into a temporary variable %L.
DIR /b /s /-p dir1 dir2 dir3 dir3 dir2 dir1 "dir with space" - recursively (/s) enumerate through dir1, dir2, dir3 and dir with space, printing only the full path of files to stdout (/b). Don't require keypresses to page the output, if that has been set (/-p). Note that, due to foibles of FOR it is up to you to only quote directories with special chars (e.g. space), i.e. do not quote single-word names.
... ^| ... - pipe the stdout of the first command to the stdin of the second. We use ^ to escape | within the backquotes of FOR.
%SYSTEMROOT%\system32\find.exe /i "search_file_name" - find the string search_file_name in stdin. Use a case-insensitive search (/i).
%SYSTEMROOT%\system32\sort.exe - sort stdin and write to stdout.
DO #(...) - we need to run two commands for each line, so we group them in brackets. FOR normally prints each command before it runs it, the # suppresses this.
(IF /i NOT "{!LASTLINE!}"=="{%~L}" ECHO %~L) - if the line we are looking at %~L is not the same as the line we have stored !LASTLINE!, print the line we are looking at. Again, use a case-insensitive comparison (/i). We bookend both variables with {} to handle empty values, and we use delayed expansion (!LASTLINE! instead of %LASTLINE%) to expand LASTLINE every time it loops, not just when the FOR command is first seen.
SET "LASTLINE=%~L" - store the line we are looking at %~L into the variable LASTLINE.
If you do not have access to the inbuilt environment variables, you may not have %SYSTEMROOT% or %COMSPEC%. Substitute these for C:\Windows and C:\Windows\System32\cmd.exe or, more appropriately, determine the correct locations for the system you are scanning.
Download cygwin utility, it is simple Linux terminal under windows.
It allow to execute any Linux command in Windows.
And you have sort -u there.

stdin → stdout?

What is the equivalent of the Unix echo foo | cat?
ECHO foo | TYPE CON hangs, waiting for input, at least on Windows XP/SP3. Possibly CON is not stdin but keyboard input.
You may wonder what is the point of this exercise: There are programs which behave differently when they notice that their output is piped, and I want a way to test them.
Unsure what you want to do but this may help:
type file|more
And this may be more appropriate for your needs.
foo.exe | findstr "^"
As I understand it, you are looking for an application you can pipe to which simply passes anything piped to it through to stdout.
I believe foo.exe | more will serve your purpose on Windows.
Note: more does have the side effect of paging the output, so if you need to test longer outputs you could write a simple application which does the redirection.
Edit: You can write a simple batch file to redirect stdin to stdout and pipe to that.
From jeb's answer here:
#echo off
setlocal DisableDelayedExpansion
for /F "tokens=*" %%a in ('findstr /n $') do (
set "line=%%a"
setlocal EnableDelayedExpansion
set "line=!line:*:=!"
echo(!line!
endlocal
)
Save this as redir.bat and use like so foo.exe | redir.bat. Tested on Win7. Compatible with default Windows install. Only downside is it's not an easy one-liner to remember.
I would use more for simple cases, and fall back on this for longer outputs.

Is it possible to set an environment variable to the output of a command in cmd.exe

I need to do the equivalent of
set ENVAR=`some-command`
In a windows/cmd.exe script. Cygwin is not an option.
For bonus marks: Is there some cmd.exe equivalent of backticks in general?
A quick and dirty way would be redirecting it to a file and then reading this, e.g.
some-command>out.txt
set /p ENVAR=<out.txt
I think for can also help you, but I don't remember the exact syntax. Try something like
for /f "usebackq" %x in (`some-command`) do set ENVAR=%x
I probably forgot some token or delim in the options...
Not "probably", it is absolutely a must to specify "delims=" (it means "no delimiters"), unless you want your variable to only contain up to first space or tab of the input data.
It is recommended to specify "delims=" as the last option to avoid potential confusion in options perception by the operator and by the shell.
I.e.
FOR /F "usebackq delims=" %%a IN (`cygpath.exe -u "%~1"`) DO (
SET CMDNAME=%%~a
SHIFT
)
See SS64 article on FOR /F.

Resources