且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

点源文件中的 PowerShell 点源 - 导入类

更新时间:2023-12-04 21:50:04

PetSerAl 和之前无数次一样,已经提供对该问题的简短评论中的关键指针:

由于您使用了,您必须使用模块并在using module语句中导入它们为了使用它们.

遗憾的是,在撰写本文时,获取帮助 about_Modules).

具体来说,为了在 class 定义中引用类型(类),PowerShell 在解析时必须知道该类型.

在您的示例中,为了从 Car 类派生 Tesla,当脚本为 Car>解析执行开始之前 - 这就是为什么尝试通过点源导入Car为时已晚的原因它包含的脚本 (."$PSScriptRoot\Car.ps1")

PowerShell 在解析时通过以下两种方式之一了解引用类型:

  • 如果类型已经加载到当前的 PowerShell 会话中

  • 通过一个 using module 语句引用一个定义了类型(类)的模块(注意,还有一个 using assembly 语句用于加载类型来自 DLL,并使用命名空间 以启用仅通过名称引用类型).

    • 请注意,在这种情况下,相关的 Import-Module cmdlet 不起作用,因为它在运行时执行.

因此,正如 PetSerAl 所建议的:

  • 将您的类存储在模块文件(在最简单的情况下是独立的*.psm1文件)

  • 然后使用using moduleTesla模块导入Car模块和MyScript.ps1 脚本导入 Tesla 模块,使用相对于封闭脚本/模块位置的路径.

    • 警告:从 PowerShell Core 6.1.0-preview.4 开始,有一个 bug 要求您使用 \ 作为具有 using module 的相对路径中的路径分隔符,即使在 Unix 上- 类似平台,您通常在其中使用 /.
MyScript.ps1班级\Car.psm1 # 注意 .psm1 扩展名Tesla.psm1 # 同上

Car.psm1:

class Car {}

Tesla.psm1:

使用模块 .\Car.psm1特斯拉类:汽车{}

MyScript.ps1:

using module .\classes\Tesla.psm1$tesla = [特斯拉]::new()

My project is structured like this:

MyScript.ps1
classes\
    Car.ps1
    Tesla.ps1

Car.ps1 is the base class of Tesla.ps1. I attempt to define Tesla like this in Tesla.ps1:

. "$PSScriptRoot\Car.ps1"

class Tesla : Car
{
}

MyScript.ps1 needs to use the Tesla class, but shouldn't need to know that it inherits from Car.

. "$PSScriptRoot\classes\Tesla.ps1"

$tesla = [Tesla]::new()

Dot sourcing to classes\Tesla.ps1 works fine, but this error is thrown from the Tesla file:

Unable to find type [Car]

If I import all the files in the correct order in MyScript.ps1, it works fine. Example:

. "$PSScriptRoot\classes\Car.ps1"
. "$PSScriptRoot\classes\Tesla.ps1"

$tesla = [Tesla]::new()

This is cumbersome, especially as the complexity grows. Am I dot sourcing incorrectly? Is there a better way to import a custom PowerShell class using a relative path that isn't in the PSModulePath?

PetSerAl, as countless times before, has provided the crucial pointers in a terse comment on the question:

Due to your use of classes, you must use modules and import them with using module statements in order to use them.

Unfortunately, as of this writing, using module is still not mentioned in Get-Help about_Modules).

Specifically, in order to reference a type (class) in a class definition, that type must be known to PowerShell at parse time.

In your example, in order to derive Tesla from class Car, type Car must be known to PowerShell when the script is parsed, before execution begins - that's why it is too late to try to import Car by dot-sourcing its containing script (. "$PSScriptRoot\Car.ps1")

PowerShell knows about referenced types at parse time in one of two ways:

  • If the type is already loaded into the current PowerShell session

  • Via a using module statement that references a module in which the type (class) is defined (note that there's also a using assembly statement for loading types from DLLs, and using namespace to enable referring to types by their mere names).

    • Note that the related Import-Module cmdlet does not work in this case, because it executes at runtime.

Therefore, as PetSerAl suggests:

  • Store your classes in module files (stand-alone *.psm1 files in the simplest case)

  • Then use using module to have the Tesla module import the Car module and the MyScript.ps1 script import the Tesla module, using paths that are relative to the enclosing script / module's location.

    • Caveat: As of PowerShell Core 6.1.0-preview.4, there is a bug that requires that you use \ as the path separator in relative paths with using module, even on Unix-like platforms, where you'd normally use /.
MyScript.ps1
classes\
    Car.psm1    # Note the .psm1 extension
    Tesla.psm1  # Ditto

Car.psm1:

class Car {}

Tesla.psm1:

using module .\Car.psm1

class Tesla : Car {}

MyScript.ps1:

using module .\classes\Tesla.psm1

$tesla = [Tesla]::new()