เรียกสคริปต์ PowerShell PS1 จากสคริปต์ PS1 อื่นภายใน Powershell ISE


150

ฉันต้องการเรียกใช้สคริปต์ myScript1.ps1 ภายในสคริปต์ myScript2.ps1 ที่สองภายใน Powershell ISE

รหัสต่อไปนี้ภายใน MyScript2.ps1 ทำงานได้ดีจาก Powershell Administration แต่ใช้ไม่ได้ใน PowerShell ISE:

#Call myScript1 from myScript2
invoke-expression -Command .\myScript1.ps1

ฉันได้รับข้อผิดพลาดต่อไปนี้เมื่อฉันรัน MyScript2.ps1 จาก PowerShell ISE:

ไม่รู้จักคำว่า '. \ myScript1.ps1' เป็นชื่อของ cmdlet ฟังก์ชันไฟล์สคริปต์หรือโปรแกรมที่ทำงานได้ ตรวจสอบการสะกดของชื่อหรือถ้ามีพา ธ ตรวจสอบว่าพา ธ ถูกต้องแล้วลองอีกครั้ง

คำตอบ:


90

ในการค้นหาตำแหน่งของสคริปต์ให้ใช้Split-Path $MyInvocation.MyCommand.Path(ตรวจสอบให้แน่ใจว่าคุณใช้สิ่งนี้ในบริบทของสคริปต์)

เหตุผลที่คุณควรใช้ไม่ใช่สิ่งอื่นใดสามารถแสดงได้ด้วยสคริปต์ตัวอย่างนี้

## ScriptTest.ps1
Write-Host "InvocationName:" $MyInvocation.InvocationName
Write-Host "Path:" $MyInvocation.MyCommand.Path

นี่คือผลลัพธ์บางส่วน

PS C: \ Users \ JasonAr>. \ ScriptTest.ps1
InvocationName:. \ ScriptTest.ps1
เส้นทาง: C: \ Users \ JasonAr \ ScriptTest.ps1

PS C: \ Users \ JasonAr> . \ ScriptTest.ps1
InvocationName:.
เส้นทาง: C: \ Users \ JasonAr \ ScriptTest.ps1

PS C: \ Users \ JasonAr> & ". \ ScriptTest.ps1"
InvocationName: &
เส้นทาง: C: \ Users \ JasonAr \ ScriptTest.ps1

ในPowerShell 3.0และใหม่กว่าคุณสามารถใช้ตัวแปรอัตโนมัติ$PSScriptRoot:

## ScriptTest.ps1
Write-Host "Script:" $PSCommandPath
Write-Host "Path:" $PSScriptRoot
PS C: \ Users \ jarcher>. \ ScriptTest.ps1
สคริปต์: C: \ Users \ jarcher \ ScriptTest.ps1
เส้นทาง: C: \ Users \ jarcher

การเพิ่มในช่วงท้าย: หากคุณกังวลเกี่ยวกับความแปรปรวนของ abot (หรือจริงๆแล้วเพียงแค่ต้องการโค้ด "solid") คุณอาจต้องการใช้ "Write-Output" แทน "Write-Host"
KlaymenDK

20
จะเป็นการดีที่จะดูตัวอย่างการใช้ Split-Path ในคำตอบ คุณต้องแสดงการเรียกสคริปต์ภายในสคริปต์อื่นจริงๆ
Jeremy

45

ฉันกำลังเรียก myScript1.ps1 จาก myScript2.ps1

สมมติว่าสคริปต์ทั้งสองอยู่ในตำแหน่งเดียวกันก่อนอื่นให้รับตำแหน่งของสคริปต์โดยใช้คำสั่งนี้:

$PSScriptRoot

จากนั้นต่อท้ายชื่อสคริปต์ที่คุณต้องการเรียกแบบนี้:

& "$PSScriptRoot\myScript1.ps1"

สิ่งนี้ควรใช้งานได้


