2015-04-10 17 views
18

ho imparato da questo StackOverflow question, che PowerShell la semantica di ritorno sono diversi diciamo dalla semantica di ritorno C# s'. Citazione dalla domanda di cui sopra:Come restituire uno e un solo valore da una funzione di PowerShell?

PowerShell ha una semantica di ritorno davvero stravagante, almeno se vista da una prospettiva di programmazione più tradizionale. Ci sono due idee principali su cui riflettere: tutto l'output viene catturato e restituito. La parola chiave return indica solo un punto di uscita logico. sguardo

Let questo esempio:

function Calculate 
{ 
    echo "Calculate" 
    return 11 
} 

$result = Calculate 

Se echo $result vi renderete conto che qualcosa non va. Ci si aspetterebbe questo:

11 

Ma la realtà, ciò che in realtà si vede, è diverso:

Calculate 
11 

Così, invece di tornare solo il valore di reintroduzione, è effettivamente ottenere indietro un array.

È possibile eliminare le istruzioni echo e non inquinare il valore restituito, ma se si chiama un'altra funzione dalla funzione, che fa eco a qualcosa, allora si è di nuovo nei guai.

La mia domanda è, come posso scrivere una funzione PowerShell, che restituisce una sola cosa. Se le funzioni integrate PowerShell restituiscono un solo valore, perché non posso farlo?

La soluzione che sto usando ora e mi piacerebbe per sbarazzarsi di:

function Calculate 
{ 
    #Every function that returns has to echo something 
    echo "" 
    return 11 
} 

#The return values is the last value from the returning array 
$result = (Calculate)[-1] 

risposta

7

Diverse risposte già fornite non rispondono completamente alla tua domanda perché non rappresentano altri cmdlet che potresti chiamare - come fai notare - che potrebbe "inquinare" il tuo output. La risposta di @Laezylion è sostanzialmente corretta, ma credo che porti ulteriore elaborazione.

È chiaro che si apprezza il fatto che la soluzione attuale - forzare ogni funzione a restituire un array e quindi prendere l'ultimo elemento - non è una soluzione molto solida. (In effetti, penso che sia sicuro chiamarlo un kludge. :-) L'approccio corretto - e una risposta diretta alla tua domanda - è chiaro ... anche se a prima vista sembra una tautologia:

Question: How do you ensure a function returns only one thing? 
Answer: By ensuring that it returns only one thing. 

