Get-PSCallStack в Windows PowerShell

Я пишу рекурсивную функцию и хотел бы добавить режим отладки с некоторой информацией о вызывающем абоненте (функции) вместе с ScriptLineNumber.
В PowerShell 7 я могу сделать что-то вроде:

MyScript.ps1

[CmdletBinding()]param()
function MyFunction($Depth = 0) {
    if ($DebugPreference -in 'Stop', 'Continue', 'Inquire') {
        $Caller = (Get-PSCallStack)[1]
        Write-Debug "Caller: $($Caller.FunctionName), Line: $($Caller.ScriptLineNumber)"
    }
    Write-Host 'Current depth:' $Depth
    if ($Depth -lt 1) { MyFunction ($Depth + 1) }
}

MyFunction   
.\MyScript.ps1 -Debug
DEBUG: Caller: <ScriptBlock>, Line: 11
Current depth: 0
DEBUG: Caller: MyFunction, Line: 8
Current depth: 1

Как это сделать в Windows PowerShell 5.1?

Мне не хватает знаний C#, но я думаю, что ответ скрыт в этом коде C#, но я понятия не имею, откуда взялся Context.Debugger.GetCallStack()...

В чем именно заключается ваш вопрос? Get-PSCallStack уже доступен в 5.1...

Mathias R. Jessen 28.08.2024 11:33

Ваш код дал те же результаты как в версии 5.1, так и в версии 7.4.5. Если у вас это не так, возможно, у вас что-то не так с 5.1.

Darin 28.08.2024 11:43

@Darin, @MathiasR.Jessen, упс, в моем сценарии была опечатка: Get-CallStack вместо Get-PSCallStack (спасибо за ваш отзыв).

iRon 28.08.2024 11:51

@iRon Откуда Profiler? Get-CallStack требует объекта попадания bp от трассировщика в качестве обязательного аргумента параметра, поэтому опубликованный вами код никогда не будет работать, если мы заменим Get-PSCallStack на Get-CallStack. Если вы имеете в виду другой модуль, сообщите нам, какой :)

Mathias R. Jessen 28.08.2024 12:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Прежде чем ответить, я должен отметить, что Get-PSCallStack доступен в Windows PowerShell 5.1 и с тех пор не менялся, поэтому это упражнение, скорее всего, совершенно бесполезно.


Мне не хватает знаний C#, но я думаю, что ответ скрыт в этом коде C#, но я понятия не имею, откуда взялся Context.Debugger.GetCallStack()...

Context является защищенным членом PSCmdlet (точнее, его классом-предком InternalCommand), поэтому вы можете вызвать его из метода экземпляра, объявленного производным типом (например, классом GetPSCallStackCommand).

Поскольку у нас нет доступа к непубличному члену изнутри PSScriptCmdlet, нам понадобится немного магии отражения:

function Get-PSScriptCallStack {
    [CmdletBinding()]
    param() 

    $nonPublicInstanceFlags = [System.Reflection.BindingFlags]'Instance,NonPublic'
    
    $member_psCmdletContext = $PSCmdlet.GetType().GetMembers($nonPublicInstanceFlags).Where({ $_.Name -eq 'Context' })[0]
    $contextInstance = $member_psCmdletContext.GetValue($PSCmdlet, @())
    
    $member_contextDebugger = $contextInstance.GetType().GetMembers($nonPublicInstanceFlags).Where({ $_.Name -eq 'Debugger' })[0]
    $debuggerInstance = $member_contextDebugger.GetValue($contextInstance, @())

    # Debugger.GetCallStack() is public
    $debuggerInstance.GetCallStack()
}

Другие вопросы по теме