Esta resposta NÃO é para você , se:
- raramente, se alguma vez precisar usar CLIs externas (o que geralmente vale a pena procurar - os comandos nativos do PowerShell funcionam muito melhor juntos e não precisam desse recurso).
- não está familiarizado com a substituição de processos de Bash.
Esta resposta é para você , se você:
- costuma usar CLIs externas (por hábito ou devido à falta de (boas) alternativas nativas do PowerShell), especialmente ao escrever scripts.
- estão acostumados e apreciam o que a substituição de processos do Bash pode fazer.
- Atualização : agora que o PowerShell também é suportado nas plataformas Unix, esse recurso é de interesse crescente - consulte esta solicitação de recurso no GitHub, o que sugere que o PowerShell implemente um recurso semelhante ao processo de substituição.
No mundo Unix, no Bash / Ksh / Zsh, uma substituição de processo oferece tratamento da saída do comando como se fosse um arquivo temporário que se limpa; por exemplo cat <(echo 'hello')
, onde cat
vê a saída do echo
comando como o caminho de um arquivo temporário que contém a saída do comando .
Embora os comandos nativos do PowerShell não tenham necessidade real desse recurso, ele pode ser útil ao lidar com CLIs externas .
Emular o recurso no PowerShell é complicado , mas pode valer a pena, se você precisar dele com frequência.
Imagine uma função nomeada cf
que aceita um bloco de script, executa o bloco e grava sua saída em um temp. arquivo criado sob demanda e retorna a temp. caminho do arquivo ; por exemplo:
findstr.exe "Windows" (cf { Get-ChildItem c:\ }) # findstr sees the temp. file's path.
Este é um exemplo simples que não ilustra bem a necessidade desse recurso. Talvez um cenário mais convincente seja o uso de psftp.exe
transferências SFTP: seu uso em lote (automatizado) requer o fornecimento de um arquivo de entrada contendo os comandos desejados, enquanto esses comandos podem ser facilmente criados como uma string em movimento.
Para ser o mais amplamente possível possível com utilitários externos, a temperatura. arquivo deve usar UTF-8 codificação sem uma BOM (marca de ordem de bytes) por padrão, mas você pode solicitar um BOM UTF-8 com -BOM
, se necessário.
Infelizmente, o aspecto de limpeza automática das substituições de processos não pode ser emulado diretamente , portanto, é necessária uma chamada de limpeza explícita ; a limpeza é realizada chamando cf
sem argumentos :
Para uso interativo , você pode automatizar a limpeza adicionando a chamada de limpeza à sua prompt
função da seguinte forma (a prompt
função retorna a sequência de prompt , mas também pode ser usada para executar comandos nos bastidores toda vez que o prompt é exibido, semelhante ao do Bash $PROMPT_COMMAND
variável); para disponibilidade em qualquer sessão interativa, adicione o seguinte e a definição cf
abaixo ao seu perfil do PowerShell:
"function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
Invoke-Expression
Para uso em scripts , para garantir que a limpeza seja realizada, o bloco que usa cf
- potencialmente todo o script - precisa ser empacotado em um bloco try
/ finally
, no qual cf
sem argumentos é chamado para limpeza:
# 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.
}
Aqui está a implementação : função avançada ConvertTo-TempFile
e seu alias sucinto cf
:
Nota : O uso de New-Module
, que requer PSv3 +, para definir a função por meio de um módulo dinâmico garante que não haja conflitos de variáveis entre os parâmetros da função e as variáveis referenciadas dentro do bloco de script passado.
$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
}
}
}
Observe a capacidade de especificar opcionalmente um caminho [arquivo] de saída e / ou extensão de nome de arquivo.