Как объединить файл psm1 и psd1 в одну функцию powershell?

#powershell #windows-10

#powershell #windows-10

Вопрос:

Я пытаюсь реализовать старую функцию Bash в Powershell (7 ) для отображения стека некоторого количества последних посещенных каталогов. Я нашел этот код из репозитория проекта Pscx. В конце концов, я хотел бы видеть это как автономную функцию.

Однако я сталкиваюсь с несколькими проблемами:

  1. Требуемый код находится в 2 файлах: Pscx.CD.psm1 и Messages.psd1 . Их необходимо каким-то образом объединить в одну функцию, если это возможно.
  2. Появляются несколько разных сообщений об ошибках, хотя команды все еще работают. Я не знаю, что с этим делать.
 Cannot index into a null array.
At D:blahblahPscx.CD.psm1:137 char:21
                   if ($Pscx:Preferences['CD_GetChildItem'])
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   CategoryInfo          : InvalidOperation: (:) [], RuntimeException
   FullyQualifiedErrorId : NullArray
  
  1. Если вы в настоящее время находитесь где-то в середине стека и для выполнения a cd в новое местоположение, все последующие элементы стека перезаписываются, а не добавляются в конец списка (последний элемент). Как добавить 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 amp; 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)
        }
    }
}


  

Комментарии:

1. Можете ли вы более подробно объяснить последнюю проблему? Как воспроизвести?

2. # 3 Если вы это сделаете: cd A; cd B; cd C; cd D; и затем вы делаете cd -1 , чтобы попасть в каталог «B» (индекс 1). Если вы сейчас это сделаете cd F , все предыдущие индексы для C и D будут удалены, а «F» будет добавлен после «B». Я хочу сохранить предыдущие C и D и добавить F после.

Ответ №1:

Единственное изменение, необходимое для объединения данных из 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
    }
}
  

Боюсь, я не понимаю последнего вопроса. С удовольствием обновлю ответ, если вы можете показать, как воспроизвести описанное поведение

Комментарии:

1. Спасибо. Это отлично сработало. Последний пункт см. В Моем объяснении в комментарии выше.