Lasciatemi chiarire. Esistono in realtà due tipi di funzioni/cmdlet in PowerShell: (A) quelli che restituiscono i dati e (B) quelli che non restituiscono i dati ma possono invece riportare i messaggi di progresso o diagnostici. I problemi sorgono, come hai visto, quando provi a mescolare i due. E questo può facilmente accadere inavvertitamente. Pertanto, come autore della funzione, è tua responsabilità comprendere ogni chiamata di funzione/cmdlet all'interno della tua funzione: in particolare, restituisce qualcosa, sia esso un vero valore di ritorno o solo un output diagnostico? Ti aspetti l'output dalle funzioni di tipo A, ma devi stare attento a qualsiasi funzione di tipo B. Per chi restituisce qualcosa, devi assegnarlo a una variabile, inserirlo in una pipeline o ... farlo andare via. (Ci sono diversi modi per farlo andare via: collegarlo a Out-Null, lanciarlo a vuoto, reindirizzare a $ null o assegnarlo a $ null. Vedere Jason Archer's Stack Overflow post che valuta le prestazioni di ciascuno di questi sapori, suggerendo di allontanarti da Out-Null.)

Sì, questo approccio richiede uno sforzo maggiore, ma in questo modo si ottiene una soluzione più solida. Per un po 'più di discussione su questo problema, queste funzioni A/B, ecc. Vedi Domanda 1 su A Plethora of PowerShell Pitfalls recentemente pubblicata su Simple-Talk.com.

+2

'+ 1' per informazioni davvero utili. '-1' per non fornire una soluzione manutenibile, ma invece che implica lo status quo è accettabile: *" Pertanto, è responsabilità dell'utente, come autore della funzione, capire ogni chiamata di funzione/cmdlet all'interno della propria funzione: in particolare, restituisce qualsiasi cosa, sia un vero valore di ritorno o solo un output diagnostico? "* - persone seriamente? Quanto è pazzo? Devo sapere se qualche cosa chiamo * potrebbe * fornire informazioni diagnostiche ad un certo punto? Pfff. –

+0

Oh e potrei aggiungere, il commento non è stato quello di prenderti in giro. Piuttosto, penso di condividere il sentimento espresso da [poster Byron laggiù] (http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/#comment-112182) a http : //www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/ –

+0

Apprezzo la tua frustrazione, Martin. :-) Tuttavia, potrei suggerire che non è così male come sembra. I cmdlet di Microsoft o altre fonti commerciali non diffonderanno gli output diagnostici su willy-nilly; Ho aggiunto quella parte per essere a conoscenza dei tuoi cmdlet _own_. La parte che devi veramente ricordare è i valori di ritorno dei cmdlet. Tuttavia, come con qualsiasi API, è necessario comprendere i cmdlet che si stanno utilizzando, sia i parametri che prendono sia i valori restituiti, se presenti. –

1

È possibile sostituire l'eco (alias di scrittura ouptut) con write-host:

PS>function test{ write-host "foo";"bar"} 
PS>$a=test          
test           
PS>$a           
bar 
1

echo è un alias per Write-Output che invia l'oggetto al comando successivo.

Write-Host dovrebbe aiutare.

PS:> function Calculate 
>> { 
>> #Every function that returns has to echo something 
>> write-host "test" 
>> return 11 
>> } 
>> 
PS:> $A=Calculate 
test 
PS:> $A 
11 
4

Non utilizzare echo per scrivere le informazioni, utilizzare Write-Host, Write-Verbose o Write-Debug a seconda del messaggio.

Echo è un alias per Write-Output che lo invierà come oggetto attraverso la pipeline. Write-Host/Verbose/Debug tuttavia sono solo messaggi di console.

PS P:\> alias echo 

CommandType Name     ModuleName 
----------- ----     ---------- 
Alias  echo -> Write-Output    

Esempio:

function test { 
    Write-Host calc 
    return 11 
} 

$t = test 
"Objects returned: $($t.count)" 
$t 

#Output 
calc 
Objects returned: 1 
11 

Inoltre, se si torna al valore sull'ultima riga nella funzione, quindi non è necessario utilizzare return. Scrivere da solo l'oggetto/valore è sufficiente.

function test { 
    Write-Host calc 
    11 
} 

La differenza è che return 11 sarebbe saltare le linee dopo che lo si utilizza nel bel mezzo di una funzione.

function test { 
    return 11 
    12 
} 

test 

#Output 
11 
+0

Ciao, ho provato questo, ma poi, da qualche parte lungo la strada ho incontrato un problema, quando immagino strumento cli ha fatto eco a qualcosa che è finito nel valore di ritorno. Anche se potrei usare Write-Host nelle mie funzioni, ma anche le funzioni e gli strumenti di altri possono fare eco. Ecco perché sto usando questa '' 'echo" "' '' convetion. – boli

+1

Se non si desidera ricevere l'output da un'app o da una funzione o uno script esterni, è necessario gettare via l'output 'ipconfig | Out-Null' o salvarlo in una variabile '$ ip = ipconfig'. Se hai bisogno dell'output, salvalo in una variabile, analizzalo e restituisci il risultato quando vuoi (e nel tuo formato). Il tuo campione non mostra nessun programma esterno chiamato, dice solo che ricevi due oggetti, e questo perché scrivi informazioni.messaggi ("Calcolo") all'utente usando 'echo/Write-Output'. Se questa non è la risposta che desideri, devi modificare la tua domanda. –

+0

Nel mio test (non sono affatto un esperto di PS) 'function f {& ipconfig | Scrivi-Host; return 123} 'fa il trucco quindi suppongo che si tratti di trovare quelle fastidiose funzioni che usano' echo' etc e applicare il trattamento di cui sopra ... – sxc731

4

Vale la pena menzionare: Qualsiasi output del comando non gestito all'interno della vostra funzione finirà come valore di ritorno.

che uso per ottenere questo problema con un contenuto di funzione di gestione XML:

Function Add-Node(NodeName){ 
    $NewXmlNode = $Xml.createElement("Item") 
    $Xml.Items.appendChild($NewXmlNode) | out-null 
} 

Se si rimuove il Out-Null, il ritorno della funzione saranno le proprietà di base Nodo ... (È inoltre possibile gestire il codice di uscita del comando appendChild ... dipende dalla motivazione) :)

+0

Mi sento fuori-nulla, tutto è anche molto più lavoro del mio echo "" + returnValue [-1], ma è una buona alternativa. – boli

12

È anche possibile assegnare tutto a null poi tornare solo la variabile che si desidera:

Function TestReturn { 

$Null = @(
    Write-Output "Hi there!" 
    1 + 1 
    $host 
    $ReturnVar = 5 
) 
    return $ReturnVar 
} 
+0

Beh, non posso fingere di conoscere tutte le conseguenze che ciò potrebbe causare, ma sembra che * funzioni *. Specialmente con le librerie di terze parti come la CLI di Azure che semplicemente insistono nell'iniettare le cose nella funzione restituisce. – CarComp

Problemi correlati