4
& "$PSScriptRoot\myScript1.ps1"ก็เพียงพอแล้ว
Weihui Guo

37

เส้นทางปัจจุบันของ MyScript1.ps1 ไม่เหมือนกับ myScript2.ps1 คุณสามารถรับเส้นทางโฟลเดอร์ของ MyScript2.ps1 และเชื่อมต่อกับ MyScript1.ps1 จากนั้นเรียกใช้งาน สคริปต์ทั้งสองต้องอยู่ในตำแหน่งเดียวกัน

## MyScript2.ps1 ##
$ScriptPath = Split-Path $MyInvocation.InvocationName
& "$ScriptPath\MyScript1.ps1"

ฉันต้องเริ่มต้นตัวแปร $ MyInvocation อย่างไร
Nicola Celiento

1
คุณไม่ได้เป็นตัวแปรอัตโนมัติ
Shay Levy

ใช้งานได้ แต่ได้รับข้อผิดพลาดต่อไปนี้ก่อนที่จะเรียกใช้สคริปต์จริง ๆ : Split-Path: ไม่สามารถผูกอาร์กิวเมนต์กับพารามิเตอร์ 'Path' ได้เนื่องจากเป็นสตริงว่าง ที่บรรทัด: 4 ถ่าน: 25 + $ ScriptPath = แยกเส้นทาง <<<< $ MyInvocation.InvocationName + CategoryInfo: invaliddata: (:) [Split-Path] ParameterBindingValidationException + FullyQualifiedErrorId: ParameterArgumentValidationErrorEmptyStringNotAllowed, Microsoft.PowerShell.Commands.SplitPathCommand
Nicola Celiento

สร้างสคริปต์ใหม่ใส่: $ MyInvocation.InvocationName ในนั้นและเรียกใช้สคริปต์ คุณได้รับเส้นทางของสคริปต์หรือไม่?
Shay Levy

@JasonMArcher - ทำไมแทน? เท่าที่ฉันรู้ทั้งสองให้ผลลัพธ์เดียวกัน?
manojlds

19

โซลูชันหนึ่งบรรทัด:

& ((Split-Path $MyInvocation.InvocationName) + "\MyScript1.ps1")

นี่เป็นสิ่งที่ดี แต่ทำไมไม่เพียงแค่& '.\MyScript1.ps'ว่าสคริปต์อยู่ในไดเร็กทอรีเดียวกัน
JoePC

4
ซึ่งใช้ไดเรกทอรีปัจจุบันไม่ใช่ไดเรกทอรีสคริปต์ แน่นอนว่าพวกเขามักจะเหมือนกัน ... แต่ไม่เสมอไป!
noelicus

11

นี่เป็นเพียงข้อมูลเพิ่มเติมสำหรับคำตอบเพื่อส่งต่ออาร์กิวเมนต์ไปยังไฟล์อื่น

ที่คุณคาดหวังการโต้แย้ง

PrintName.ps1

Param(
    [Parameter( Mandatory = $true)]
    $printName = "Joe"    
)


Write-Host $printName

วิธีเรียกไฟล์

Param(
    [Parameter( Mandatory = $false)]
    $name = "Joe"    
)


& ((Split-Path $MyInvocation.InvocationName) + "\PrintName.ps1") -printName $name

หากคุณไม่ป้อนข้อมูลใด ๆ ค่าเริ่มต้นจะเป็น "Joe" และสิ่งนี้จะถูกส่งเป็นอาร์กิวเมนต์ไปยังอาร์กิวเมนต์printNameในไฟล์PrintName.ps1ซึ่งจะพิมพ์สตริง"Joe"ออกมา


4

คุณอาจพบคำตอบแล้ว แต่นี่คือสิ่งที่ฉันทำ

ฉันมักจะวางบรรทัดนี้ไว้ที่จุดเริ่มต้นของสคริปต์การติดตั้งของฉัน:

if(!$PSScriptRoot){ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } #In case if $PSScriptRoot is empty (version of powershell V.2).  

