powershell - 如何将 psm1 和 psd1 文件合并为一个 powershell 函数?
问题描述
我正在尝试在 Powershell (7+) 中实现一个旧的 Bash 函数,以显示一些上次访问目录的堆栈。我从Pscx项目 repo 中找到了这段代码。归根结底,我希望将其视为独立功能。
但是我遇到了一些问题:
- 所需的代码位于 2 个文件中:
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
对新位置执行 a,则所有后续堆栈项都将被覆盖,而不是添加到列表的末尾(最后一项)。如何将 a 添加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
}
}
恐怕我不明白最后一个问题。如果您可以展示如何重现所描述的行为,很高兴更新答案
推荐阅读
- math - 为什么我们乘以 2 PI 来创建一个圆
- java - 为什么 java 对象比较在 '==' 和 .equals() 方法上都失败了?
- javascript - 浏览器和nodejs上有没有可以将base64字符串转换为utf8字符串的API?
- kubernetes - 如何在 helmchart 中设置自定义发布名称
- php - Facebook登录并通过命令行获取帖子
- java - 如果在并发操作中不存在,则 JPA 插入实体
- node.js - MariaDB 连接到 ExpressJS 访问被拒绝,没有密码错误
- html - 图像 inisde flex 项目缩小
- python - 模块“pytextrank”没有属性“parse_doc”
- javascript - 如何使用 javascript、html 和/或 css 获取两个元素并将它们集中在一起?