PowerShell: Chạy lệnh từ thư mục script


144

Tôi có một tập lệnh PowerShell thực hiện một số nội dung bằng thư mục hiện tại của tập lệnh. Vì vậy, khi bên trong thư mục đó, chạy .\script.ps1hoạt động chính xác.

Bây giờ tôi muốn gọi tập lệnh đó từ một thư mục khác mà không thay đổi thư mục tham chiếu của tập lệnh. Vì vậy, tôi muốn gọi ..\..\dir\script.ps1và vẫn muốn tập lệnh đó hoạt động như được gọi từ bên trong thư mục của nó.

Làm thế nào để tôi làm điều đó, hoặc làm thế nào để tôi sửa đổi một tập lệnh để nó có thể chạy từ bất kỳ thư mục nào?

Câu trả lời:


200

Bạn có nghĩa là bạn muốn đường dẫn riêng của tập lệnh để bạn có thể tham chiếu một tệp bên cạnh tập lệnh? Thử cái này:

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

Bạn có thể nhận được rất nhiều thông tin từ $ MyInvocation và các thuộc tính của nó.

Nếu bạn muốn tham chiếu một tệp trong thư mục làm việc hiện tại, bạn có thể sử dụng Resolve-Path hoặc Get-ChildItem:

$filepath = Resolve-Path "somefile.txt"

EDIT (dựa trên nhận xét từ OP):

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

# do stuff, call ant, etc

# now back to previous directory
Pop-Location

Có lẽ có nhiều cách khác để đạt được thứ gì đó tương tự bằng Invoke-Command.


1
Hmm, được thôi, có lẽ tôi nên rõ ràng hơn một chút về kịch bản của mình. Trên thực tế nó là một kịch bản gọi antvới một số tham số. Vì vậy, tôi phải gọi anttừ thư mục đó để đảm bảo rằng nó tìm thấy tệp cấu hình chính xác. Lý tưởng nhất là tôi đang tìm kiếm một cái gì đó để tạm thời thay đổi thư mục thực thi cục bộ trong tập lệnh đó.
chọc

3
À, trong trường hợp đó bạn sẽ muốn Push-LocationPop-Location
JohnL

5
cẩn thận về điều đó, $ MyInvocation rất nhạy cảm với bối cảnh, tôi thường làm $ ScriptPath = (Get-Var biến MyInvocation -Scope Script) .Value.MyCommand.Path hoạt động từ bất kỳ loại lồng hoặc chức năng nào
Ion Todirel

39

Nếu bạn đang gọi các ứng dụng gốc, bạn cần lo lắng về việc [Environment]::CurrentDirectorykhông phải về $PWDthư mục hiện tại của PowerShell . Vì nhiều lý do, PowerShell không đặt thư mục làm việc hiện tại của quy trình khi bạn Đặt vị trí hoặc Vị trí đẩy, do đó bạn cần đảm bảo rằng bạn sẽ làm như vậy nếu bạn đang chạy các ứng dụng (hoặc lệnh ghép ngắn) mà nó sẽ được đặt.

Trong một kịch bản, bạn có thể làm điều này:

$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

Không có sự thay thế hoàn hảo cho điều này. Nhiều người trong chúng ta đặt một dòng trong chức năng nhắc nhở của mình để đặt [Môi trường] :: CurrentDirectory ... nhưng điều đó không giúp ích gì cho bạn khi bạn thay đổi vị trí trong tập lệnh.

Hai lưu ý về lý do tại sao điều này không được PowerShell đặt tự động:

  1. PowerShell có thể đa luồng. Bạn có thể có nhiều Runspaces (xem RunspacePool và mô-đun PSThreadJob) chạy đồng thời trong một quy trình. Mỗi runspace có $PWDthư mục làm việc hiện tại của riêng nó , nhưng chỉ có một quy trình và chỉ có một Môi trường.
  2. Ngay cả khi bạn là một luồng đơn, $PWDkhông phải lúc nào cũng là một CurrentDirector hợp pháp (chẳng hạn bạn có thể đưa CD vào nhà cung cấp đăng ký).

Nếu bạn muốn đặt nó vào dấu nhắc của mình (sẽ chỉ chạy trong không gian chính, luồng đơn), bạn cần sử dụng:

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

4
Nó không thay đổi thư mục làm việc theo quy trình vì các nhà cung cấp đường dẫn: bạn có thể đang CD vào sổ đăng ký, cơ sở dữ liệu SQL hoặc tổ hợp IIS, v.v ...
piers7

1
Đúng. Tôi biết, đó là điểm của "Ghi chú cuối cùng" cuối cùng đó. Họ có thể (như tôi làm trong chức năng nhắc nhở của mình) đã cập nhật nó vào vị trí hiện tại của nhà cung cấp FileSystem, giống như mọi shell khác trên thế giới.
Jaykul

2
Các vị thần kịch bản thần thánh, TÔI ĐÃ BỊ XÓA .\_/.- vì điều này đã giết chết một nửa ngày của tôi! Mọi người, nghiêm túc chứ? Nghiêm túc chứ? ..
ulidtko

2
Bây giờ hầu như không cần thiết, các phiên bản hiện đại hơn của PowerShell đã đặt thư mục hoạt động khi chúng khởi chạy các ứng dụng nhị phân.
Jaykul

1
Phương pháp này thất bại trong việc khôi phục bản gốc [Environment]::CurrentDirectorykhi khác với $PWD. Điều chính xác cần làm là lưu trữ nó vào một biến $origEnvDir = [Environment]::CurrentDirectoryvà sau đó khôi phục nó [Environment]::CurrentDirectory = $origEnvDir.
Strom

37

Có câu trả lời với số lượng lớn phiếu bầu, nhưng khi tôi đọc câu hỏi của bạn, tôi nghĩ bạn muốn biết thư mục chứa tập lệnh, không phải là nơi tập lệnh đang chạy. Bạn có thể lấy thông tin với các biến tự động của powershell

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

Ví dụ: tôi có tập lệnh $ profile tìm tệp giải pháp phòng thu trực quan và khởi động nó. Tôi muốn lưu trữ đường dẫn đầy đủ, khi tệp giải pháp được bắt đầu. Nhưng tôi muốn lưu tập tin nơi tập lệnh gốc tồn tại. Vì vậy, tôi đã sử dụng $ PsScriptRoot.


12

Tôi thường sử dụng đoạn mã sau để nhập một mô-đun nằm trong cùng thư mục với tập lệnh đang chạy. Đầu tiên, nó sẽ nhận được thư mục mà powershell đang chạy

$ currentPath = Split-Path ((Biến đổi MyInvocation -Scope 0). Giá trị) .MyCommand.Path

mô-đun nhập khẩu "$ currentPath \ sqlps.ps1"


bạn muốn đặt -Scope thành Script
Ion Todirel

9

Điều này sẽ làm việc tốt.

Push-Location $PSScriptRoot

Write-Host CurrentDirectory $CurDir

Đừng quên gọi Pop-Locationở cuối để đưa đường dẫn về trạng thái ban đầu.
Lam Lê

2

Vâng, tôi đã tìm kiếm giải pháp cho điều này trong một thời gian, mà không có bất kỳ kịch bản nào chỉ từ CLI. Đây là cách tôi làm nó xD:

  • Điều hướng đến thư mục mà bạn muốn chạy tập lệnh (điều quan trọng là bạn phải hoàn thành tab)

    ..\..\dir

  • Bây giờ bao quanh vị trí với dấu ngoặc kép và bên trong chúng thêm vào cd, vì vậy chúng ta có thể gọi một thể hiện khác của powershell.

    "cd ..\..\dir"

  • Thêm một lệnh khác để chạy tập lệnh được phân tách bằng ;, với dấu phân cách lệnh trong powershell

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

  • Cuối cùng, chạy nó với một thể hiện khác của powershell

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

Điều này sẽ mở cửa sổ powershell mới, đi đến ..\..\dir, chạy script.ps1và đóng cửa sổ.


Lưu ý rằng ";" chỉ cần tách các lệnh, giống như bạn gõ từng cái một, nếu lần đầu tiên thất bại thứ hai sẽ chạy và tiếp theo sau và tiếp theo sau ... Nếu bạn muốn giữ cửa sổ powershell mới mở, bạn thêm -noexit vào lệnh đã truyền. Lưu ý rằng trước tiên tôi điều hướng đến thư mục mong muốn để tôi có thể sử dụng hoàn thành tab (bạn không thể trong dấu ngoặc kép).

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

Sử dụng dấu ngoặc kép ""để bạn có thể chuyển các thư mục có khoảng trắng trong tên, vd

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


0

Tôi đã tạo một lớp lót trong giải pháp của @ JohnL:

$MyInvocation.MyCommand.Path | Split-Path | Push-Location
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.