จากนั้นฉันสามารถใช้ตัวแปร $ PSScriptRoot เป็นตำแหน่งของสคริปต์ปัจจุบัน (เส้นทาง) เช่นในตัวอย่างการร้อง:

if(!$PSScriptRoot){ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } #In case if $PSScriptRoot is empty (version of powershell V.2).  

Try {
If (Test-Path 'C:\Program Files (x86)') {
    $ChromeInstallArgs= "/i", "$PSScriptRoot\googlechromestandaloneenterprise64_v.57.0.2987.110.msi", "/q", "/norestart", "/L*v `"C:\Windows\Logs\Google_Chrome_57.0.2987.110_Install_x64.log`""
    Start-Process -FilePath msiexec -ArgumentList $ChromeInstallArgs -Wait -ErrorAction Stop
    $Result= [System.Environment]::ExitCode
} Else {
    $ChromeInstallArgs= "/i", "$PSScriptRoot\googlechromestandaloneenterprise_v.57.0.2987.110.msi", "/q", "/norestart", "/L*v `"C:\Windows\Logs\Google_Chrome_57.0.2987.110_Install_x86.log`""
    Start-Process -FilePath msiexec -ArgumentList $ChromeInstallArgs -Wait -ErrorAction Stop
    $Result= [System.Environment]::ExitCode
    }

} ### End Try block


Catch  {
    $Result = [System.Environment]::Exitcode
    [System.Environment]::Exit($Result)
   }
[System.Environment]::Exit($Result)

ในกรณีของคุณคุณสามารถแทนที่ได้

เริ่มต้นกระบวนการ ... บรรทัดกับ

เรียกใช้นิพจน์ $ PSScriptRoot \ ScriptName.ps1

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับตัวแปรอัตโนมัติ $ MYINVOCATION และ $ PSScriptRoot ได้ที่เว็บไซต์ Microsoft: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_automatic_variables


4

ในการเรียกใช้ไฟล์สคริปต์อย่างง่ายดายในโฟลเดอร์เดียวกัน (หรือโฟลเดอร์ย่อยของ) ในฐานะผู้เรียกคุณสามารถใช้สิ่งนี้:

# Get full path to the script:
$ScriptRoute = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, "Scriptname.ps1"))

# Execute script at location:
&"$ScriptRoute"

3

ฉันมีปัญหากับเรื่องนี้ ฉันไม่ได้ใช้$MyInvocationสิ่งที่ฉลาดเพื่อแก้ไขมัน หากคุณเปิด ISE โดยคลิกขวาที่ไฟล์สคริปต์editแล้วเลือกจากนั้นเปิดสคริปต์ที่สองจากภายใน ISE คุณสามารถเรียกใช้จากอีกสคริปต์หนึ่งได้โดยใช้ไวยากรณ์. \ script.ps1ปกติ ฉันเดาว่า ISE มีแนวคิดของโฟลเดอร์ปัจจุบันและการเปิดเช่นนี้จะตั้งค่าโฟลเดอร์ปัจจุบันเป็นโฟลเดอร์ที่มีสคริปต์ เมื่อฉันเรียกใช้สคริปต์หนึ่งจากอีกสคริปต์หนึ่งในการใช้งานปกติฉันใช้แค่. \ script.ps1 , IMO ผิดที่จะแก้ไขสคริปต์เพียงเพื่อให้ทำงานใน ISE ได้อย่างถูกต้อง ...


2

ฉันมีปัญหาที่คล้ายกันและแก้ไขด้วยวิธีนี้

ไดเร็กทอรีการทำงานของฉันคือโฟลเดอร์สคริปต์ทั่วไปและโฟลเดอร์สคริปต์เฉพาะที่ให้บริการในรูทเดียวกันฉันต้องเรียกโฟลเดอร์สคริปต์เฉพาะ (ซึ่งเรียกสคริปต์ทั่วไปพร้อมพารามิเตอร์ของปัญหาเฉพาะ) ไดเร็กทอรีการทำงานจึงเป็นเช่นนี้

