当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 使用 $MyInspiration 做更多事情

作者:精品下载站 日期:2024-12-14 08:03:47 浏览:15 分类:玩电脑

使用 $MyInspiration 做更多事情


不久前,有人在 Twitter 上对我发表了关于我分享的与 PowerShell 相关的内容的评论。他想了解有关 $MyInvocau 变量的更多信息。这是没有详细记录的内容,但在 PowerShell 脚本编写中非常有用。让我们更详细地看一下它。

当您运行 PowerShell 脚本或函数时,会自动创建 $MyInspiration 变量。从技术上讲,它是一个 System.Management.Automation.InitationInfo 对象。您可以在此处找到 Microsoft 的文档。但让我们从实际的角度来探讨一下这个问题。

这是一个简单的演示函数。

Function Get-Foo {
    [cmdletbinding()]
    Param(
        [Parameter(Position = 0, Mandatory)]
        [string]$Name,
        [datetime]$Since = (Get-Date).AddHours(-24)
        )

        $MyInvocation

        Write-Host "Getting $name since $since" -fore yellow
}

为了充分利用 $MyInitation,我发现最好将脚本文件点源化或将模块导入到 PowerShell 会话中。该函数除了显示 $MyInspiration 之外不执行任何操作。

[玩转系统] 使用 $MyInspiration 做更多事情

尽管这是一个丰富的对象,但您可能只会发现少数属性在 PowerShell 脚本编写中有用。我一直使用 MyCommand 属性。

我的调用.我的命令

这是演示函数的另一个版本。

Function Get-Foo {
    [cmdletbinding()]
    Param(
        [Parameter(Position = 0, Mandatory)]
        [string]$Name, [
        datetime]$Since = (Get-Date).AddHours(-24)
    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
    } #begin
    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Name"

        #create a global copy for testing purposes
        $global:mi = $myinvocation

        Write-Host "Getting $name since $since" -fore yellow
    } #process
    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end
}

这是我几乎所有 PowerShell 函数的格式。 Begin 和 End 脚本块具有反映命令名称的详细语句。我喜欢这种方法,因为如果我更改函数名称,我不必修改任何其他代码。

[玩转系统] 使用 $MyInspiration 做更多事情

当我有函数调用其他函数时,这才真正有用。这些详细的语句使跟踪流程变得更加容易。

Function Get-Foo {
    [cmdletbinding()]
    Param(
        [Parameter(Position = 0, Mandatory)]
        [string]$Name, [
        datetime]$Since = (Get-Date).AddHours(-24)
    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
    } #begin
    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Name"

        #create a global copy for testing purposes
        $global:mi = $myinvocation

        Write-Host "Getting $name since $since" -fore yellow
        If (Test-Foo $name) {
            $name
        }
    } #process
    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end
}

Function Test-Foo {
    [cmdletbinding()]
    Param([string]$Name)

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"

    } #begin
    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Testing $name"
        $True
    } #process
    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"

    } #end
}

[玩转系统] 使用 $MyInspiration 做更多事情

现在,详细输出显示我何时使用一个命令、启动另一个命令并返回到原始命令。多年来我发现这项技术非常宝贵。

脚本

您还可以将 $MyInspiration 与脚本一起使用。

#requires -version 5.1

Param(
    [Parameter(Position = 0, Mandatory)]
    [string]$Name,
    [datetime]$Since = (Get-Date).AddHours(-24)
)
write-host "Starting $(Resolve-Path $myinvocation.InvocationName)" -ForegroundColor green
write-host "PSScriptroot is $PSScriptRoot" -ForegroundColor green
#create a global copy for testing purposes
$global:mi = $MyInvocation

Write-Host "Getting $name since $since" -fore yellow
write-host "Ending $($myinvocation.mycommand)" -ForegroundColor green

throw "I failed"

[玩转系统] 使用 $MyInspiration 做更多事情

我可以从 $MyInspiration 捕获脚本名称。我更进一步,使用 Resolve-Path 将任何 PSDrive 或相对路径转换为完整文件系统路径。 $PSScriptRoot 反映当前命令的位置。但这就是事情变得有趣的地方。

调用信息

我的演示脚本故意抛出异常。我们来看一下。

[玩转系统] 使用 $MyInspiration 做更多事情

这是同一类型的对象,但属性的填充方式不同。在这里我可以看到错误发生的位置,即第 16 行。事实上,我什至看到了该行文本。这有可能。

