2012-10-27 39 views
7

Ho molti script. Dopo aver apportato le modifiche, mi piace eseguirle tutte per vedere se ho rotto qualcosa. Ho scritto uno script per scorrere ognuno di essi, eseguendolo su nuovi dati.Chiamare uno script PowerShell in una nuova istanza di PowerShell pulita (da un altro script)

All'interno del mio loop sono attualmente in esecuzione powershell.exe -command <path to script>. Non so se questo è il modo migliore per farlo, o se le due istanze sono completamente separate l'una dall'altra.

Qual è il modo migliore per eseguire uno script in un'istanza pulita di PowerShell? O dovrei dire "sessione"?

+0

Che cosa significa "il modo migliore per farlo" significa? Senza queste informazioni il tuo approccio con 'powershell.exe' sembra il migliore. Fa esattamente quello che ti serve, secondo la domanda. –

+0

Ho pensato che esistessero le migliori pratiche. Il tuo commento non implica, a meno che qualcun altro non ne conosca uno. Il metodo 'powershell.exe' non mi permette di ottenere la posizione dello script tramite' $ MyInvocation.MyCommand.ScriptBlock.File', a meno che non stia facendo qualcosa di sbagliato. – Vimes

+0

In realtà, la cosa MyInvocation potrebbe funzionare ... Potrei giurarlo non prima. Ora mi sto solo strappando i capelli cercando di farlo accettare i percorsi con lo spazio E non lancarlo in una nuova finestra. Ho impiegato più di 10 ore su questo, quindi qualsiasi aiuto è benvenuto. – Vimes

risposta

8

Utilizzare powershell.exe sembra essere un buon approccio ma con i suoi pro e contro, ovviamente.

Pro:

  • Ogni script viene richiamato in una sessione pulita separata.
  • Anche gli arresti anomali non interrompono l'intero processo di test.

Contro:

  • Invocare powershell.exe è un po 'lento.
  • Il test dipende dai codici di uscita ma 0 non sempre significa successo.

Nessuno dei contro è menzionato è una domanda come un potenziale problema.

Lo script demo è di seguito. È stato testato con PS v2 e v3.I nomi di script possono includere caratteri speciali come spazi, apostrofi, parentesi, apici, dollari. Uno menzionato nel requisito dei commenti è la possibilità di ottenere i percorsi di script nel loro codice. Con gli script approccio proposte possono ottenere il loro proprio percorso come $MyInvocation.MyCommand.Path

# make a script list, use the full paths or explicit relative paths 
$scripts = @(
    '.\test1.ps1' # good name 
    '.\test 2.ps1' # with a space 
    ".\test '3'.ps1" # with apostrophes 
    ".\test [4].ps1" # with brackets 
    '.\test `5`.ps1' # with backticks 
    '.\test $6.ps1' # with a dollar 
    '.\test ''3'' [4] `5` $6.ps1' # all specials 
) 

# process each script in the list 
foreach($script in $scripts) { 
    # make a command; mind &, ' around the path, and escaping ' 
    $command = "& '" + $script.Replace("'", "''") + "'" 

    # invoke the command, i.e. the script in a separate process 
    powershell.exe -command $command 

    # check for the exit code (assuming 0 is for success) 
    if ($LastExitCode) { 
     # in this demo just write a warning 
     Write-Warning "Script $script failed." 
    } 
    else { 
     Write-Host "Script $script succeeded." 
    } 
} 
+0

Questo sembra funzionare, anche nel mio codice reale! Grazie Roman, stavo sfuggendo alle mie quotazioni con back-ticks. Vedo che le mie virgolette sono interpretate da PowerShell? Inoltre, una nota per i futuri lettori, se il "pedice" è un oggetto FileInfo, dovrai aggiungere ".FullName" prima di Sostituisci. PowerShell di solito lo converte automaticamente, ma non in questo caso. – Vimes

+1

La mia prima versione prima che la modifica fosse anche con i backtick, ma è possibile anche la versione con le virgolette singole, in singole stringhe tra virgolette le virgolette singole sono sfuggite al raddoppio.Vedi anche 'help about_Quoting_Rules'. –

+0

Ah,in qualche modo non ho mai saputo della citazione tra virgolette. Leggerò! – Vimes

2

Le due istanze sono completamente separate, perché sono due processi diversi. In genere, non è il modo più efficiente per avviare un processo PowerShell per ogni esecuzione di script. A seconda del numero di script e della frequenza con cui rieseguirli, ciò potrebbe influire sul rendimento generale. Se non lo è, lascerei tutto così com'è.

Un'altra opzione sarebbe quella di eseguire nello stesso runspace (questa è una parola corretta per esso), ma pulire tutto ogni volta. Vedi this answer per un modo per farlo. Oppure utilizzare l'estratto sotto:

$sysvars = get-variable | select -Expand name 
function remove-uservars { 
get-variable | 
    where {$sysvars -notcontains $_.name} | 
    remove-variable 
} 
+0

JohnB @: domanda sulla tua taglia - in che modo la mia soluzione 'non sembra funzionare per script arbitrari? – Neolisk

+0

Penso che tu abbia suggerito due cose. Lo faccio nel modo in cui sono stato, che non gestisce i percorsi con spazi (ho lottato con le modalità di analisi, i blocchi di script, Start-Process, ecc. Ed è orrendo) e continua a lanciare una nuova finestra, rendendo difficile vedere l'output in un flusso o acquisire su file. L'altro modo è quello di eliminare le variabili, ma ciò non copre le funzioni, i tipi, i moduli e chissà cos'altro. Potrei fare qualche ricerca per capire cosa può essere creato globalmente e quindi tracciarlo a mano, ma è molto lavoro e probabilmente cambierà tra le versioni. – Vimes

+0

Qualsiasi aiuto per far funzionare la prima idea sarebbe molto apprezzato. Ho capito che gli spazi funzionano (credo), ma i processi PowerShell appena eseguiti non escono dallo schermo. – Vimes

5

Se si utilizza PowerShell 2.0 o versione successiva, è possibile utilizzare i lavori per eseguire questa operazione. Ogni lavoro viene eseguito in un processo separato PowerShell es .:

$scripts = ".\script1.ps1", ".\script2.ps1" 

$jobs = @() 
foreach ($script in $scripts) 
{ 
    $jobs += Start-Job -FilePath $script 
} 

Wait-Job $jobs 

foreach ($job in $jobs) 
{ 
    "*" * 60 
    "Status of '$($job.Command)' is $($job.State)" 
    "Script output:" 
    Receive-Job $job 
} 

Inoltre, controlla la PowerShell Community Extensions. Ha un comando Test-Script in grado di rilevare errori di sintassi in un file di script. Ovviamente, non catturerà gli errori di runtime.

+0

Mi piace questa risposta, ma '{& $ script}' provoca errori in seguito, quando si esegue Receive-Job: "L'espressione dopo '&' in un elemento della pipeline ha prodotto un oggetto che non era valido. Deve risultare in un nome di comando , blocco di script o oggetto CommandInfo ". Non so cosa c'è di sbagliato nel blocco di script. Mi sembra buono Ho scoperto il parametro '-FilePath' per Start-Job, che sembra funzionare:' Start-Job -FilePath $ script'. Aggiornerò l'esempio di codice e segnerò la risposta. Ma se hai idea del perché l'approccio del blocco di script non ha funzionato, sono curioso. – Vimes

+0

Inoltre, i miei script di produzione interrogano i propri percorsi e quindi usano percorsi relativi per trovarsi l'un l'altro. Lo faccio con 'Split-Path -Parent $ MyInvocation.MyCommand.ScriptBlock.File'. Penso che questo stia causando l'errore che sto ottenendo: "Impossibile associare l'argomento al parametro 'Path' perché è null".Non è chiaro perché non ottengo nomi di file o numeri di riga negli errori dai lavori. Sto iniziando a pensare che Jobs potrebbe non essere lo strumento giusto per eseguire script arbitrari. Fammi sapere se hai qualche idea. – Vimes

3

Un consiglio per gli utenti di PowerShell V3: noi (il team di PowerShell) abbiamo aggiunto una nuova API nella classe Runspace denominata ResetRunspace(). Questa API reimposta la tabella delle variabili globali allo stato iniziale per quello spazio di esecuzione (oltre a ripulire alcune altre cose). Quello che non fa è ripulire le definizioni di funzioni, tipi e formati di file o scaricare moduli. Ciò consente all'API di essere molto più veloce. Si noti inoltre che il Runspace deve essere stato creato utilizzando un oggetto InitialSessionState, non un'istanza RunspaceConfiguration. ResetRunspace() è stato aggiunto come parte della funzione Workflow in V3 per supportare l'esecuzione parallela in modo efficiente in uno script.

+1

Informazioni interessanti. Come lo uso? Per esempio. se chiamo * [System.Management.Automation.Runspaces.Runspace] :: DefaultRunspace.ResetRunspaceState() * allora fallisce: * Eccezione chiamando "ResetRunspaceState" con "0" argomento (i): "La pipeline non viene eseguita perché una pipeline è Le pipeline non possono essere eseguite simultaneamente * –

Problemi correlati