Ta odpowiedź NIE jest dla Ciebie , jeśli:
- rzadko, jeśli w ogóle, potrzebujesz zewnętrznych interfejsów CLI (o co ogólnie warto starać się - komendy natywne PowerShell grają znacznie lepiej razem i nie potrzebują takiej funkcji).
- nie są zaznajomieni z podstawieniem procesu Basha.
Ta odpowiedź JEST dla Ciebie , jeśli:
- często korzystasz z zewnętrznych interfejsów CLI (czy to z przyzwyczajenia, czy z powodu braku (dobrych) alternatyw natywnych dla PowerShell), szczególnie podczas pisania skryptów.
- są przyzwyczajeni i doceniają to, co może zastąpić proces Bash.
- Aktualizacja : Teraz, gdy PowerShell jest także obsługiwany na platformach Unix, ta funkcja jest coraz bardziej interesująca - zobacz to żądanie funkcji na GitHub, co sugeruje, że PowerShell implementuje funkcję podobną do zastępowania procesów.
W świecie uniksowym, w Bash / Ksh / Zsh, podstawienie procesu oferuje traktowanie wyniku polecenia tak, jakby był to plik tymczasowy , który czyści się po sobie; np. cat <(echo 'hello')
gdzie cat
widzi wynik echo
polecenia jako ścieżkę pliku tymczasowego zawierającego wynik polecenia .
Chociaż natywne polecenia programu PowerShell nie potrzebują takiej funkcji, może być przydatne podczas obsługi zewnętrznych interfejsów CLI .
Emulowanie funkcji w PowerShell jest uciążliwe , ale może być tego warte, jeśli często go potrzebujesz.
Wyobraź sobie funkcję o nazwie, cf
która akceptuje blok skryptu, wykonuje blok i zapisuje dane wyjściowe w temp. plik tworzony na żądanie i zwraca temp. ścieżka pliku ; na przykład:
findstr.exe "Windows" (cf { Get-ChildItem c:\ }) # findstr sees the temp. file's path.
To prosty przykład, który nie ilustruje dobrze potrzeby takiej funkcji. Być może bardziej przekonującym scenariuszem jest użycie psftp.exe
do przesyłania SFTP: jego użycie wsadowe (zautomatyzowane) wymaga udostępnienia pliku wejściowego zawierającego pożądane polecenia, podczas gdy takie polecenia można łatwo utworzyć w postaci ciągu w locie.
Aby być możliwie jak najbardziej kompatybilnym z zewnętrznymi narzędziami, temp. Plik powinien używać UTF-8 kodowanie bez BOM (znacznikiem kolejności bajtów) domyślnie, ale można poprosić o UTF-8 BOM się -BOM
, w razie potrzeby.
Niestety aspekt automatycznego czyszczenia zastępowania procesów nie może być bezpośrednio emulowany, dlatego konieczne jest jawne wywołanie czyszczenia ; czyszczenie odbywa się przez wywołanie cf
bez argumentów :
Do użytku interaktywnego można zautomatyzować czyszczenie, dodając wywołanie prompt
funkcji czyszczenia do funkcji w następujący sposób ( prompt
funkcja zwraca ciąg znaków zachęty , ale można jej także używać do wykonywania za sceną poleceń za każdym razem, gdy wyświetlany jest monit, podobnie jak polecenie Basha $PROMPT_COMMAND
zmienna); w celu zapewnienia dostępności w dowolnej sesji interaktywnej dodaj następujące oraz definicję cf
poniżej do swojego profilu PowerShell:
"function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
Invoke-Expression
W przypadku użycia w skryptach , aby zapewnić wykonanie czyszczenia, blok, który używa cf
- potencjalnie cały skrypt - musi być owinięty w try
/ finally
block, w którym cf
bez argumentów wywoływane jest czyszczenie:
# 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.
}
Oto implementacja : funkcja zaawansowana ConvertTo-TempFile
i jej zwięzły alias cf
:
Uwaga : Zastosowanie New-Module
, które wymaga PSv3 +, do zdefiniowania funkcji za pomocą modułu dynamicznego zapewnia, że nie może wystąpić konflikt zmiennych między parametrami funkcji a zmiennymi, do których odwołuje się wewnątrz bloku skryptu.
$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
}
}
}
Zwróć uwagę na możliwość opcjonalnego określenia ścieżki wyjściowej [pliku] i / lub rozszerzenia nazwy pliku.