PowerShell: esegui il comando dalla directory dello script


144

Ho uno script PowerShell che fa alcune cose usando la directory corrente dello script. Quindi, all'interno di quella directory, l'esecuzione .\script.ps1funziona correttamente.

Ora voglio chiamare quello script da una directory diversa senza cambiare la directory di riferimento dello script. Quindi voglio chiamare ..\..\dir\script.ps1e voglio ancora che lo script si comporti come è stato chiamato dalla sua directory.

Come posso farlo o come posso modificare uno script in modo che possa essere eseguito da qualsiasi directory?

Risposte:


200

Vuoi dire che vuoi il percorso dello script in modo da poter fare riferimento a un file accanto allo script? Prova questo:

$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
Write-host "My directory is $dir"

Puoi ottenere molte informazioni da $ MyInvocation e dalle sue proprietà.

Se si desidera fare riferimento a un file nella directory di lavoro corrente, è possibile utilizzare Resolve-Path o Get-ChildItem:

$filepath = Resolve-Path "somefile.txt"

EDIT (basato sul commento di OP):

# temporarily change to the correct folder
Push-Location $folder

# do stuff, call ant, etc

# now back to previous directory
Pop-Location

Probabilmente ci sono altri modi per ottenere qualcosa di simile anche usando Invoke-Command.


1
Hmm, okay, forse avrei dovuto essere un po 'più chiaro sulla mia sceneggiatura. In realtà è uno script che chiama antcon alcuni parametri. Quindi devo chiamare antda quella cartella per assicurarmi che trovi correttamente il file di configurazione. Idealmente sto cercando qualcosa per cambiare temporaneamente la directory di esecuzione localmente all'interno di quello script.
colpì il

3
Ah, allora in quel caso vorrai Push-LocationePop-Location
JohnL,

5
attento a questo, $ MyInvocation è sensibile al contesto, di solito faccio $ ScriptPath = (Get-Variable MyInvocation -Scope Script) .Value.MyCommand.Path che funziona da qualsiasi tipo di annidamento o funzione
Ion Todirel

39

Se stai chiamando app native, devi preoccuparti di [Environment]::CurrentDirectorynon conoscere la $PWDdirectory corrente di PowerShell . Per vari motivi, PowerShell non imposta la directory di lavoro corrente del processo quando si imposta Set-Location o Push-Location, quindi è necessario assicurarsi di farlo se si eseguono applicazioni (o cmdlet) che prevedono che vengano impostate.

In uno script, puoi fare questo:

$CWD = [Environment]::CurrentDirectory

Push-Location $MyInvocation.MyCommand.Path
[Environment]::CurrentDirectory = $PWD
##  Your script code calling a native executable
Pop-Location

# Consider whether you really want to set it back:
# What if another runspace has set it in-between calls?
[Environment]::CurrentDirectory = $CWD

Non c'è alternativa infallibile a questo. Molti di noi mettono una riga nella nostra funzione di prompt per impostare [Environment] :: CurrentDirectory ... ma questo non ti aiuta quando cambi la posizione all'interno di uno script.

Due note sul motivo per cui questo non è impostato automaticamente da PowerShell:

  1. PowerShell può essere multi-thread. Puoi avere più Runpace (vedi RunspacePool e il modulo PSThreadJob) in esecuzione contemporaneamente in un singolo processo. Ogni spazio di esecuzione ha la propria $PWDdirectory di lavoro presente, ma esiste solo un processo e un solo ambiente.
  2. Anche quando sei single thread, $PWDnon è sempre una CurrentDirectory legale (potresti ad esempio inserire un CD nel provider del registro).

Se vuoi inserirlo nel tuo prompt (che verrebbe eseguito solo nello spazio di esecuzione principale, a thread singolo), devi utilizzare:

[Environment]::CurrentDirectory = Get-Location -PSProvider FileSystem

4
Non cambia la directory di lavoro del processo a causa dei provider di percorsi: potresti inserire CD nel registro, in un database SQL o in un alveare IIS ecc ...
piers7

