我发现了以下我不理解的行为。我的$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-path
和prompt
函数-因此,交互式提示字符串不会更改。相比之下,Scepticalist的解决方案通过使
shorten-path
和prompt
函数成为模块的顶级函数,将它们隐式地(导出并)通过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列表中。