Cette réponse n'est PAS pour vous , si vous:
- rarement, voire jamais, avez besoin d'utiliser des CLI externes (ce qui vaut généralement la peine d'être recherché - les commandes natives de PowerShell jouent beaucoup mieux ensemble et n'ont pas besoin d'une telle fonctionnalité).
- ne connaissent pas la substitution de processus de Bash.
Cette réponse EST pour vous , si vous:
- utilisez fréquemment des CLI externes (que ce soit par habitude ou par manque de (bonnes) alternatives natives PowerShell), en particulier lors de l'écriture de scripts.
- sont habitués et apprécient ce que la substitution de processus de Bash peut faire.
- Mise à jour : Maintenant que PowerShell est également pris en charge sur les plates-formes Unix, cette fonctionnalité présente un intérêt croissant - voir cette demande de fonctionnalité sur GitHub, ce qui suggère que PowerShell implémente une fonctionnalité similaire à la substitution de processus.
Dans le monde Unix, dans Bash / Ksh / Zsh, une substitution de processus propose de traiter la sortie de commande comme s'il s'agissait d'un fichier temporaire qui se nettoie après lui-même; par exemple cat <(echo 'hello')
, où cat
voit la sortie de la echo
commande comme le chemin d'un fichier temporaire contenant la sortie de la commande .
Bien que les commandes natives de PowerShell n'aient pas vraiment besoin d'une telle fonctionnalité, cela peut être pratique lorsque vous traitez avec des CLI externes .
Émuler la fonctionnalité dans PowerShell est lourd , mais peut en valoir la peine, si vous en avez souvent besoin.
Imaginez une fonction nommée cf
qui accepte un bloc de script, exécute le bloc et écrit sa sortie dans un temp. fichier créé à la demande et retourne le temp. chemin du fichier ; par exemple:
findstr.exe "Windows" (cf { Get-ChildItem c:\ }) # findstr sees the temp. file's path.
Il s'agit d'un exemple simple qui n'illustre pas bien la nécessité d'une telle fonctionnalité. Un scénario plus convaincant est peut-être l'utilisation des psftp.exe
transferts SFTP: son utilisation par lots (automatisée) nécessite de fournir un fichier d' entrée contenant les commandes souhaitées, tandis que ces commandes peuvent facilement être créées sous forme de chaîne à la volée.
Afin d'être aussi largement compatible avec les utilitaires externes que possible, le temp. fichier doit utiliser UTF-8 codage sans une nomenclature (marque d'ordre d' octet) par défaut, mais vous pouvez demander une nomenclature UTF-8 avec -BOM
, le cas échéant.
Malheureusement, l' aspect de nettoyage automatique des substitutions de processus ne peut pas être directement émulé, donc un appel de nettoyage explicite est nécessaire ; le nettoyage est effectué en appelant cf
sans arguments :
Pour une utilisation interactive , vous pouvez automatiser le nettoyage en ajoutant l'appel de nettoyage à votre prompt
fonction comme suit (la prompt
fonction renvoie la chaîne d' invite , mais peut également être utilisée pour exécuter des commandes en arrière-plan chaque fois que l'invite est affichée, semblable à Bash's $PROMPT_COMMAND
variable); pour la disponibilité dans toute session interactive, ajoutez les éléments suivants ainsi que la définition de cf
ci - dessous à votre profil PowerShell:
"function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
Invoke-Expression
Pour une utilisation dans les scripts , pour garantir que le nettoyage est effectué, le bloc qui utilise cf
- potentiellement le script entier - doit être encapsulé dans un bloc try
/ finally
, dans lequel cf
sans arguments est appelé pour le nettoyage:
# 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.
}
Voici la mise en œuvre : fonctions avancées ConvertTo-TempFile
et son alias succinct, cf
:
Remarque : L'utilisation de New-Module
, qui nécessite PSv3 +, pour définir la fonction via un module dynamique garantit qu'il ne peut y avoir de conflits de variables entre les paramètres de fonction et les variables référencées à l'intérieur du bloc de script passé.
$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
}
}
}
Notez la possibilité de spécifier éventuellement un chemin de sortie [fichier] et / ou une extension de nom de fichier.