Làm thế nào để có được thư mục hiện tại của lệnh ghép ngắn đang được thực thi


202

Đây có thể là một nhiệm vụ đơn giản, nhưng tôi đã thấy một số nỗ lực về cách lấy đường dẫn đến thư mục nơi lệnh ghép được thực hiện được đặt với thành công hỗn hợp. Chẳng hạn, khi tôi thực thi C:\temp\myscripts\mycmdlet.ps1có tệp cài đặt tại C:\temp\myscripts\settings.xmltôi muốn có thể lưu trữ C:\temp\myscriptstrong một biến trong đó mycmdlet.ps1.

Đây là một giải pháp hoạt động (mặc dù hơi cồng kềnh):

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

Một số khác đề xuất giải pháp này chỉ hoạt động trên môi trường thử nghiệm của chúng tôi:

$settingspath = '.\settings.xml'

Tôi thích cách tiếp cận thứ hai rất nhiều và thích nó phải phân tích filepath như một tham số mỗi lần, nhưng tôi không thể làm cho nó hoạt động trên môi trường phát triển của mình. Tôi phải làm gì đây? Nó có liên quan gì đến cách PowerShell được cấu hình không?


11
Lưu ý rằng tiêu đề mơ hồ của câu hỏi này đã dẫn đến các câu trả lời bên dưới giải quyết một trong hai vấn đề riêng biệt, mà không nêu rõ: (a) cách tham chiếu vị trí hiện tại (thư mục) hoặc (b) cách tham chiếu vị trí của tập lệnh đang chạy ( thư mục chứa tập lệnh đang chạy, có thể có hoặc không phải là thư mục hiện tại).
mkuity0

Câu trả lời:


143

Cách đáng tin cậy để làm điều này giống như bạn đã chỉ ra $MyInvocation.MyCommand.Path.

Sử dụng các đường dẫn tương đối sẽ dựa trên $ pwd, trong PowerShell, thư mục hiện tại cho một ứng dụng hoặc thư mục làm việc hiện tại cho API .NET.

PowerShell v3 + :

Sử dụng biến tự động $PSScriptRoot.


6
Bạn có thể vui lòng giải thích cho tôi làm thế nào bạn tìm thấy tài sản PATH? $ MyInvocation.MyCommand | gm không hiển thị thuộc tính đó trong danh sách thành viên.
Vitaliy Markitanov

19
Tại sao không sử dụng $ PSScriptRoot? Có vẻ đáng tin cậy hơn
mBrice1024

@ user2326106 Bạn có thể giải thích sự khác biệt giữa $PSScriptRoot$MyInvocation.MyCommand.Path?
duct_tape_coder

1
Câu trả lời này không đầy đủ.
K - Độc tính trong SO đang tăng lên.

Câu trả lời này không đầy đủ.
stackleit

263

Vâng, điều đó nên làm việc. Nhưng nếu bạn cần xem đường dẫn tuyệt đối, đây là tất cả những gì bạn cần:

(Get-Item -Path ".\").FullName

4
Cảm ơn, đây là một phương pháp tuyệt vời để tìm đường dẫn đầy đủ từ các đường dẫn tương đối. Ví dụ (Get-mục -Path $ myRelativePath verbose) .FullName
DLux

Cảm ơn vì điều này. Các câu trả lời khác không hoạt động cho các tập lệnh Powershell được biên dịch thành EXE.
Zach Alexander

10
Điều này là sai . Điều này nhận được thư mục hiện tại của quá trình , có thể là bất cứ nơi nào. Ví dụ, nếu thư mục hiện tại của dòng lệnh của tôi là C:\mydirvà tôi gọi lệnh C:\dir1\dir2\dir3\mycmdlet.ps1, thì điều này sẽ giải quyết C:\mydir, không C:\dir1\dir2\dir3. Gọi một tệp thực thi mới có cùng một vấn đề vì thư mục hiện tại được kế thừa từ tiến trình cha.
jpmc26

85

Phương pháp đơn giản nhất dường như là sử dụng biến được xác định trước:

 $PSScriptRoot

about_Automatic_Variablesabout_Scriptscả hai trạng thái:

Trong PowerShell 2.0, biến này chỉ hợp lệ trong các mô-đun tập lệnh (.psm1). Bắt đầu trong PowerShell 3.0, nó có hiệu lực trong tất cả các tập lệnh.

Tôi sử dụng nó như thế này:

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

12
Đây là phiên bản cụ thể. Điều này đòi hỏi ít nhất Powershell 3.0.
Marvin Dickhaus

1
Đây là những gì tôi cần để tham chiếu một tệp ở cùng vị trí với tập lệnh - cảm ơn!
Adam Prescott

Đây là câu trả lời tốt nhất vì nó cung cấp cho bạn chính xác đường dẫn mà tập lệnh PS của bạn hiện diện, về cơ bản là gốc để thực thi tập lệnh của bạn. Nó không quan tâm thư mục làm việc hiện tại của bạn từ đâu mà bạn đã gọi các tập lệnh của mình. +1.
RBT

@MarvinDickhaus Đó là lý do tại sao cần phải sử dụng "Set-StrictMode -Version 3.0" trong hầu hết các tập lệnh của bạn :) Cảm ơn rất nhiều vì các liên kết!
Alexander Shapkin

44

Bạn cũng có thể dùng:

(Resolve-Path .\).Path

Phần trong ngoặc trả về một PathInfođối tượng.

(Có sẵn từ PowerShell 2.0.)


2
Điều này là sai . Điều này nhận được thư mục hiện tại của quá trình , có thể là bất cứ nơi nào. Ví dụ, nếu thư mục hiện tại của dòng lệnh của tôi là C:\mydirvà tôi gọi lệnh C:\dir1\dir2\dir3\mycmdlet.ps1, thì điều này sẽ giải quyết C:\mydir, không C:\dir1\dir2\dir3. Gọi một tệp thực thi mới có cùng một vấn đề vì thư mục hiện tại được kế thừa từ tiến trình cha.
jpmc26

3
Cảm ơn! Tôi cũng đã hiểu nhầm tiêu đề của câu hỏi này và câu trả lời này chính xác là những gì tôi đang tìm kiếm. Tuy nhiên ... Nó không trả lời câu hỏi.
Ryan The Leach

33

Đường dẫn thường là null. Chức năng này an toàn hơn.

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
Tại sao -Scope 1? không
-Scope

1
Biến có được: Số phạm vi '1' vượt quá số phạm vi hoạt động.
suiwenfeng

2
Bạn đang gặp lỗi này vì bạn không có phạm vi cha mẹ. Tham số -Scope được biến trong một phạm vi được chỉ định. 1 trong trường hợp này là phạm vi cha. Để biết thêm thông tin, hãy xem bài viết về kỹ thuật này về Biến-biến ( technet.microsoft.com/en-us/l Library / hh849899.aspx )
Christian Flem

27

Thử :

(Get-Location).path

hoặc là:

($pwd).path

Tôi tiếp tục quên về $pwd/ $PWDbất cứ khi nào tôi nghỉ dài hạn từ powershell! IMO hữu ích hơn nhiều ...
kayleeFrye_onDeck

17

Get-Location sẽ trả về vị trí hiện tại:

$Currentlocation = Get-Location

3
PS C: \ Windows \ system32> C: \ powershell \ checkfile.ps1 -> điều này sẽ cung cấp cho c: \ windows \ system32
nbi

11

Tôi thích giải pháp một dòng :)

$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent

Đây là giải pháp tốt nhất cho đến nay.
Teoman shipahi

Có làm việc cho tôi. Pwd của tôi là thư mục người dùng của tôi và tôi đang chạy tập lệnh trong thư mục khác (B) mà nó cung cấp (B)
pate


4

Trong Powershell 3 trở lên, bạn chỉ cần sử dụng

$PSScriptRoot


1

Bạn sẽ nghĩ rằng sử dụng '. \' Làm đường dẫn có nghĩa là đường dẫn gọi. Nhưng không phải lúc nào cũng vậy. Ví dụ, nếu bạn sử dụng nó trong ScriptBlock công việc. Trong trường hợp đó, nó có thể trỏ đến% profile% \ Documents.


1

Hàm này sẽ đặt vị trí nhắc nhở thành đường dẫn kịch bản, xử lý các cách khác nhau để lấy scriptpath giữa vscode, psise và 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 }
}

0

Đối với những gì nó có giá trị, là một giải pháp một dòng, dưới đây là một giải pháp làm việc cho tôi.

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

1 ở cuối là bỏ qua /.

Nhờ các bài viết ở trên bằng cách sử dụng lệnh ghép ngắn Get-Location .


0

Hầu hết các câu trả lời không hoạt động khi gỡ lỗi trong các IDE sau:

  • PS-ISE (PowerShell ISE)
  • Mã VS (Mã Visual Studio)

Bởi vì trong đó $PSScriptRootlà trống và Resolve-Path .\(và similars) sẽ dẫn đến đường dẫn không chính xác.

Câu trả lời của Freakydinde là người duy nhất giải quyết các tình huống đó, vì vậy tôi đã bỏ phiếu cho điều đó, nhưng tôi không nghĩ Set-Locationrằng câu trả lời đó thực sự là điều mong muốn. Vì vậy, tôi đã sửa nó và làm cho mã rõ ràng hơn một chút:

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

-1

Để mở rộng câu trả lời của @Cradle: bạn cũng có thể viết một hàm đa mục đích sẽ mang lại cho bạn kết quả tương tự theo câu hỏi của OP:

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

Nếu bạn chỉ cần tên của thư mục hiện tại, bạn có thể làm một cái gì đó như thế này:

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

Giả sử bạn đang làm việc từ C: \ Temp \ Location \ MyWorkingDirectory>

Đầu ra

MyWorkingDirectory


-1

Tôi gặp vấn đề tương tự và nó gây cho tôi rất nhiều rắc rối vì tôi đang tạo các chương trình được viết bằng PowerShell (ứng dụng GUI của người dùng cuối đầy đủ) và tôi có rất nhiều tệp và tài nguyên tôi cần tải từ đĩa. Từ kinh nghiệm của tôi, sử dụng .để đại diện cho thư mục hiện tại là không đáng tin cậy. Nó nên đại diện cho thư mục làm việc hiện tại, nhưng nó thường không. Dường như PowerShell lưu vị trí mà PowerShell đã được gọi bên trong .. Nói chính xác hơn, khi PowerShell được khởi động lần đầu tiên, nó sẽ bắt đầu, theo mặc định, trong thư mục người dùng gia đình của bạn. Đó thường là thư mục của tài khoản người dùng của bạn, đại loại nhưC:\USERS\YOUR USER NAME. Sau đó, PowerShell thay đổi thư mục thành thư mục mà bạn đã gọi nó hoặc thư mục chứa tập lệnh mà bạn đang thực thi được đặt trước khi hiển thị cho bạn lời nhắc PowerShell hoặc chạy tập lệnh. Nhưng điều đó xảy ra sau khi ứng dụng PowerShell ban đầu bắt đầu trong thư mục người dùng gia đình của bạn.

.đại diện cho thư mục ban đầu bên trong mà PowerShell bắt đầu. Vì vậy, .chỉ đại diện cho thư mục hiện tại trong trường hợp nếu bạn đã gọi PowerShell từ thư mục mong muốn. Nếu sau này bạn thay đổi thư mục trong mã PowerShell, thay đổi dường như không được phản ánh bên .trong mọi trường hợp. Trong một số trường hợp .đại diện cho thư mục làm việc hiện tại và trong thư mục khác mà PowerShell (chính nó, không phải tập lệnh) đã được gọi, điều gì có thể dẫn đến kết quả không nhất quán. Vì lý do này, tôi sử dụng kịch bản invoker. Tập lệnh PowerShell với một lệnh duy nhất bên trong : POWERSHELL. Điều đó sẽ đảm bảo rằng PowerShell được gọi từ thư mục mong muốn và do đó thực hiện.đại diện cho thư mục hiện tại. Nhưng nó chỉ hoạt động nếu bạn không thay đổi thư mục sau này trong mã PowerShell. Trong trường hợp tập lệnh, tôi sử dụng tập lệnh invoker tương tự tập lệnh cuối cùng tôi đã đề cập, ngoại trừ tập lệnh chứa tùy chọn tệp : POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. Điều đó đảm bảo rằng PowerShell được khởi động trong thư mục làm việc hiện tại.

Chỉ cần nhấp vào tập lệnh sẽ gọi PowerShell từ thư mục người dùng gia đình của bạn cho dù tập lệnh được đặt ở đâu. Nó dẫn đến thư mục làm việc hiện tại là thư mục chứa tập lệnh, nhưng thư mục gọi PowerShell đang tồn tại C:\USERS\YOUR USER NAME.trả về một trong hai thư mục này tùy thuộc vào tình huống, thật là vô lý.

Nhưng để tránh tất cả sự phiền phức này và sử dụng tập lệnh invoker, bạn chỉ cần sử dụng $PWDhoặc $PSSCRIPTROOTthay vì .đại diện cho thư mục hiện tại tùy thuộc vào thời tiết bạn muốn đại diện cho thư mục làm việc hiện tại hoặc thư mục mà tập lệnh đã được gọi. Và nếu bạn, vì một số lý do, muốn lấy lại hai thư mục khác .trả về, bạn có thể sử dụng $HOME.

Cá nhân tôi chỉ có tập lệnh invoker bên trong thư mục gốc của ứng dụng mà tôi phát triển bằng PowerShell, nó gọi tập lệnh ứng dụng chính của tôi và chỉ cần nhớ là không bao giờ thay đổi thư mục làm việc hiện tại bên trong mã nguồn của ứng dụng, vì vậy tôi không bao giờ phải lo lắng về điều này, và tôi có thể sử dụng .để thể hiện thư mục hiện tại và để hỗ trợ địa chỉ tệp tương đối trong các ứng dụng của mình mà không gặp vấn đề gì. Điều này sẽ hoạt động trong các phiên bản mới hơn của PowerShell (mới hơn phiên bản 2).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.