2016-11-30 17 views
2

Ich lerne Skripte mit PowerShell zu schreiben, und ich fand diesen Code, der mir bei einem Projekt helfen wird Beispiel kommt von Is there a one-liner for using default values with Read-Host?.

$defaultValue = 'default' 

$prompt = Read-Host "Press enter to accept the default [$($defaultValue)]" 

$prompt = ($defaultValue,$prompt)[[bool]$prompt] 

Ich glaube, ich verstehe, dass $prompt = ($defaultValue,$prompt) ein Array mit zwei Elementen zu schaffen, und dass der [bool] Teil zwingt den $prompt Datentyp Boolean, aber ich verstehe nicht, was das dritte Codezeile funktioniert als Ganze .

Antwort

2

Casting $prompt zu [bool] erzeugt einen Wert von $true oder $false je nachdem, ob die Variable leer ist ($null oder leere Zeichenkette sowohl $false werden) oder nicht (nicht-leer geworden $true strings).

[bool]''   → $false 
[bool]'something' → $true

die boolesche Wert in dem Index-Operator dann wirft implizit den Wert auf eine ganze Zahl in dem $false 0 wird, und $true 1 wird, damit die erste oder zweite Element des Arrays auswählt.

[int]$false → 0 
[int]$true → 1
($defaultValue,$prompt)[0] → $defaultValue 
($defaultValue,$prompt)[1] → $prompt
4

Dies ist ein gemeinsames Programmiermuster:

if (user entered a price) 
{ 
    price = user entered value 
} 
else 
{ 
    price = default value 
} 

und weil das ist durchaus üblich, und auch langatmig, haben einige Sprachen ein besonderes ternary operator, dass Code alles viel prägnanter schreiben und weisen Sie einer Variable "diesen Wert oder diesen Wert" in einer Bewegung zu. z.B. in C# können Sie schreiben:

price = (user entered a price) ? (user entered value) : (default value) 
# var = IF [boolean test] ? THEN (x)   ELSE (y) 

und die ? weist (x), wenn der Test wahr ist, und (y), wenn der Test falsch ist.

In Python ist es geschrieben:

price = (user entered value) if (user entered a price) else (default value) 

Und in Powershell, es ist geschrieben:

# you can't have a ternary operator in PowerShell, because reasons. 

Ja. Kein nettes Short-Code-Muster erlaubt.

Aber etwas Sie tun können, ist Missbrauch Array-Indizierung (@('x', 'y')[0] is 'x' und @('x', 'y')[1] is 'y' und) und schreiben, dass hässlich und verwirrend Code-Golf-line:

$price = ($defaultValue,$userValue)[[bool]$UserEnteredPrice] 

# var (x,y) is an array   $array[ ] is array indexing 
     (0,1) are the array indexes of the two values 

            [bool]$UserEnteredPrice casts the 'test' part to a True/False value 
            [True/False] used as indexes into an array indexing makes no sense 
                so they implicitly cast to integers, and become 0/1 

# so if the test is true, the $UserValue is assigned to $price, and if the test fails, the $DefaultValue is assigned to price. 

Und es verhält sich wie ein ternärer Operator, außer es ist verwirrend und hässlich und in einigen Situationen wird es Sie stolpern, wenn Sie nicht vorsichtig sind, indem Sie beide Array-Ausdrücke unabhängig davon auswerten, welche ausgewählt wurde (anders als echte ?-Operatoren).


bearbeiten: Was ich sollte wirklich hinzuzufügen, ist ein Powershell-Form ziehe ich - das Ergebnis eines if Test zuordnen können direkt in Powershell und zu tun:

$price = if ($userValue) { $userValue } else { $DefaultValue } 

# -> 

$prompt = if ($prompt) { $prompt } else { $DefaultValue } 
+0

Danke für die ausführliche Antwort und die Informationen über andere Programmiersprachen. – Cliff

0

Um Ergänzung die große Antworten gegeben by Ansgar Wiechers und by TessellatingHeckler:

Es würde sein grea t wenn Powershell hatten Operatoren für ternary conditionals und null-coalescing, wie (in der Frage auf das Beispiel angewandt) folgt:

# Ternary conditional 
# Note: does NOT work in PowerShell as of PSv5.1 
$prompt = $prompt ? $prompt : $defaultValue 

# Or, more succinctly, with null coalescence: 
# Note: does NOT work in PowerShell as of PSv5.1 
# (Note: This example assumes that $prompt will be $null in the default 
#  case, whereas the code in the question actually assigns the 
#  empty string to $prompt if the user just presses Enter.) 
$prompt = $prompt ?? $defaultValue 

Leider ist diese ausdrucke Konstrukte sind (noch) nicht Teil der Powershell, und es scheint, dass Das PowerShell-Team hat einen längeren Zeitraum der Enttäuschung eingeleitet, der seit diesem Schreiben fast zehn Jahre gedauert hat:

Bei Microsoft "zu versenden ist zu wählen". Eines der Dinge, über die wir sehr enttäuscht waren, dass wir in V1.0 nicht liefern konnten, ist ein ternärer Operator.

Aus PowerShell Team blog post vom 29. Dezember 2006

Am selben Blog-Post bietet Lückenbüßer in Form von Funktionen dass (unvollkommen) diese Operatoren emulieren.

Der Versuch, eine Sprache zu "reparieren", ist jedoch immer ein schwieriges Geschäft, also hoffen wir auf eine korrekte Implementierung eines Tages.

Im Folgenden sind Versionen der Funktionen aus der Blog-Post mit zugehörigen Alias-Definitionen angepasst, deren Verwendung erlaubt dann die folgende Lösung:

# Ternary conditional - note how the alias must come *first* 
# Note: Requires the function and alias defined below. 
$prompt = ?: $prompt $prompt $defaultValue 

# Or, more succinctly, with null coalescence - note how the alias must come *first* 
# Note: Requires the function and alias defined below. 
$prompt = ?? $prompt $defaultValue 

Quellcode:

Beachten Sie, dass die die tatsächlichen Funktionen sind ziemlich kurz; Es ist die Kommentar-basierte Hilfe, die diese Auflistung langwierig macht.

Set-Alias ?: Invoke-Ternary -Option AllScope 
<# 
.SYNOPSIS 
Emulation of a ternary conditional operator. 

.DESCRIPTION 
An emulation of the still-missing-from-the-PS-language ternary conditional, 
such as the C-style <predicate> ? <if-true> : <if-false> 

Because a function is used for emulation, however, the function name must 
come first in the invocation. 

If you define a succinct alias, e.g., set-alias ?: Invoke-Ternary, 
concise in-line conditionals become possible. 

To specify something other than a literal or a variable reference, pass a 
script block for any of the tree operands. 
A predicate script block is of necessity always evaluated, but a script block 
passed to the true or false branch is only evaluated on demand. 

.EXAMPLE 
> Invoke-Ternary { 2 -lt 3 } 'yes' 'no' 

Evaluates the predicate script block, which outputs $true, and therefore 
selects and outputs the true-case expression, string 'yes'. 

.EXAMPLE 
> Invoke-Ternary $false { $global:foo = 'bar' } { Get-Date } 

Outputs the result of executing Get-Date. 
Note that the true-case script block is NOT evaluated in this case. 

.NOTES 
Gratefully adapted from http://blogs.msdn.com/powershell/archive/2006/12/29/dyi-ternary-operator.aspx 
#> 
function Invoke-Ternary 
{ 
    [CmdletBinding()] 
    param($Predicate, $Then, $Otherwise = $null) 

    if ($(if ($Predicate -is [scriptblock]) { & $Predicate } else { $Predicate })) { 
    if ($Then -is [ScriptBlock]) { & $Then } else { $Then } 
    } else { 
    if ($Otherwise -is [ScriptBlock]) { & $Otherwise } else { $Otherwise } 
    } 
} 


Set-Alias ?? Invoke-NullCoalescence -Option AllScope 
<# 
.SYNOPSIS 
Emulation of a null-coalescence operator. 

.DESCRIPTION 
An emulation of a null-coalescence operator such as the following: 
<expr> ?? <alternative-expr-if-expr-is-null> 

Because a function is used for emulation, however, the function name must 
come first in the invocation. 

If you define a succinct alias, e.g., set-alias ?? Invoke-NullCoalescence, 
concise in-line null-coalescing becomes possible. 

To specify something other than a literal or a variable reference, pass a 
script block for any of the two operands. 
A first-operand script block is of necessity always evaluated, but a 
second-operand script block is only evaluated on demand. 

Note that only a true $null value in the first operand causes the second 
operand to be returned. 

.EXAMPLE 
> Invoke-NullCoalescence $null '(empty)' 

Since the first operand is $null, the second operand, string '(empty)', is 
output. 

.EXAMPLE 
> Invoke-NullCoalescence '' { $global:foo = 'bar' } 

Outputs the first operand, the empty string, because it is not $null. 
Note that the second-operand script block is NOT evaluated in this case. 

.NOTES 
Gratefully adapted from http://blogs.msdn.com/powershell/archive/2006/12/29/dyi-ternary-operator.aspx 
#> 
function Invoke-NullCoalescence 
{ 
    [CmdletBinding()] 
    param($Value, $Alternative) 

    if ($Value -is [scriptblock]) { $Value = & $Value } 

    if ($null -ne $Value) { 
    $Value 
    } else { 
    if ($Alternative -is [ScriptBlock]) { & $Alternative } else { $Alternative } 
    } 
}