调用调试

使用函数可以得到相同的结果。事实上,当您使用函数来获取 ps1 文件时,效果会更好。这是来自异常对象的 InloggingInfo。

[玩转系统] 使用 $MyInspiration 做更多事情

这并没有告诉我运行了什么函数,但它确实显示了我尝试在失败的函数内运行的命令、行号和源文件!

有了这些信息,我可以将代码添加到脚本中来编辑源文件并直接跳转到有问题的行。如果您仍在使用 PowerShell ISE,则可以使用 $PSISE 对象模型将其自动化。但是,PowerShell ISE 是一个编辑器,您无法让它自动运行脚本。相反,我有一个辅助函数来创建一个包含所需信息的临时文件。

Function New-ISETemp {
    [cmdletbinding()]
    [Outputtype("System.IO.FileInfo")]
    Param([string]$FilePath, [int]$LineNumber)

    $content = @"
#Press F5 to Run This Script

`$f = `$psise.CurrentPowerShellTab.files.add("$FilePath")
`$psise.CurrentFile.Editor.Focus()
`$LineNumber = $LineNumber
`$f.Editor.SetCaretPosition(`$LineNumber,1)
`$f.editor.SelectCaretLine()
"@

    $tmp = [system.io.path]::GetTempFileName() -replace "\.tmp$", ".ps1"
    $content | Out-File -FilePath $tmp

    #write the temp file as the function output
    $tmp
}

在我的函数中,我有代码在对我的函数使用 -Debug 时在 PowerShell ISE 中打开临时文件。

Function Get-Foo2 {
    [cmdletbinding()]
    Param(
        [Parameter(Position = 0, Mandatory)]
        [string]$Name, [
        datetime]$Since = (Get-Date).AddHours(-24)
    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Name"
        #create a global copy of myinvocation for testing purposes
        $global:mi = $myinvocation
        Write-Host "Getting $name since $since" -fore yellow

        Try {
            Get-Service $name -ErrorAction Stop
        }
        Catch {
            Write-Warning $_.exception.message
            write-Warning $DebugPreference
            #export for testing purposes
            $_.invocationInfo | Export-Clixml d:\temp\demo-error.xml
            if ($DebugPreference -match 'continue|inquire') {
                $src = $_.invocationInfo.PSCommandPath
                $line = $_.invocationInfo.ScriptLineNumber
                Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Debugging line $line"
                $f = New-ISETemp -filepath $src -linenumber $line
                powershell_ise $f
            }
        }

    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end
}

[玩转系统] 使用 $MyInspiration 做更多事情

PowerShell ISE 启动并加载了我的临时文件。

[玩转系统] 使用 $MyInspiration 做更多事情

我需要做的就是按 F5,加载源文件并选择有问题的行。

[玩转系统] 使用 $MyInspiration 做更多事情

使用 VS Code 调用调试

如果您使用 VS Code 作为编辑器,这会更容易。这是我的函数的 VSCode 版本。

Function Get-Foo2 {
    [cmdletbinding()]
    Param(
        [Parameter(Position = 0, Mandatory)]
        [string]$Name, [
        datetime]$Since = (Get-Date).AddHours(-24)
    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Name"
        #create a global copy of myinvocation for testing purposes
        $global:mi = $myinvocation
        Write-Host "Getting $name since $since" -fore yellow

        Try {
            Get-Service $name -ErrorAction Stop
        }
        Catch {
            Write-Warning $_.exception.message
            write-Warning $DebugPreference
            #export for testing purposes
            $_.invocationInfo | Export-Clixml d:\temp\demo-error.xml
            if ($DebugPreference -match 'continue|inquire') {
                $src = $_.invocationInfo.PSCommandPath
                $line = $_.invocationInfo.ScriptLineNumber
                $goto = "{0}:{1}" -f $src, $line
                Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Debugging $goto"
                #launch VS Code and jump to the line
                code.cmd --goto $goto             
            }
        } #catch
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end
}

现在,当我使用 -Debug 运行该函数时,VS Code 会加载该文件并直接跳转到该行。

[玩转系统] 使用 $MyInspiration 做更多事情

概括

您可能不需要在代码中合并这种递归调试。但我认为它演示了如何利用 InspirationInfo 对象的不同属性。如果你想出了一个有用的方法,我很想听听。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