[英]How to merge a psm1 and a psd1 file into one powershell function?
我正在尝试在 Powershell (7+) 中实现一个旧的 Bash 函数来显示一些上次访问的目录的堆栈。 我从Pscx项目仓库中找到了这段代码。 归根结底,我希望将其视为独立功能。
但是我遇到了几个问题:
Pscx.CD.psm1
和Messages.psd1
。 如果可能的话,它们需要组合成一个函数。Cannot index into a null array.
At D:\blah\blah\Pscx.CD.psm1:137 char:21
+ if ($Pscx:Preferences['CD_GetChildItem'])
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
cd
,则所有后续堆栈项都将被覆盖,而不是添加到列表的末尾(最后一项)。 如何将cd <full path location>
到堆栈的末尾而不覆盖以前的项目?好的好的,这是合并的代码:
# From messages.psd1
ConvertFrom-StringData @'
SettingLocationF1=Setting location to: '{0}'
BackStackEmpty=The backward stack is empty.
ForeStackEmpty=The foreward stack is empty.
GoingToTheSameDir=Wherever you go, there you are!
NumOutOfRangeF1={0} is out of range.
'@
# From Messages.psd1
$backwardStack = new-object System.Collections.ArrayList
$forewardStack = new-object System.Collections.ArrayList
$ExecutionContext.SessionState.Module.OnRemove = {
Set-Alias cd Set-Location -Scope Global -Option AllScope -Force
}.GetNewClosure()
# We are going to replace the PowerShell default "cd" alias with the CD function defined below.
Set-Alias cd Set-LocationEx -Force -Scope Global -Option AllScope -Description "PSCX alias"
function Set-LocationEx
{
[CmdletBinding(DefaultParameterSetName='Path')]
param(
[Parameter(Position=0, ParameterSetName='Path', ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[string]
$Path,
[Parameter(Position=0, ParameterSetName='LiteralPath', ValueFromPipelineByPropertyName=$true)]
[Alias("PSPath")]
[string]
$LiteralPath,
[Parameter(ValueFromRemainingArguments=$true)]
[string[]]
$UnboundArguments,
[Parameter()]
[switch]
$PassThru,
[Parameter()]
[switch]
$UseTransaction
)
Begin
{
Set-StrictMode -Version Latest
# String resources
Import-LocalizedData -BindingVariable msgTbl -FileName Messages
$ExtraArgs = @{}
if (($PSVersionTable.PSVersion.Major -lt 6) -or ($PSVersionTable.PSEdition -eq 'Desktop'))
{
$ExtraArgs['UseTransaction'] = $UseTransaction
}
function SetLocationImpl($path, [switch]$IsLiteralPath)
{
if ($pscmdlet.ParameterSetName -eq 'LiteralPath' -or $IsLiteralPath)
{
Write-Debug "Setting location to literal path: '$path'"
Set-Location -LiteralPath $path @ExtraArgs
}
else
{
Write-Debug "Setting location to path: '$path'"
Set-Location $path @ExtraArgs
}
if ($PassThru)
{
Write-Output $ExecutionContext.SessionState.Path.CurrentLocation
}
else
{
# If not passing thru, then check for user options of other info to display.
if ($Pscx:Preferences['CD_GetChildItem'])
{
Get-ChildItem
}
elseif ($Pscx:Preferences['CD_EchoNewLocation'])
{
Write-Host $ExecutionContext.SessionState.Path.CurrentLocation
}
}
}
}
Process
{
if ($pscmdlet.ParameterSetName -eq 'Path')
{
Write-Debug "Path parameter received: '$Path'"
$aPath = $Path
}
else
{
Write-Debug "LiteralPath parameter received: '$LiteralPath'"
$aPath = $LiteralPath
}
if ($UnboundArguments -and $UnboundArguments.Count -gt 0)
{
$OFS=','
Write-Debug "Appending unbound arguments to path: '$UnboundArguments'"
$aPath = $aPath + " " + ($UnboundArguments -join ' ')
}
# If no input, dump contents of backward and foreward stacks
if (!$aPath)
{
# Command to dump the backward & foreward stacks
""
" # Directory Stack:"
" --- ----------------"
if ($backwardStack.Count -ge 0)
{
for ($i = 0; $i -lt $backwardStack.Count; $i++)
{
" {0,3} {1}" -f $i, $backwardStack[$i]
}
}
"-> {0,3} {1}" -f $i++,$ExecutionContext.SessionState.Path.CurrentLocation
if ($forewardStack.Count -ge 0)
{
$ndx = $i
for ($i = 0; $i -lt $forewardStack.Count; $i++)
{
" {0,3} {1}" -f ($ndx+$i), $forewardStack[$i]
}
}
""
return
}
Write-Debug "Processing arg: '$aPath'"
$currentPathInfo = $ExecutionContext.SessionState.Path.CurrentLocation
# Expand ..[.]+ out to ..\..[\..]+
if ($aPath -like "*...*")
{
$regex = [regex]"\.\.\."
while ($regex.IsMatch($aPath))
{
$aPath = $regex.Replace($aPath, "..$([System.IO.Path]::DirectorySeparatorChar)..")
}
}
if ($aPath -eq "-")
{
if ($backwardStack.Count -eq 0)
{
Write-Warning $msgTbl.BackStackEmpty
}
else
{
$lastNdx = $backwardStack.Count - 1
$prevPath = $backwardStack[$lastNdx]
SetLocationImpl $prevPath -IsLiteralPath
[void]$forewardStack.Insert(0, $currentPathInfo.Path)
$backwardStack.RemoveAt($lastNdx)
}
}
elseif ($aPath -eq "+")
{
if ($forewardStack.Count -eq 0)
{
Write-Warning $msgTbl.ForeStackEmpty
}
else
{
$nextPath = $forewardStack[0]
SetLocationImpl $nextPath -IsLiteralPath
[void]$backwardStack.Add($currentPathInfo.Path)
$forewardStack.RemoveAt(0)
}
}
elseif ($aPath -like "-[0-9]*")
{
[int]$num = $aPath.replace("-","")
$backstackSize = $backwardStack.Count
$forestackSize = $forewardStack.Count
if ($num -eq $backstackSize)
{
Write-Host "`n$($msgTbl.GoingToTheSameDir)`n"
}
elseif ($num -lt $backstackSize)
{
$selectedPath = $backwardStack[$num]
SetLocationImpl $selectedPath -IsLiteralPath
[void]$forewardStack.Insert(0, $currentPathInfo.Path)
$backwardStack.RemoveAt($num)
[int]$ndx = $num
[int]$count = $backwardStack.Count - $ndx
if ($count -gt 0)
{
$itemsToMove = $backwardStack.GetRange($ndx, $count)
$forewardStack.InsertRange(0, $itemsToMove)
$backwardStack.RemoveRange($ndx, $count)
}
}
elseif (($num -gt $backstackSize) -and ($num -lt ($backstackSize + 1 + $forestackSize)))
{
[int]$ndx = $num - ($backstackSize + 1)
$selectedPath = $forewardStack[$ndx]
SetLocationImpl $selectedPath -IsLiteralPath
[void]$backwardStack.Add($currentPathInfo.Path)
$forewardStack.RemoveAt($ndx)
[int]$count = $ndx
if ($count -gt 0)
{
$itemsToMove = $forewardStack.GetRange(0, $count)
$backwardStack.InsertRange(($backwardStack.Count), $itemsToMove)
$forewardStack.RemoveRange(0, $count)
}
}
else
{
Write-Warning ($msgTbl.NumOutOfRangeF1 -f $num)
}
}
else
{
$driveName = ''
if ($ExecutionContext.SessionState.Path.IsPSAbsolute($aPath, [ref]$driveName) -and
!(Test-Path -LiteralPath $aPath -PathType Container))
{
# File or a non-existant path - handle the case of "cd $profile" when the profile script doesn't exist
$aPath = Split-Path $aPath -Parent
Write-Debug "Path is not a container, attempting to set location to parent: '$aPath'"
}
SetLocationImpl $aPath
$forewardStack.Clear()
# Don't add the same path twice in a row
if ($backwardStack.Count -gt 0)
{
$newPathInfo = $ExecutionContext.SessionState.Path.CurrentLocation
if (($currentPathInfo.Provider -eq $newPathInfo.Provider) -and
($currentPathInfo.ProviderPath -eq $newPathInfo.ProviderPath))
{
return
}
}
[void]$backwardStack.Add($currentPathInfo.Path)
}
}
}
将Messages.psd1
的数据合并到函数定义中所需的唯一修改是在Begin
块中的这一行:
Import-LocalizedData -BindingVariable msgTbl -FileName Messages
此语句将在运行时从特定于语言环境的Messages.psd1
文件导入资源并将它们分配给$msgTbl
,因此我们需要将其替换为静态值:
$msgTbl = ConvertFrom-StringData @'
SettingLocationF1=Setting location to: '{0}'
BackStackEmpty=The backward stack is empty.
ForeStackEmpty=The foreward stack is empty.
GoingToTheSameDir=Wherever you go, there you are!
NumOutOfRangeF1={0} is out of range.
'@
您遇到的错误很容易修复 - 因为您只需要一个独立的功能,所以您无需担心特定于Pscx
模块的配置选项,因此只需完全删除此else
语句:
else
{
# If not passing thru, then check for user options of other info to display.
if ($Pscx:Preferences['CD_GetChildItem'])
{
Get-ChildItem
}
elseif ($Pscx:Preferences['CD_EchoNewLocation'])
{
Write-Host $ExecutionContext.SessionState.Path.CurrentLocation
}
}
恐怕我不明白最后一个问题。 如果您能展示如何重现所描述的行为,很高兴更新答案
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.