Esta respuesta NO es para usted , si usted:
- rara vez, si es que alguna vez, necesita usar CLI externas (que generalmente vale la pena esforzarse - los comandos nativos de PowerShell se juegan mucho mejor juntos y no necesitan tal característica).
- No estoy familiarizado con el proceso de sustitución de Bash.
Esta respuesta ES para usted , si usted:
- utiliza con frecuencia CLI externas (ya sea por costumbre o por falta de alternativas (buenas) nativas de PowerShell), especialmente al escribir scripts.
- están acostumbrados y aprecian lo que puede hacer la sustitución del proceso de Bash.
- Actualización : ahora que PowerShell también es compatible con plataformas Unix, esta característica es de creciente interés; consulte esta solicitud de característica en GitHub, lo que sugiere que PowerShell implemente una característica similar a la sustitución de procesos.
En el mundo Unix, en Bash / Ksh / Zsh, una sustitución de proceso ofrece el tratamiento de la salida del comando como si fuera un archivo temporal que se limpia después de sí mismo; por ejemplo cat <(echo 'hello')
, donde cat
ve la salida del echo
comando como la ruta de un archivo temporal que contiene la salida del comando .
Si bien los comandos nativos de PowerShell no tienen una necesidad real de tal característica, puede ser útil cuando se trata de CLI externas .
Emular la función en PowerShell es engorroso , pero puede valer la pena si lo necesita con frecuencia.
Imagine una función llamada cf
que acepte un bloque de script, ejecute el bloque y escriba su salida en una temp. archivo creado bajo demanda y devuelve la temp ruta del archivo ; p.ej:
findstr.exe "Windows" (cf { Get-ChildItem c:\ }) # findstr sees the temp. file's path.
Este es un ejemplo simple que no ilustra bien la necesidad de tal característica. Quizás un escenario más convincente es el uso de psftp.exe
transferencias SFTP: su uso por lotes (automatizado) requiere proporcionar un archivo de entrada que contenga los comandos deseados, mientras que dichos comandos se pueden crear fácilmente como una cadena sobre la marcha.
Para ser tan ampliamente compatible con las utilidades externas como sea posible, la temp. archivo debe utilizar UTF-8 codificación sin BOM (marca de orden de bytes) de forma predeterminada, aunque se puede solicitar una lista de materiales UTF-8 con -BOM
, si es necesario.
Desafortunadamente, el aspecto de limpieza automática de las sustituciones de proceso no se puede emular directamente , por lo que se necesita una llamada de limpieza explícita ; la limpieza se realiza llamando cf
sin argumentos :
Para uso interactivo , puede automatizar la limpieza agregando la llamada de limpieza a su prompt
función de la siguiente manera (la prompt
función devuelve la cadena de solicitud , pero también se puede usar para ejecutar comandos detrás de escena cada vez que se muestra la solicitud, similar a Bash $PROMPT_COMMAND
variable); para disponibilidad en cualquier sesión interactiva, agregue lo siguiente y la definición de cf
abajo a su perfil de PowerShell:
"function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
Invoke-Expression
Para su uso en scripts , para garantizar que se realice la limpieza, el bloque que usa cf
, potencialmente todo el script, debe estar envuelto en un bloque try
/ finally
, en el que cf
sin argumentos se requiere la limpieza:
# 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.
}
Aquí está la implementación : función avanzada ConvertTo-TempFile
y su breve alias cf
:
Nota : El uso de New-Module
, que requiere PSv3 +, para definir la función a través de un módulo dinámico asegura que no pueda haber conflictos de variables entre los parámetros de la función y las variables referenciadas dentro del bloque de script pasado.
$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
}
}
}
Tenga en cuenta la capacidad de especificar opcionalmente una ruta de salida [archivo] y / o una extensión de nombre de archivo.