\Nico\Scripts\Script1.ps1
             \Script2.ps1
      \Problem1\Solution1.ps1
               \ParameterForSolution1.config
      \Problem2\Solution2.ps1
               \ParameterForSolution2.config

Solutions1 และ Solutions2 เรียก PS1 ในโฟลเดอร์ Scripts โดยโหลดพารามิเตอร์ที่เก็บไว้ใน ParameterForSolution ดังนั้นใน powershell ISE ฉันเรียกใช้คำสั่งนี้

.\Nico\Problem1\Solution1.PS1

และรหัสภายใน Solution1.PS1 คือ:

# This is the path where my script is running
$path = split-path -parent $MyInvocation.MyCommand.Definition

# Change to root dir
cd "$path\..\.."

$script = ".\Script\Script1.PS1"

$parametro = "Problem1\ParameterForSolution1.config"
# Another set of parameter Script1.PS1 can receive for debuggin porpuose
$parametro +=' -verbose'

Invoke-Expression "$script $parametro"

2

ฉันส่งตัวอย่างของฉันเพื่อพิจารณา นี่คือวิธีที่ฉันเรียกโค้ดบางส่วนจากสคริปต์คอนโทรลเลอร์ในเครื่องมือที่ฉันสร้างขึ้น สคริปต์ที่ทำงานจำเป็นต้องยอมรับพารามิเตอร์ด้วยเช่นกันตัวอย่างนี้จะแสดงวิธีการส่งผ่าน ถือว่าสคริปต์ที่ถูกเรียกอยู่ในไดเร็กทอรีเดียวกับสคริปต์คอนโทรลเลอร์ (สคริปต์ที่ทำการเรียก)

[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string[]]
$Computername,

[Parameter(Mandatory = $true)]
[DateTime]
$StartTime,

[Parameter(Mandatory = $true)]
[DateTime]
$EndTime
)

$ZAEventLogDataSplat = @{
    "Computername" = $Computername
    "StartTime"    = $StartTime
    "EndTime"      = $EndTime
}

& "$PSScriptRoot\Get-ZAEventLogData.ps1" @ZAEventLogDataSplat

ข้างต้นเป็นสคริปต์คอนโทรลเลอร์ที่ยอมรับ 3 พารามิเตอร์ สิ่งเหล่านี้ถูกกำหนดไว้ในบล็อกพารามิเตอร์ จากนั้นสคริปต์ควบคุมจะเรียกใช้สคริปต์ชื่อ Get-ZAEventLogData.ps1 เพื่อประโยชน์ของตัวอย่างสคริปต์นี้ยังยอมรับพารามิเตอร์ 3 ตัวเดียวกัน เมื่อสคริปต์คอนโทรลเลอร์เรียกไปยังสคริปต์ที่ทำงานได้สคริปต์จะต้องเรียกใช้และส่งผ่านพารามิเตอร์ ด้านบนแสดงให้เห็นว่าฉันทำได้อย่างไรโดยการแยกชิ้นส่วน


1

คุณเรียกใช้สคริปต์ในตัวของ PowerShell ภายในสคริปต์ของคุณได้อย่างไร

คุณใช้สคริปต์ในตัวเช่น

Get-Location
pwd
ls
dir
split-path
::etc...

คอมพิวเตอร์ของคุณทำงานโดยตรวจสอบเส้นทางของสคริปต์โดยอัตโนมัติ

ในทำนองเดียวกันฉันสามารถเรียกใช้สคริปต์ที่กำหนดเองได้โดยเพียงแค่ใส่ชื่อของสคริปต์ในบล็อกสคริปต์

::sid.ps1 is a PS script I made to find the SID of any user
::it takes one argument, that argument would be the username
echo $(sid.ps1 jowers)


(returns something like)> S-X-X-XXXXXXXX-XXXXXXXXXX-XXX-XXXX


