This syntax is extremely convenient for capturing the output of a subcommand, and passing it as an argument to an outer command.
For example, using Docker, you can find containers that have a given property, stop them, and delete them, all in a single command line:
Code: Select all
docker rm $(docker stop $(docker ps -a -q --filter='some_criteria'))
Looking for a workaround for the cmd.exe shell, I eventually developed a $.bat script that makes it easy to capture the output of a command into a variable. (I hope I'm not reinventing the wheel!)
So with successive calls to that script, it's easy to pass on the output of commands to other commands, through intermediate variables. All that without having to do any copying and pasting in the cmd console.
The simplest way of using $.bat is to pass it the variable name, then the command to capture, and its arguments:
Code: Select all
C:\JFL\Temp>set HOST
Environment variable HOST not defined
C:\JFL\Temp>$ HOST hostname
set HOST=JFLZB
C:\JFL\Temp>set HOST
HOST=JFLZB
C:\JFL\Temp>
Code: Select all
C:\JFL\Temp>$ -X HOST hostname
for /f "delims=" %v in ('hostname ^| findstr /R "^."') do set HOST=%v
C:\JFL\Temp>
So I added a second way of operating the $.bat script: If no command is passed in, it captures the data on its standard input:
Code: Select all
C:\JFL\Temp>set HOST
Environment variable HOST not defined
C:\JFL\Temp>hostname | $ HOST
C:\JFL\Temp>set HOST=JFLZB
C:\JFL\Temp>set HOST
HOST=JFLZB
C:\JFL\Temp>
This is due to a batch limitation: When run in a pipe, a batch is actually run in a sub-shell. So if it sets a variable, that change is lost when that sub-shell exits at the end of the pipe.
I worked around that limitation by using a JScript subroutine, that uses the WScript.Shell.SendKeys() routine to generate keystrokes in the parent shell.
Still, it's a dirty trick, and its performance is poor. If anybody has an idea on how to do that in pure batch, I'm interested!
Finally note that this $.bat script is still a work in progress.
I know that the usual tricky batch characters will cause problems. I need to add some careful escaping.
Another known shortcoming is that $.bat only captures the last non-empty line in the subcommand output.
In the Docker scenario I used as an example in the beginning, there might be several containers that match the selection criteria, and you'd want to capture them all.
But there are cases where this is an advantage:
Code: Select all
C:\JFL\Temp>$ DT wmic os get LocalDateTime
set DT=LocalDateTime
set DT=20210122170704.345000+060
C:\JFL\Temp>
Any improvement idea welcome!