Этот ответ НЕ для вас , если вы:
- редко, если вообще когда-либо, нуждаетесь в использовании внешних интерфейсов командной строки (к которым, как правило, стоит стремиться - собственные команды PowerShell играют намного лучше вместе и не нуждаются в такой возможности).
- не знакомы с заменой процесса Баша.
Этот ответ для вас , если вы:
- часто используете внешние CLI (будь то по привычке или из-за отсутствия (хороших) альтернатив PowerShell-нативных), особенно при написании сценариев.
- привыкли и ценят то, что может сделать замена процесса в Bash.
- Обновление : теперь, когда PowerShell поддерживается и на платформах Unix, эта функция вызывает все больший интерес - см. Этот запрос на GitHub.Это означает, что PowerShell реализует функцию, аналогичную процессу замещения.
В мире Unix, в Bash / Ksh / Zsh, подстановка процесса предлагает обрабатывать вывод команды, как если бы это был временный файл, который очищается после себя; например cat <(echo 'hello')
, где cat
видит выходные данные echo
команды как путь к временному файлу, содержащему выходные данные команды .
Хотя для собственных команд PowerShell нет реальной необходимости в такой функции, она может быть полезна при работе с внешними интерфейсами командной строки .
Эмуляция функции в PowerShell громоздка , но, возможно, она того стоит, если вам часто это нужно.
Представьте себе функцию с именем, cf
которая принимает блок скрипта, выполняет блок и записывает свои выходные данные в temp. файл создается по требованию и возвращает темп. путь к файлу ; например:
findstr.exe "Windows" (cf { Get-ChildItem c:\ }) # findstr sees the temp. file's path.
Это простой пример, который не иллюстрирует необходимость такой функции. Возможно, более убедительным сценарием является использование psftp.exe
для передач SFTP: его пакетное (автоматическое) использование требует предоставления входного файла, содержащего нужные команды, тогда как такие команды могут быть легко созданы в виде строки на лету.
Чтобы быть максимально совместимым с внешними утилитами, насколько это возможно, временная. Файл должен использовать UTF-8 кодировке без BOM (метка порядка байтов) по умолчанию, хотя вы можете запросить BOM UTF-8 с -BOM
, если это необходимо.
К сожалению, аспект автоматической очистки замен процесса не может быть напрямую эмулирован, поэтому необходим явный вызов очистки ; уборка производится по телефонуcf
без аргументов :
Для интерактивного использования вы можете автоматизировать очистку, добавив вызов очистки в вашу prompt
функцию следующим образом ( prompt
функция возвращает строку приглашения , но ее также можно использовать для выполнения закулисных команд при каждом отображении приглашения, аналогично Bash.$PROMPT_COMMAND
переменная); для доступности в любом интерактивном сеансе добавьте следующее, а также приведенное cf
ниже определение в свой профиль PowerShell:
"function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
Invoke-Expression
Для использования в сценариях , чтобы гарантировать выполнение очистки, блок, который использует cf
- возможно весь сценарий - должен быть заключен в блок try
/ finally
, в котором cf
без аргументов вызывается для очистки:
# 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.
}
Вот реализация : расширенная функция ConvertTo-TempFile
и ее краткий псевдоним cf
:
Примечание : использование New-Module
, которое требует PSv3 +, для определения функции через динамический модуль гарантирует, что не может быть никаких переменных конфликтов между параметрами функции и переменными, на которые есть ссылки в переданном блоке скрипта.
$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
}
}
}
Обратите внимание на возможность при желании указать выходной путь [файл] и / или расширение имени файла.