Come ottenere la directory corrente del cmdlet in esecuzione


202

Questo dovrebbe essere un compito semplice, ma ho visto diversi tentativi su come ottenere il percorso della directory in cui si trova il cmdlet eseguito con esito misto. Ad esempio, quando eseguo C:\temp\myscripts\mycmdlet.ps1un file di impostazioni in C:\temp\myscripts\settings.xmlcui vorrei poter archiviare C:\temp\myscriptsuna variabile all'interno mycmdlet.ps1.

Questa è una soluzione che funziona (anche se un po 'ingombrante):

$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$settingspath = $directorypath + '\settings.xml'

Un altro ha suggerito questa soluzione che funziona solo sul nostro ambiente di test:

$settingspath = '.\settings.xml'

Mi piace che quest'ultimo si avvicini molto e preferisco che debba analizzare ogni volta il percorso del file come parametro, ma non riesco a farlo funzionare nel mio ambiente di sviluppo. Cosa dovrei fare? Ha qualcosa a che fare con la configurazione di PowerShell?


11
Si noti che il titolo ambiguo di questa domanda ha portato alle seguenti risposte a risolvere uno dei due problemi distinti, senza indicare esplicitamente quali: (a) come fare riferimento alla posizione corrente (directory) o (b) come fare riferimento alla posizione dello script in esecuzione ( la directory in cui si trova lo script in esecuzione, che può essere o meno la directory corrente).
mklement0

Risposte:


144

Il modo affidabile per farlo è proprio come hai mostrato $MyInvocation.MyCommand.Path.

L'uso dei percorsi relativi si baserà su $ pwd, in PowerShell, la directory corrente per un'applicazione o la directory di lavoro corrente per un'API .NET.

PowerShell v3 + :

Usa la variabile automatica $PSScriptRoot.


6
Puoi spiegarmi come hai trovato il PERCORSO? $ MyInvocation.MyCommand | gm non mostra tale proprietà nell'elenco dei membri.
Vitaliy Markitanov,

19
perché non usare semplicemente $ PSScriptRoot? Sembra più affidabile
mBrice1024

@ user2326106 Puoi spiegare la differenza tra $PSScriptRoote $MyInvocation.MyCommand.Path?
duct_tape_coder

1
Questa risposta è incompleta.
K - La tossicità in SO sta crescendo.

Questa risposta è incompleta.
Stackleit

263

Sì, dovrebbe funzionare. Ma se hai bisogno di vedere il percorso assoluto, questo è tutto ciò che ti serve:

(Get-Item .).FullName

4
Grazie, questo è un ottimo metodo per trovare il percorso completo dai percorsi relativi. Ad esempio (Get-Item -Path $ myRelativePath -Verbose) .FullName
dlux

Grazie per questo. Altre risposte non funzionavano per gli script Powershell compilati in EXE.
Zach Alexander,

10
Questo è sbagliato . Questo ottiene la directory corrente del processo , che può essere ovunque. Ad esempio, se la mia directory corrente della riga di comando è C:\mydir, e invoco il comando C:\dir1\dir2\dir3\mycmdlet.ps1, questo si risolverà in C:\mydirno C:\dir1\dir2\dir3. Il richiamo di un nuovo eseguibile presenta lo stesso problema poiché la directory corrente viene ereditata dal processo padre.
jpmc26,

85

Il metodo più semplice sembra essere quello di utilizzare la seguente variabile predefinita:

 $PSScriptRoot

about_Automatic_Variablesed about_Scriptsentrambi dichiarano:

In PowerShell 2.0, questa variabile è valida solo nei moduli di script (.psm1). A partire da PowerShell 3.0, è valido in tutti gli script.

Lo uso così:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName

12
È specifica della versione. Ciò richiede almeno Powershell 3.0.
Marvin Dickhaus,

1
Questo è ciò di cui avevo bisogno per fare riferimento a un file nella stessa posizione dello script - grazie!
Adam Prescott,

Questa è la risposta migliore in quanto ti dà esattamente il percorso in cui è presente lo script PS che è essenzialmente la radice per l'esecuzione dello script. Non importa quale sia la tua attuale directory di lavoro da cui hai invocato i tuoi script. +1.
RBT

@MarvinDickhaus Ecco perché è necessario utilizzare "Set-StrictMode -Version 3.0" nella maggior parte dei tuoi script :) Grazie mille per i link!
Alexander Shapkin,

