Diese Antwort ist NICHT für Sie , wenn Sie:
- selten, wenn überhaupt, externe CLIs verwenden müssen (was im Allgemeinen erstrebenswert ist - native PowerShell-Befehle spielen viel besser zusammen und benötigen keine solche Funktion).
- sind mit Bashs Prozessersetzung nicht vertraut.
Diese Antwort ist für Sie , wenn Sie:
- häufig externe CLIs verwenden (ob aus Gewohnheit oder aufgrund fehlender (guter) PowerShell-nativer Alternativen), insbesondere beim Schreiben von Skripten.
- sind gewöhnt und wissen zu schätzen, was die Prozessersetzung von Bash leisten kann.
- Update : Da PowerShell nun auch auf Unix-Plattformen unterstützt wird, ist diese Funktion von zunehmendem Interesse - siehe diese Funktionsanforderung auf GitHub, was darauf hindeutet, dass PowerShell eine Funktion implementiert, die der Prozessersetzung ähnelt.
In der Unix-Welt wird in Bash / Ksh / Zsh durch eine Prozessersetzung die Befehlsausgabe so behandelt, als wäre es eine temporäre Datei , die nach sich selbst bereinigt. Beispiel cat <(echo 'hello')
: cat
Wobei die Ausgabe des echo
Befehls als Pfad einer temporären Datei betrachtet wird, die die Befehlsausgabe enthält .
In PowerShell-nativen Befehlen ist eine solche Funktion zwar nicht unbedingt erforderlich, sie kann jedoch im Umgang mit externen CLIs hilfreich sein .
Das Emulieren der Funktion in PowerShell ist umständlich , kann sich aber lohnen, wenn Sie es häufig benötigen.
Stellen Sie sich eine Funktion mit dem Namen vor cf
, die einen Skriptblock akzeptiert, den Block ausführt und seine Ausgabe in ein temporäres Verzeichnis schreibt. Datei, die auf Anforderung erstellt wird, und gibt das Temp zurück. Dateipfad ; z.B:
findstr.exe "Windows" (cf { Get-ChildItem c:\ }) # findstr sees the temp. file's path.
Dies ist ein einfaches Beispiel, das die Notwendigkeit einer solchen Funktion nicht gut veranschaulicht . Ein vielleicht überzeugenderes Szenario ist die Verwendung vonpsftp.exe
für SFTP Transfer: sein Charge (automatisiert) Verwendung erfordert eine Eingabe bereitstellt Datei die gewünschten Befehle enthält, während solche Befehle leicht als Zeichenfolge im Fluge erzeugt werden können.
Um mit externen Versorgungsunternehmen so weit wie möglich kompatibel zu sein, sollte die temp. Datei sollte UTF-8 verwenden standardmäßig Codierung ohne Stückliste (Byte Order Mark) verwenden, obwohl Sie bei Bedarf eine UTF-8-Stückliste mit anfordern können -BOM
.
Leider kann der automatische Bereinigungsaspekt von Prozessersetzungen nicht direkt emuliert werden, sodass ein expliziter Bereinigungsaufruf erforderlich ist . Die Bereinigung erfolgt durch Aufrufen cf
ohne Argumente :
Für interaktive Nutzung, Sie können die Bereinigung , indem Sie die Bereinigung Aufruf an Ihre automatisieren prompt
Funktion wie folgt (die prompt
Funktion , um die Eingabeaufforderung zurückgibt String , sondern auch verwendet werden kann , hinter die Kulissen führen Befehle jedes Mal , wenn die Eingabeaufforderung angezeigt wird, ähnlich wie Bash $PROMPT_COMMAND
Variable); Um die Verfügbarkeit in einer interaktiven Sitzung zu überprüfen, fügen Sie cf
Ihrem PowerShell-Profil Folgendes sowie die folgende Definition hinzu :
"function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
Invoke-Expression
Zur Verwendung in Skripten muss der Block, der cf
das gesamte Skript verwendet, in einen try
/ finally
-Block eingeschlossen werden, in dem cf
ohne Argumente für die Bereinigung aufgerufen wird , um sicherzustellen, dass die Bereinigung durchgeführt wird :
# Example
try {
# Pass the output from `Get-ChildItem` via a temporary file.
findstr.exe "Windows" (cf { Get-ChildItem c:\ })
# cf() will reuse the existing temp. file for additional invocations.
# Invoking it without parameters will delete the temp. file.
} finally {
cf # Clean up the temp. file.
}
Hier ist die Implementierung : Erweiterte Funktion ConvertTo-TempFile
und ihr prägnanter Alias,cf
:
Hinweis : Die Verwendung von New-Module
, für die PSv3 + erforderlich ist, um die Funktion über ein dynamisches Modul zu definieren stellt sicher, dass keine Variablenkonflikte zwischen den Funktionsparametern und Variablen wird, auftreten können.
$null = New-Module { # Load as dynamic module
# Define a succinct alias.
set-alias cf ConvertTo-TempFile
function ConvertTo-TempFile {
[CmdletBinding(DefaultParameterSetName='Cleanup')]
param(
[Parameter(ParameterSetName='Standard', Mandatory=$true, Position=0)]
[ScriptBlock] $ScriptBlock
, [Parameter(ParameterSetName='Standard', Position=1)]
[string] $LiteralPath
, [Parameter(ParameterSetName='Standard')]
[string] $Extension
, [Parameter(ParameterSetName='Standard')]
[switch] $BOM
)
$prevFilePath = Test-Path variable:__cttfFilePath
if ($PSCmdlet.ParameterSetName -eq 'Cleanup') {
if ($prevFilePath) {
Write-Verbose "Removing temp. file: $__cttfFilePath"
Remove-Item -ErrorAction SilentlyContinue $__cttfFilePath
Remove-Variable -Scope Script __cttfFilePath
} else {
Write-Verbose "Nothing to clean up."
}
} else { # script block specified
if ($Extension -and $Extension -notlike '.*') { $Extension = ".$Extension" }
if ($LiteralPath) {
# Since we'll be using a .NET framework classes directly,
# we must sync .NET's notion of the current dir. with PowerShell's.
[Environment]::CurrentDirectory = $pwd
if ([System.IO.Directory]::Exists($LiteralPath)) {
$script:__cttfFilePath = [IO.Path]::Combine($LiteralPath, [IO.Path]::GetRandomFileName() + $Extension)
Write-Verbose "Creating file with random name in specified folder: '$__cttfFilePath'."
} else { # presumptive path to a *file* specified
if (-not [System.IO.Directory]::Exists((Split-Path $LiteralPath))) {
Throw "Output folder '$(Split-Path $LiteralPath)' must exist."
}
$script:__cttfFilePath = $LiteralPath
Write-Verbose "Using explicitly specified file path: '$__cttfFilePath'."
}
} else { # Create temp. file in the user's temporary folder.
if (-not $prevFilePath) {
if ($Extension) {
$script:__cttfFilePath = [IO.Path]::Combine([IO.Path]::GetTempPath(), [IO.Path]::GetRandomFileName() + $Extension)
} else {
$script:__cttfFilePath = [IO.Path]::GetTempFilename()
}
Write-Verbose "Creating temp. file: $__cttfFilePath"
} else {
Write-Verbose "Reusing temp. file: $__cttfFilePath"
}
}
if (-not $BOM) { # UTF8 file *without* BOM
# Note: Out-File, sadly, doesn't support creating UTF8-encoded files
# *without a BOM*, so we must use the .NET framework.
# [IO.StreamWriter] by default writes UTF-8 files without a BOM.
$sw = New-Object IO.StreamWriter $__cttfFilePath
try {
. $ScriptBlock | Out-String -Stream | % { $sw.WriteLine($_) }
} finally { $sw.Close() }
} else { # UTF8 file *with* BOM
. $ScriptBlock | Out-File -Encoding utf8 $__cttfFilePath
}
return $__cttfFilePath
}
}
}
Beachten Sie die Möglichkeit, optional einen Ausgabepfad [Datei] und / oder eine Dateinamenerweiterung anzugeben.