$(sid.ps1 jowers).Replace("S","X")

(returns same as above but with X instead of S)

ไปที่บรรทัดคำสั่ง powershell แล้วพิมพ์

> $profile

สิ่งนี้จะส่งคืนเส้นทางไปยังไฟล์ที่บรรทัดคำสั่ง PowerShell ของเราจะดำเนินการทุกครั้งที่คุณเปิดแอป

มันจะเป็นแบบนี้

C:\Users\jowers\OneDrive\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1

ไปที่เอกสารและดูว่าคุณมีไดเร็กทอรี WindowsPowerShell อยู่แล้วหรือไม่ ฉันไม่ได้เป็นเช่นนั้น

> cd \Users\jowers\Documents
> mkdir WindowsPowerShell
> cd WindowsPowerShell
> type file > Microsoft.PowerShellISE_profile.ps1

ตอนนี้เราได้สร้างสคริปต์ที่จะเปิดขึ้นทุกครั้งที่เราเปิดแอป PowerShell

เหตุผลที่เราทำเช่นนั้นก็เพื่อที่เราจะได้เพิ่มโฟลเดอร์ของเราเองที่เก็บสคริปต์ที่กำหนดเองทั้งหมดของเรา มาสร้างโฟลเดอร์นั้นกันและฉันจะตั้งชื่อว่า "Bin" ตามไดเร็กทอรีที่ Mac / Linux มีสคริปต์

> mkdir \Users\jowers\Bin

ตอนนี้เราต้องการให้เพิ่มไดเรกทอรีนั้นใน$env:pathตัวแปรของเราทุกครั้งที่เราเปิดแอปเพื่อกลับไปที่WindowsPowerShellไดเรกทอรีและ

> start Microsoft.PowerShellISE_profile.ps1

จากนั้นเพิ่มสิ่งนี้

$env:path += ";\Users\jowers\Bin"

ตอนนี้เชลล์จะค้นหาคำสั่งของคุณโดยอัตโนมัติตราบใดที่คุณบันทึกสคริปต์ของคุณในไดเร็กทอรี "Bin" นั้น

เปิด powershell อีกครั้งและควรเป็นหนึ่งในสคริปต์แรกที่เรียกใช้งาน

เรียกใช้สิ่งนี้ในบรรทัดคำสั่งหลังจากโหลดซ้ำเพื่อดูไดเร็กทอรีใหม่ของคุณในตัวแปรพา ธ ของคุณ:

> $env:Path

ตอนนี้เราสามารถเรียกสคริปต์ของเราจากบรรทัดคำสั่งหรือจากภายในสคริปต์อื่นได้ดังนี้:

$(customScript.ps1 arg1 arg2 ...)

อย่างที่คุณเห็นเราต้องเรียกพวกเขาด้วย.ps1ส่วนขยายจนกว่าเราจะสร้างนามแฝงให้ ถ้าเราอยากได้แบบแฟนซี


ว้าวขอบคุณสำหรับสิ่งนี้มีจำนวนมากที่นี่ แต่มีอีก 9 คำตอบอยู่แล้วที่นี่ สิ่งนี้แตกต่างกันอย่างไร? มีข้อมูลเพิ่มเติมอะไรบ้าง?
Stephen Rauch

การทำเช่นนี้ช่วยให้เราสามารถใช้สคริปต์ที่กำหนดเองของเราภายในสคริปต์อื่น ๆ ได้ในลักษณะเดียวกับที่ใช้สคริปต์ในตัวภายในสคริปต์ของเรา ทำสิ่งนี้และตราบเท่าที่คุณบันทึกสคริปต์ของคุณในไดเร็กทอรีที่คุณใส่ไว้ในเส้นทางของคุณคอมพิวเตอร์จะค้นหาเส้นทางของสคริปต์ที่กำหนดเองโดยอัตโนมัติเมื่อคุณใช้ในบรรทัดคำสั่งหรือในสคริปต์อื่น
Tyler Curtis Jowers
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.