我发现了以下我不理解的行为。我的$profile中有一些函数(特别是更改了prompt,所以function prmopt { }),其设置会更改提示,并且在我启动控制台时,如果将源函数(. PromptCustom)点源,它将完全生效,新的提示将接管。但是,我不希望自己的$profile太大,因此我将五个或五个左右不同的提示移到了模块中,但是当我尝试对其中的任何一个进行点源处理时,什么也没发生。它们只是输出提示内容,而不是取代默认的prompt

目的是要具有多种功能,可以根据需要在提示之间进行切换(即不是一个适用于每个控制台的提示,我只需要将function prompt放在$profile中)即可。当我将遵循以下模板的功能移动到模块时,它们全部中断,因此我想知道这是否是范围问题,以及如何实现在模块中具有多个提示功能的目标,而我可以在其中切换而不是被迫将它们保留在我的$profile中? (编辑:正如@ mklement0所指出的那样更新这个问题,因为它实际上是关于所需的目标的,即提示我可以在两者之间进行切换)。

这是我的提示函数之一,如果此函数是在我的$profile中定义的,则该函数会点源并将其完美地替代为默认提示,但是如果将其放入模块中则什么也不做:

function PromptShortenPath {
    # https://stackoverflow.com/questions/1338453/custom-powershell-prompts
    function shorten-path([string] $path) {
        $loc = $path.Replace($HOME, '~')
        # remove prefix for UNC paths
        $loc = $loc -replace '^[^:]+::', ''
        # make path shorter like tabs in Vim,
        # handle paths starting with \\ and . correctly
        return ($loc -replace '\\(\.?)([^\\])[^\\]*(?=\\)','\$1$2')
    }
    function prompt {
        # our theme
        $cdelim = [ConsoleColor]::DarkCyan
        $chost = [ConsoleColor]::Green
        $cloc = [ConsoleColor]::Cyan

        write-host "$([char]0x0A7) " -n -f $cloc
        write-host ([net.dns]::GetHostName()) -n -f $chost
        write-host ' {' -n -f $cdelim
        write-host (shorten-path (pwd).Path) -n -f $cloc
        write-host '}' -n -f $cdelim
        return ' '
    }

    if ($MyInvocation.InvocationName -eq "PromptShortenPath") {
        "`nWarning: Must dotsource '$($MyInvocation.MyCommand)' or it will not be applied to this session.`n`n   . $($MyInvocation.MyCommand)`n"
    } else {
        . prompt
    }
}

最佳答案

Scepticalist's helpful answer提供了一种在导入时激活prompt函数的有效解决方案。

您的问题中用于按需激活功能的方法是通过稍后点集一个嵌套了prompt函数的功能来实现的,如果该功能是从模块中导入的,则根本无法按书面要求进行工作,如下所述; 以获取解决方案,请参见底部

至于您尝试了什么:



这不是点源函数prompt的定义,而是在采购范围内运行该函数。

  • 实际上,它(无意义地)打印(一次,作为输出)提示字符串应该是什么,并使函数局部变量在调用者的范围内徘徊。

  • 因此,通过将prompt函数定义嵌套在PromptShortenPath函数内部,可以自动在调用者的作用域中定义prompt函数的点源,以及shorten-path函数[1]。
  • 如果您的PromptShortenPath函数是在模块外部定义的,则点源意味着它的源范围是(非模块)调用者的当前范围,它在此处定义了嵌套函数,并带有新的prompt函数的外观,交互式提示字符串按预期进行了更改。
  • 相反,如果在模块内部定义了PromptShortenPath函数,则点源表示源范围是源模块,这意味着调用方的当前范围不受影响,并且永远不会看到嵌套的shorten-pathprompt函数-因此,交互式提示字符串不会更改。
  • 这需要重复:点源函数(相对于脚本)在源范围域的当前范围而不是调用者的当前范围中运行该函数;就是说,从模块中点源提供一个函数总是在该模块的当前作用域中运行它,该作用域与调用者的作用域是不同的,并且与调用者的作用域无关(除非调用者恰好是同一模块内的顶级作用域)。

  • 相比之下,Scepticalist的解决方案通过使shorten-pathprompt函数成为模块的顶级函数,将它们隐式地(导出并)通过Import-Module导入到调用者的作用域中,并再次在调用者的作用域中出现新的prompt函数更改交互式提示字符串,即使在导入时也是如此。

    也可与模块配合使用的替代方法:

    最简单的解决方案是使用范围说明符global:定义嵌套函数,该函数直接在全局范围内定义它,而不管哪个范围包含该定义。

    作为有益的副作用,您然后不必再在调用时点源提示激活功能。

    请注意,下面的解决方案将帮助程序函数shorten-path嵌入global:prompt函数中,以确保其对后者的可用性。另一种选择是也将shorten-path定义为global:shorten-path,但是不需要使用辅助函数来使全局范围混乱,尤其是考虑到可能发生名称冲突的情况。
    # Use a dynamic module to simulate importing the `Set-Prompt` function
    # from a (regular, persisted) module.
    $null = New-Module {
    
      function Set-Prompt {
    
        # Note the `global:` prefix.
        Function global:prompt {
          # Note the *embedded* definition of helper function shorten-path,
          # which makes it available to the enclosing function only and avoids
          # the need to make the helper function global too.
          Function shorten-path([string] $path) {
            $loc = $path.Replace($HOME, '~')
            # remove prefix for UNC paths
            $loc = $loc -replace '^[^:]+::', ''
            # make path shorter like tabs in Vim,
            # handle paths starting with \\ and . correctly
            return ($loc -replace '\\(\.?)([^\\])[^\\]*(?=\\)', '\$1$2')
          }
    
          # our theme
          $cdelim = [ConsoleColor]::DarkCyan
          $chost = [ConsoleColor]::Green
          $cloc = [ConsoleColor]::Cyan
    
          Write-Host "$([char]0x0A7) " -n -f $cloc
          Write-Host ([net.dns]::GetHostName()) -n -f $chost
          Write-Host ' {' -n -f $cdelim
          Write-Host (shorten-path (pwd).Path) -n -f $cloc
          Write-Host '}' -n -f $cdelim
          return ' '
        }
    
      }
    
    }
    
    # Now that Set-Prompt is imported, invoke it as you would
    # any function, and the embedded `prompt` function will take effect.
    Set-Prompt
    

    [1]请注意,虽然shorten-path原则上遵循PowerShell的名词-动词命名约定,但shorten不在approved verbs列表中。

    10-07 19:50
    查看更多