1
Sì. Lo so, quello era il punto dell'ultima "nota finale". Avrebbero potuto (come faccio nella mia funzione prompt) aggiornarlo nella posizione corrente del provider FileSystem, come ogni altra shell al mondo.
Jaykul,

2
Dei sacri script, sono così deluso .\_/.- per questa metà della mia giornata uccisa! Le persone, sul serio? Scherzi a parte? ..
Ulidtko,

2
Al momento non è più necessario, le versioni più moderne di PowerShell impostano la directory di lavoro quando avviano app binarie.
Jaykul,

1
Questo metodo non riesce a ripristinare l'originale [Environment]::CurrentDirectoryquando differisce da $PWD. La cosa corretta da fare sarebbe archiviarla in una variabile $origEnvDir = [Environment]::CurrentDirectorye poi ripristinarla [Environment]::CurrentDirectory = $origEnvDir.
Strom,

37

Ci sono risposte con un gran numero di voti, ma quando ho letto la tua domanda, ho pensato che volessi conoscere la directory in cui si trova lo script, non quella in cui lo script è in esecuzione. È possibile ottenere le informazioni con le variabili automatiche di PowerShell

$PSScriptRoot - the directory where the script exists, not the target directory the script is running in
$PSCommandPath - the full path of the script

Ad esempio, ho uno script $ profile che trova il file della soluzione di Visual Studio e lo avvia. Volevo memorizzare il percorso completo, una volta avviato un file di soluzione. Ma volevo salvare il file in cui esiste lo script originale. Quindi ho usato $ PsScriptRoot.


12

Ho spesso usato il seguente codice per importare un modulo che si trova nella stessa directory dello script in esecuzione. Ottiene innanzitutto la directory da cui è in esecuzione PowerShell

$ currentPath = Split-Path ((Get-Variable MyInvocation -Scope 0) .Value) .MyCommand.Path

modulo di importazione "$ currentPath \ sqlps.ps1"


si desidera impostare -Scope su Script
Ion Todirel il

9

Funzionerebbe benissimo.

Push-Location $PSScriptRoot

Write-Host CurrentDirectory $CurDir

Non dimenticare di chiamare Pop-Locationalla fine per riportare il percorso al suo stato originale.
Lam Le

2

Beh, stavo cercando una soluzione per questo da un po ', senza script solo dalla CLI. Ecco come lo faccio xD:

  • Passare alla cartella da cui si desidera eseguire lo script (la cosa importante è che hai completamenti di tabulazione)

    ..\..\dir

  • Ora circonda la posizione con doppie virgolette e al suo interno si aggiunge cd, in modo da poter invocare un'altra istanza di PowerShell.

    "cd ..\..\dir"

  • Aggiungi un altro comando per eseguire lo script separato da ;, con un separatore di comandi in PowerShell

    "cd ..\..\dir\; script.ps1"

  • Infine eseguilo con un'altra istanza di PowerShell

    start powershell "cd..\..\dir\; script.ps1"

Questo aprirà una nuova finestra di PowerShell, andrà a ..\..\dir, eseguirà script.ps1e chiuderà la finestra.


Nota che ";" separa semplicemente i comandi, come se li avessi digitati uno per uno, se il primo fallisce, il secondo verrà eseguito e il successivo e il successivo ... Se vuoi mantenere aperta la nuova finestra di PowerShell aggiungi -noexit nel comando passato. Nota che prima cerco nella cartella desiderata in modo da poter utilizzare i completamenti di tabulazione (non è possibile tra virgolette doppie).

start powershell "-noexit cd..\..\dir\; script.ps1"

Usa le virgolette doppie in ""modo da poter passare le directory con spazi nei nomi, ad es.

start powershell "-noexit cd '..\..\my dir'; script.ps1"


0

Ho creato una soluzione con la soluzione @ JohnL:

$MyInvocation.MyCommand.Path | Split-Path | Push-Location
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.