Эквивалент Powershell замене процесса Bash

Bash имеет <(..) для замены процесса. Что Powershell эквивалентен?

Я знаю, что существует $(...), но это возвращает строку, в то время как <(..) возвращает файл, из которого может читать внешняя команда, который является тем, что она ожидает.

Я также не ищу основанное на канале решение, но что-то, что я могу засунуть посреди командной строки.

14
задан 7 May 2015 в 21:39
2 ответа

Если не заключен в двойные кавычки, $ (...) возвращает объект PowerShell (или, скорее, все, что возвращается вложенным кодом), оценивая заключенный код. первый. Это должно быть подходящим для ваших целей («что-то [я] может вставить в середину командной строки»), при условии, что командной строкой является PowerShell.

Вы можете проверить это, связав различные версии с Get- Member , или даже просто выводит его напрямую.

PS> "$(ls C:\Temp\Files)"
new1.txt new2.txt

PS> $(ls C:\Temp\Files)


    Directory: C:\Temp\Files


Mode                LastWriteTime         Length Name                                                                      
----                -------------         ------ ----                                                                      
-a----       02/06/2015     14:58              0 new1.txt                                                                  
-a----       02/06/2015     14:58              0 new2.txt   

PS> "$(ls C:\Temp\Files)" | gm


   TypeName: System.String
<# snip #>

PS> $(ls C:\Temp\Files) | gm


   TypeName: System.IO.FileInfo
<# snip #>

Заключенный в двойные кавычки, как вы заметили, «$ (...)» просто вернет строку.

Таким образом, если вы хотели вставить, скажем,содержимое файла прямо в строке, вы можете использовать что-то вроде:

Invoke-Command -ComputerName (Get-Content C:\Temp\Files\new1.txt) -ScriptBlock {<# something #>}
2
ответ дан 2 December 2019 в 21:13

Этот ответ НЕ для вас , если вы:
- редко, если вообще когда-либо, необходимо использовать внешние интерфейсы командной строки (к чему обычно стоит стремиться - собственные команды PowerShell намного лучше работают вместе и не нуждаются в такой функции).
- не знакомы с подстановкой процесса Башом.
Этот ответ ЕСТЬ для вас , если вы:
- часто использовать внешние интерфейсы командной строки (по привычке или из-за отсутствия (хороших) альтернатив PowerShell), особенно при написании сценариев.
- привыкли и ценят, на что способна подмена процесса Баша.
- Обновление : теперь, когда 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: его пакетное (автоматическое) использование требует предоставления входного файла , содержащего желаемые команды, тогда как такие команды можно легко создается как строка на лету.

Чтобы быть максимально совместимой с внешними утилитами, temp. файл должен использовать кодировку UTF-8 без спецификации (метка порядка байтов) по умолчанию, хотя вы можете запросить спецификацию UTF-8 с -BOM , при необходимости.

К сожалению, аспект автоматической очистки подстановки процессов не может быть напрямую эмулирован, поэтому требуется явный вызов очистки ; очистка выполняется путем вызова cf без аргументов :

  • Для интерактивного использования,вы можете автоматизировать очистку, добавив вызов очистки к своей функции prompt следующим образом (функция prompt ] возвращает строку приглашения , но также может использоваться для выполнения скрытых команд каждый раз, когда отображается приглашение, аналогично переменной Bash $ PROMPT_COMMAND ); для доступности в любом интерактивном сеансе добавьте следующее, а также определение cf ниже в свой профиль PowerShell:

     "функциональная подсказка {cf 4>` $ null; $ ((get-item function  : prompt) .definition)} "|
      Вызов-выражение
     
  • Для использования в сценариях , чтобы гарантировать выполнение очистки, блок, который использует 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
    }
  }
}

Обратите внимание на возможность дополнительно указать путь к выходному [файлу] и / или расширение имени файла.

4
ответ дан 2 December 2019 в 21:13

Теги

Похожие вопросы