44

Puoi anche usare:

(Resolve-Path .\).Path

La parte tra parentesi restituisce un PathInfooggetto.

(Disponibile da PowerShell 2.0.)


2
Questo è sbagliato . Questo ottiene la directory corrente del processo , che può essere ovunque. Ad esempio, se la mia directory corrente della riga di comando è C:\mydir, e invoco il comando C:\dir1\dir2\dir3\mycmdlet.ps1, questo si risolverà in C:\mydirno C:\dir1\dir2\dir3. Il richiamo di un nuovo eseguibile presenta lo stesso problema poiché la directory corrente viene ereditata dal processo padre.
jpmc26,

3
Grazie! Ho anche frainteso il titolo di questa domanda e questa risposta era esattamente quello che stavo cercando. Tuttavia ... Non risponde alla domanda.
Ryan The Leach,

33

Il percorso è spesso nullo. Questa funzione è più sicura.

function Get-ScriptDirectory
{
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value;
    if($Invocation.PSScriptRoot)
    {
        $Invocation.PSScriptRoot;
    }
    Elseif($Invocation.MyCommand.Path)
    {
        Split-Path $Invocation.MyCommand.Path
    }
    else
    {
        $Invocation.InvocationName.Substring(0,$Invocation.InvocationName.LastIndexOf("\"));
    }
}

1
perché -Scope 1? non -Scope 0
suiwenfeng

1
Get-Variable: il numero di ambito '1' supera il numero di ambiti attivi.
suiwenfeng

2
Stai ricevendo questo errore perché non hai un ambito padre. -Il parametro Scope ottiene la variabile in un ambito specificato. 1 in questo caso è l'ambito padre. Per ulteriori informazioni, consultare questo articolo di Technet su Get-Variable ( technet.microsoft.com/en-us/library/hh849899.aspx )
Christian Flem,

27

Provare :

(Get-Location).path

o:

($pwd).path

Continuo a dimenticare $pwd/ $PWDogni volta che faccio una lunga pausa da PowerShell! IMO molto più utile ...
kayleeFrye_onDeck

17

Get-Location restituirà la posizione corrente:

$Currentlocation = Get-Location

3
PS C: \ Windows \ system32> C: \ powershell \ checkfile.ps1 -> questo darà c: \ windows \ system32
nbi



4

In Powershell 3 e versioni successive puoi semplicemente usare

$PSScriptRoot


1

Penseresti che usare '. \' Come percorso significa che è il percorso di invocazione. Ma non sempre. Esempio, se lo si utilizza all'interno di un lavoro ScriptBlock. In tal caso, potrebbe puntare a% profile% \ Documents.


1

questa funzione imposterà la posizione del prompt sul percorso dello script, gestendo i diversi modi di ottenere il percorso dello script tra vscode, psise e pwd:

function Set-CurrentLocation
{
    $currentPath = $PSScriptRoot                                                                                                     # AzureDevOps, Powershell
    if (!$currentPath) { $currentPath = Split-Path $pseditor.GetEditorContext().CurrentFile.Path -ErrorAction SilentlyContinue }     # VSCode
    if (!$currentPath) { $currentPath = Split-Path $psISE.CurrentFile.FullPath -ErrorAction SilentlyContinue }                       # PsISE

    if ($currentPath) { Set-Location $currentPath }
}

1

La maggior parte delle risposte non funziona durante il debug nei seguenti IDE:

  • PS-ISE (PowerShell ISE)
  • Codice VS (codice Visual Studio)

Perché in quelli $PSScriptRootè vuoto e Resolve-Path .\(e simili) si tradurranno in percorsi errati.

La risposta di Freakydinde è l'unica che risolve tali situazioni, quindi ho votato a favore, ma non credo Set-Locationche la risposta sia davvero ciò che si desidera. Quindi ho risolto questo problema e ho reso il codice un po 'più chiaro:

$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
    elseif ($psise) { split-path $psise.CurrentFile.FullPath } `
    elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }

0

Per quello che vale, per essere una soluzione a linea singola, quella che segue è una soluzione funzionante per me.

$currFolderName = (Get-Location).Path.Substring((Get-Location).Path.LastIndexOf("\")+1)

Il 1 alla fine è di ignorare il /.

Grazie ai post precedenti usando il cmdlet Get-Location .


-1

Per espandere la risposta di @Cradle: potresti anche scrivere una funzione multiuso che ti darà lo stesso risultato per la domanda del PO:

Function Get-AbsolutePath {

    [CmdletBinding()]
    Param(
        [parameter(
            Mandatory=$false,
            ValueFromPipeline=$true
        )]
        [String]$relativePath=".\"
    )

    if (Test-Path -Path $relativePath) {
        return (Get-Item -Path $relativePath).FullName -replace "\\$", ""
    } else {
        Write-Error -Message "'$relativePath' is not a valid path" -ErrorId 1 -ErrorAction Stop
    }

}

-1

Se hai solo bisogno del nome della directory corrente, potresti fare qualcosa del genere:

((Get-Location) | Get-Item).Name

Supponendo che si stia lavorando da C: \ Temp \ Location \ MyWorkingDirectory>

Produzione

MyWorkingDirectory


-1

Ho avuto problemi simili e questo mi ha causato molti problemi poiché sto realizzando programmi scritti in PowerShell (applicazioni GUI per utenti finali completi) e ho molti file e risorse che devo caricare dal disco. Dalla mia esperienza, usando . Successivamente, PowerShell cambia la directory nella directory dalla quale è stata invocata o nella directory in cui si trova lo script che si sta eseguendo prima di presentare all'utente il prompt di PowerShell o di eseguire lo script. Questo succede dopo che la stessa app PowerShell si avvia inizialmente nella directory dell'utente home.. per rappresentare la directory corrente non è affidabile. Dovrebbe rappresentare la directory di lavoro corrente, ma spesso non lo è. Sembra che PowerShell salvi la posizione da cui è stato richiamato PowerShell all'interno .. Per essere più precisi, quando PowerShell viene avviato per la prima volta, viene avviato, per impostazione predefinita, nella directory dell'utente di casa. Di solito è la directory del tuo account utente, qualcosa del genereC:\USERS\YOUR USER NAME

E .rappresenta quella directory iniziale all'interno della quale è stato avviato PowerShell. Quindi .rappresenta la directory corrente solo nel caso in cui sia stato richiamato PowerShell dalla directory desiderata. Se in seguito si modifica la directory nel codice di PowerShell, la modifica non si riflette .in tutti i casi. In alcuni casi .rappresenta la directory di lavoro corrente e in altre directory da cui è stato invocato PowerShell (stesso, non lo script), ciò che può portare a risultati incoerenti. Per questo motivo utilizzo lo script di invoker. Script di PowerShell con singolo comando all'interno: rappresenta la directory corrente. Funziona solo se non si modifica la directory in un secondo momento nel codice PowerShell. Nel caso di uno script, utilizzo lo script di invoker che è simile all'ultimo che ho citato, tranne che contiene un'opzione di file: POWERSHELL . Ciò garantirà che PowerShell sia richiamato dalla directory desiderata e quindi creerà.POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1 . Ciò garantisce l'avvio di PowerShell nella directory di lavoro corrente.

Basta fare clic sullo script per richiamare PowerShell dalla directory dell'utente di casa, indipendentemente da dove si trova lo script. Risulta che la directory di lavoro corrente è la directory in cui si trova lo script, ma la directory di invocazione di PowerShell è C:\USERS\YOUR USER NAMEe con la .restituzione di una di queste due directory a seconda della situazione, ciò che è ridicolo.

Ma per evitare tutto questo clamore e usare lo script di invocatore, puoi semplicemente usare $PWDo $PSSCRIPTROOTinvece di .rappresentare la directory corrente a seconda del tempo che desideri rappresentare la directory di lavoro corrente o la directory da cui è stato invocato lo script. E se, per qualche motivo, vuoi recuperare altre due directory che .restituiscono, puoi usare $HOME.

Personalmente ho solo lo script di invoker nella directory principale delle mie app che sviluppo con PowerShell che richiama lo script della mia app principale e ricordo semplicemente di non cambiare mai la directory di lavoro corrente all'interno del mio codice sorgente della mia app, quindi non devo mai preoccuparmi di questo, e posso usare .per rappresentare la directory corrente e supportare l'indirizzamento dei file relativi nelle mie applicazioni senza problemi. Questo dovrebbe funzionare con le versioni più recenti di PowerShell (più recenti della versione 2).

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.