Powershell tương đương với sự thay thế quá trình của Bash


14

Bash đã <(..)thay thế quá trình. Tương đương của Powershell là gì?

Tôi biết là có $(...), nhưng nó trả về một chuỗi, trong khi <(..)trả về một tệp mà lệnh bên ngoài có thể đọc từ đó, đó là những gì nó mong đợi.

Tôi cũng không tìm kiếm một giải pháp dựa trên đường ống, nhưng một cái gì đó tôi có thể dính vào giữa dòng lệnh.


3
afaik không có điều đó, nhưng sẽ rất thú vị khi được chứng minh là sai.
Zoredache

4
Bạn có thể đưa ra một ví dụ giả về cách bạn mong đợi nó sẽ được sử dụng không? Tôi đang tự hỏi nếu $ (... | select -Exandproperty objectyouwanttopass) có thể phù hợp với một trường hợp thay thế duy nhất.
Andy

2
Trong PowerShell $ () là toán tử phụ, bạn có thể sử dụng nó như thế này: Write-Output "The BITS service is $(Get-Service bits | select -ExpandProperty Stauts)"để có được trạng thái của dịch vụ BITS mà không cần tải nó vào một biến trước tiên. Nhìn vào Thay thế quy trình, điều này không hoàn toàn giống nhau, nhưng nó vẫn có thể giải quyết vấn đề bạn gặp phải
mortenya

@Andy: Tính năng này sẽ giúp với các tiện ích bên ngoài yêu cầu toán hạng tên tệp . Một ví dụ là psftp.execho chuyển SFTP: -btùy chọn của nó yêu cầu bạn cung cấp các lệnh để chạy trên máy chủ thông qua một tệp , điều này bất tiện, nếu bạn chỉ muốn chạy, giả sử , mget *. Nếu PowerShell thay thế quy trình, bạn có thể làm điều gì đó như thế psftp.exe -l user -p password somehost -b <( "mget *" ).
mkuity 24/1/2016

Câu trả lời:


4

Câu trả lời này KHÔNG dành cho bạn , nếu bạn:
- hiếm khi, nếu cần, sử dụng CLI bên ngoài (thường đáng để phấn đấu - Các lệnh gốc PowerShell chơi tốt hơn nhiều và không cần tính năng như vậy).
- không quen thuộc với quá trình thay thế của Bash.
Câu trả lời này là dành cho bạn , nếu bạn:
- thường xuyên sử dụng CLI bên ngoài (cho dù không theo thói quen hoặc do thiếu các lựa chọn thay thế gốc PowerShell), đặc biệt là trong khi viết tập lệnh.
- được sử dụng và đánh giá cao những gì thay thế quá trình của Bash có thể làm.
- Cập nhật : Hiện tại PowerShell cũng được hỗ trợ trên các nền tảng Unix, tính năng này ngày càng được quan tâm - xem yêu cầu tính năng này trên GitHub, điều này cho thấy PowerShell triển khai một tính năng gần giống với quá trình thay thế.

Trong thế giới Unix, trong Bash / Ksh / Zsh, một sự thay thế quá trình đang cung cấp xử lý đầu ra lệnh như thể nó là một tệp tạm thời tự dọn sạch; ví dụ cat <(echo 'hello'), trong đó catxem đầu ra từ echolệnh là đường dẫn của tệp tạm thời chứa đầu ra lệnh .

Mặc dù các lệnh gốc PowerShell không có nhu cầu thực sự cho tính năng như vậy, nhưng nó có thể hữu ích khi xử lý các CLI bên ngoài .

Mô phỏng tính năng trong PowerShell rất cồng kềnh , nhưng có thể đáng giá, nếu bạn thấy mình cần nó thường xuyên.

Hình ảnh một chức năng có tên cfchấp nhận một khối tập lệnh, thực thi khối và ghi đầu ra của nó vào một temp. tập tin được tạo theo yêu cầu và trả về temp. đường dẫn của tập tin ; ví dụ:

 findstr.exe "Windows" (cf { Get-ChildItem c:\ }) # findstr sees the temp. file's path.

Đây là một ví dụ đơn giản không minh họa sự cần thiết của một tính năng như vậy. Có lẽ một kịch bản thuyết phục hơn là việc sử dụng psftp.exeđể chuyển SFTP: việc sử dụng lô (tự động) của nó yêu cầu cung cấp một tệp đầu vào có chứa các lệnh mong muốn, trong khi các lệnh như vậy có thể dễ dàng được tạo thành một chuỗi khi đang di chuyển.

Vì vậy, để tương thích rộng rãi với các tiện ích bên ngoài càng tốt, temp. tập tin nên sử dụng UTF-8 mã hóa mà không có một BOM (byte-trật tự dấu) theo mặc định, mặc dù bạn có thể yêu cầu một BOM UTF-8 với -BOM, nếu cần thiết.

Thật không may, khía cạnh dọn dẹp tự động của các thay thế quá trình không thể được mô phỏng trực tiếp , vì vậy cần có một cuộc gọi dọn dẹp rõ ràng ; dọn dẹp được thực hiện bằng cách gọi cf mà không có đối số :

  • Để sử dụng tương tác , bạn có thể tự động hóa việc dọn dẹp bằng cách thêm lệnh gọi dọn dẹp vào promptchức năng của mình như sau ( prompthàm trả về chuỗi dấu nhắc , nhưng cũng có thể được sử dụng để thực hiện các lệnh phía sau hậu trường mỗi khi dấu nhắc được hiển thị, tương tự như của Bash $PROMPT_COMMANDBiến đổi); để có sẵn trong bất kỳ phiên tương tác nào, hãy thêm thông tin sau đây cũng như định nghĩa cfbên dưới vào hồ sơ PowerShell của bạn:

    "function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
      Invoke-Expression
    
  • Để sử dụng trong các tập lệnh , để đảm bảo việc dọn dẹp được thực hiện, khối sử dụng cf- có khả năng là toàn bộ tập lệnh - cần phải được gói trong một khối try/ finallytrong đó cfkhông có đối số được gọi để dọn dẹp:

# 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.
}

Đây là cách triển khai : chức năng nâng cao ConvertTo-TempFilevà bí danh cô đọng của nó , cf:

Lưu ý : Việc sử dụng New-Module, yêu cầu PSv3 +, để xác định hàm thông qua mô-đun động đảm bảo rằng không thể có xung đột biến giữa các tham số chức năng và các biến được tham chiếu bên trong khối tập lệnh được truyền.

$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
    }
  }
}

Lưu ý khả năng tùy chọn chỉ định đường dẫn [tệp] đầu ra và / hoặc phần mở rộng tên tệp.


Ý tưởng mà bạn sẽ cần phải làm điều này là không rõ ràng nhất, và đơn giản là sẽ khiến mọi thứ trở nên khó khăn hơn vì đơn giản là không muốn sử dụng PowerShell.
Jim B

1
@JimB: Cá nhân tôi sử dụng nó với psftp.exe, đó là điều thôi thúc tôi viết nó. Mặc dù tốt hơn là nên làm mọi thứ tự nhiên trong PowerShell, nhưng điều đó không phải lúc nào cũng có thể; gọi CLI bên ngoài từ PowerShell và sẽ tiếp tục xảy ra; nếu bạn thấy mình liên tục xử lý các CLI yêu cầu nhập tệp có thể dễ dàng được xây dựng trong bộ nhớ / bằng một lệnh khác, thì hàm trong câu trả lời này có thể giúp cuộc sống của bạn dễ dàng hơn.
mkuity

Bạn đang nói đùa? không có điều đó là bắt buộc Tôi vẫn chưa tìm thấy một lệnh chỉ chấp nhận các tệp có lệnh cho tham số. Theo như SFTP, một tìm kiếm đơn giản đã cho tôi thấy 2 cụm addin đơn giản để thực hiện FTP trong PowerShell.
Jim B

1
@JimB: Nếu bạn muốn tiếp tục cuộc trò chuyện này theo cách xây dựng, hãy thay đổi giọng điệu của bạn.
mkuity

2
@JimB GNU Diffutils diff chỉ hoạt động trên các tệp, trong trường hợp bạn bị giới hạn.
Pavel

2

Khi không được đặt trong dấu ngoặc kép, $(...)trả về Đối tượng PowerShell (hay đúng hơn, bất cứ điều gì được trả về bởi mã được đính kèm), trước tiên hãy đánh giá mã được đính kèm. Điều này phải phù hợp với mục đích của bạn ("một cái gì đó [I] có thể dính ở giữa dòng lệnh"), giả sử rằng dòng lệnh là PowerShell.

Bạn có thể kiểm tra điều này bằng cách dẫn các phiên bản khác nhau đến Get-Member, hoặc thậm chí chỉ xuất nó trực tiếp.

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 #>

Khi được đặt trong dấu ngoặc kép, như bạn đã nhận thấy, `" $ (...) "sẽ chỉ trả về một chuỗi.

Theo cách này, nếu bạn muốn chèn, giả sử, nội dung của tệp trực tiếp trên một dòng, bạn có thể sử dụng một cái gì đó như:

Invoke-Command -ComputerName (Get-Content C:\Temp\Files\new1.txt) -ScriptBlock {<# something #>}

Đây là một câu trả lời tuyệt vời !!
GregL

Những gì bạn mô tả không tương đương với sự thay thế quá trình của Bash. Quá trình thay thế được thiết kế để sử dụng với các lệnh yêu cầu toán hạng tên tệp ; nghĩa là, đầu ra từ một lệnh được bao trong thay thế quá trình, nói một cách lỏng lẻo, được ghi vào một tệp tạm thời và đường dẫn của tệp đó được trả về; ngoài ra, sự tồn tại của tệp nằm trong phạm vi lệnh thay thế quy trình là một phần của. Nếu PowerShell có một tính năng như vậy, bạn sẽ mong đợi một cái gì đó như sau sẽ hoạt động:Get-Content <(Get-ChildItem)
sửa đổi vào

Vui lòng sửa lại cho tôi nếu tôi sai và đây không phải là điều bạn đang tìm kiếm, nhưng không Get-ChildItem | Get-Contenthoạt động tốt? Hoặc bạn có thể thử Get-Content (Get-ChildItem).FullNamecho hiệu quả tương tự? Bạn có thể đang tiếp cận điều này từ một quan điểm bị ảnh hưởng triệt để bởi một cách tiếp cận kịch bản khác.
James Ruskin

1
Vâng, trong vương quốc PowerShell không cần tính năng này; nó chỉ được quan tâm để sử dụng với các CLI bên ngoài yêu cầu nhập tệp và trong đó nội dung của các tệp đó được xây dựng dễ dàng bằng lệnh (PowerShell). Xem nhận xét của tôi về câu hỏi cho một ví dụ thực tế. Bạn có thể không bao giờ cần một tính năng như vậy, nhưng đối với những người thường xuyên cần gọi CLI bên ngoài thì đó là điều đáng quan tâm. Ít nhất bạn nên mở đầu câu trả lời của mình bằng cách nói rằng bạn đang thể hiện cách làm việc của PowerShell - trái ngược với những gì OP đặc biệt yêu cầu - và tại sao bạn lại làm như vậy.
mkuity 27/1/2016
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.