Bash has <(..)
for process substitution. What is Powershell's equivalent?
I know there is $(...)
, but it returns a string, while <(..)
returns a file the outer command can read from, which is what it expects.
I'm also not looking for a pipe based solution, but something I can stick in the middle of the command line.
This answer is NOT for you, if you:
- rarely, if ever, need to use external CLIs (which is generally worth striving for - PowerShell-native commands play much better together and have no need for such a feature).
- aren't familiar with Bash's process substitution.
This answer IS for you, if you:
- frequently use external CLIs (whether out of habit or due to lack of (good) PowerShell-native alternatives), especially while writing scripts.
- are used to and appreciate what Bash's process substitution can do.
- Update: Now that PowerShell is supported on Unix platforms too, this feature is of increasing interest - see this feature request on GitHub, which suggests that PowerShell implement a feature akin to process substitution.
In the Unix world, in Bash/Ksh/Zsh, a process substitution is offers treating command output as if it were a temporary file that cleans up after itself; e.g.
cat <(echo 'hello')
, wherecat
sees the output from theecho
command as the path of a temporary file containing the command output.While PowerShell-native commands have no real need for such a feature, it can be handy when dealing with external CLIs.
Emulating the feature in PowerShell is cumbersome, but may be worth it, if you find yourself needing it often.
Picture a function named
cf
that accepts a script block, executes the block and writes its output to a temp. file created on demand, and returns the temp. file's path; e.g.:This is a simple example that doesn't illustrate the need for such a feature well. Perhaps a more convincing scenario is the use of
psftp.exe
for SFTP transfers: its batch (automated) use requires providing an input file containing the desired commands, whereas such commands can easily be created as a string on the fly.So as to be as widely compatible with external utilities as possible, the temp. file should use UTF-8 encoding without a BOM (byte-order mark) by default, although you can request a UTF-8 BOM with
-BOM
, if needed.Unfortunately, the automatic cleanup aspect of process substitutions cannot be directly emulated, so an explicit cleanup call is needed; cleanup is performed by calling
cf
without arguments:For interactive use, you can automate the cleanup by adding the cleanup call to your
prompt
function as follows (theprompt
function returns the prompt string, but can also be used to perform behind-the-scenes commands every time the prompt is displayed, similar to Bash's$PROMPT_COMMAND
variable); for availability in any interactive session, add the following as well as the definition ofcf
below to your PowerShell profile:For use in scripts, to ensure that cleanup is performed, the block that uses
cf
- potentially the whole script - needs to be wrapped in atry
/finally
block, in whichcf
without arguments is called for cleanup:Here's the implementation: advanced function
ConvertTo-TempFile
and its succinct alias,cf
:Note: The use of
New-Module
, which requires PSv3+, to define the function via a dynamic module ensures that there can be no variable conflicts between the function parameters and variables referenced inside the script block passed.Note the ability to optionally specify an output [file] path and/or filename extension.
When not enclosed in double quotes,
$(...)
returns a PowerShell Object (or rather, whatever is returned by the code enclosed), evaluating the enclosed code first. This should be suitable for your purposes ("something [I] can stick in the middle of the command line"), assuming that command-line is PowerShell.You can test this by piping various versions to
Get-Member
, or even just outputting it directly.When enclosed in double quotes, as you've noticed, `"$(...)" will just return a string.
In this way, if you wanted to insert, say, the contents of a file directly on a line, you could use something like: