PowerShell: Jalankan perintah dari direktori skrip

144

Saya memiliki skrip PowerShell yang melakukan beberapa hal menggunakan direktori skrip saat ini. Jadi ketika di dalam direktori itu, menjalankan .\script.ps1berfungsi dengan benar.

Sekarang saya ingin memanggil skrip itu dari direktori lain tanpa mengubah direktori referensi skrip. Jadi saya ingin menelepon ..\..\dir\script.ps1dan masih ingin agar skrip itu berperilaku seperti yang dipanggil dari dalam direktori.

Bagaimana saya melakukan itu, atau bagaimana saya memodifikasi skrip sehingga dapat berjalan dari direktori mana pun?

menyodok
sumber

Jawaban:

200

Maksud Anda ingin jalur skrip sendiri sehingga Anda dapat mereferensikan file di sebelah skrip? Coba ini:

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

Anda bisa mendapatkan banyak info dari $ MyInvocation dan propertinya.

Jika Anda ingin mereferensikan file di direktori kerja saat ini, Anda dapat menggunakan Resolve-Path atau Get-ChildItem:

$filepath = Resolve-Path "somefile.txt"

EDIT (berdasarkan komentar dari OP):

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

# do stuff, call ant, etc

# now back to previous directory
Pop-Location

Mungkin ada cara lain untuk mencapai sesuatu yang serupa menggunakan Invoke-Command juga.

JohnL
sumber
1
Hmm, oke, mungkin saya seharusnya lebih jelas tentang skrip saya. Sebenarnya itu adalah skrip yang memanggil antdengan beberapa parameter. Jadi saya harus menelepon antdari folder itu untuk memastikan bahwa ia menemukan file konfigurasi dengan benar. Idealnya saya mencari sesuatu untuk sementara mengubah direktori eksekusi secara lokal di dalam skrip itu.
colok
3
Ah kalau begitu dalam hal ini yang Anda inginkan Push-LocationdanPop-Location
JohnL
5
hati-hati tentang itu, $ MyInvocation sensitif terhadap konteks, saya biasanya melakukan $ ScriptPath = (Get-Variable MyInvocation -Scope Script) .Value.MyCommand.Path yang berfungsi dari segala jenis fungsi atau fungsi bersarang
Ion Todirel
39

Jika Anda memanggil aplikasi asli, Anda perlu khawatir [Environment]::CurrentDirectorybukan tentang $PWDdirektori PowerShell saat ini. Karena berbagai alasan, PowerShell tidak mengatur direktori kerja saat ini saat Anda mengatur-Lokasi atau Lokasi-Push, jadi Anda perlu memastikan Anda melakukannya jika Anda menjalankan aplikasi (atau cmdlet) yang mengharapkannya diatur.

Dalam skrip, Anda dapat melakukan ini:

$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

Tidak ada alternatif lain untuk ini. Banyak dari kita meletakkan garis dalam fungsi prompt kita untuk mengatur [Lingkungan] :: CurrentDirectory ... tetapi itu tidak membantu Anda ketika Anda mengubah lokasi dalam skrip.

Dua catatan tentang alasan mengapa ini tidak diatur oleh PowerShell secara otomatis:

  1. PowerShell bisa multi-berulir. Anda dapat memiliki beberapa Runspace (lihat RunspacePool, dan modul PSThreadJob) yang berjalan secara bersamaan dalam satu proses tunggal. Setiap runspace memiliki $PWDdirektori kerjanya sendiri , tetapi hanya ada satu proses, dan hanya satu Lingkungan.
  2. Bahkan ketika Anda menggunakan utas tunggal, $PWDtidak selalu direktori arus yang sah (misalnya, Anda dapat memasukkan CD ke penyedia registri).

Jika Anda ingin memasukkannya ke prompt Anda (yang hanya akan berjalan di runspace utama, single-threaded), Anda perlu menggunakan:

[Environment]::CurrentDirectory = Get-Location -PSProvider FileSystem
Jaykul
sumber
4
Itu tidak mengubah proses direktori kerja karena penyedia jalur: Anda mungkin CD ke registri, database SQL atau sarang IIS dll ...
piers7
1
Iya. Saya tahu, itulah maksud dari "catatan akhir" terakhir itu. Mereka dapat (seperti yang saya lakukan pada fungsi prompt saya) memperbaruinya ke lokasi saat ini dari penyedia FileSystem, seperti setiap shell lain di dunia.
Jaykul
2
Dewa para .\_/.penulis naskah suci, AKU SANGAT kecewa - karena ini membunuh setengah dari hariku! Orang-orang, serius? Serius? ..
ulidtko
2
Sebagian besar tidak diperlukan sekarang, versi PowerShell yang lebih modern mengatur direktori kerja ketika mereka meluncurkan aplikasi biner.
Jaykul
1
Metode ini gagal mengembalikan yang asli [Environment]::CurrentDirectoryketika itu berbeda $PWD. Hal yang benar untuk dilakukan adalah menyimpannya ke dalam variabel $origEnvDir = [Environment]::CurrentDirectorydan kemudian mengembalikannya [Environment]::CurrentDirectory = $origEnvDir.
Strom
37

Ada jawaban dengan jumlah suara besar, tetapi ketika saya membaca pertanyaan Anda, saya pikir Anda ingin tahu direktori di mana skrip berada, bukan di mana skrip berjalan. Anda bisa mendapatkan informasi dengan variabel otomatis PowerShell

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

Sebagai contoh, saya memiliki skrip $ profil yang menemukan file solusi visual studio dan memulainya. Saya ingin menyimpan path lengkap, setelah file solusi dimulai. Tapi saya ingin menyimpan file di mana skrip asli ada. Jadi saya menggunakan $ PsScriptRoot.

Andy
sumber
12

Saya sering menggunakan kode berikut untuk mengimpor modul yang duduk di bawah direktori yang sama dengan skrip yang sedang berjalan. Pertama-tama akan mendapatkan direktori dari mana PowerShell berjalan

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

import-module "$ currentPath \ sqlps.ps1"

Daniel Wu
sumber
Anda ingin mengatur -Scope ke Script
Ion Todirel
9

Ini akan bekerja dengan baik.

Push-Location $PSScriptRoot

Write-Host CurrentDirectory $CurDir
ArunSK
sumber
Jangan lupa untuk menelepon Pop-Locationdi akhir untuk mengembalikan jalur ke keadaan semula.
Lam Le
2

Yah saya sedang mencari solusi untuk ini untuk sementara waktu, tanpa skrip hanya dari CLI. Ini adalah bagaimana saya melakukannya xD:

  • Arahkan ke folder dari mana Anda ingin menjalankan skrip (yang penting adalah Anda memiliki pelengkapan tab)

    ..\..\dir

  • Sekarang mengelilingi lokasi dengan tanda kutip ganda, dan di dalamnya menambahkan cd, sehingga kami bisa meminta contoh PowerShell lainnya.

    "cd ..\..\dir"

  • Tambahkan perintah lain untuk menjalankan skrip yang dipisahkan oleh ;, dengan adalah pemisah perintah di PowerShell

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

  • Akhirnya Jalankan dengan instance PowerShell lainnya

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

Ini akan membuka jendela powershell baru, pergi ke ..\..\dir, jalankan script.ps1dan tutup jendela.


Perhatikan bahwa ";" hanya memisahkan perintah, seperti Anda mengetiknya satu per satu, jika gagal pertama kedua akan berjalan dan berikutnya, dan berikutnya setelah ... Jika Anda ingin membuka jendela PowerShell baru Anda menambahkan -noexit dalam perintah yang dikirimkan. Perhatikan bahwa saya pertama-tama menavigasi ke folder yang diinginkan sehingga saya dapat menggunakan pelengkapan tab (Anda tidak bisa menggunakan tanda kutip ganda).

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

Gunakan tanda kutip ganda ""sehingga Anda dapat melewati direktori dengan spasi dalam nama misalnya,

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

IGRACH
sumber
0

Saya membuat one-liner dari solusi @ JohnL:

$MyInvocation.MyCommand.Path | Split-Path | Push-Location
sashoalm
sumber