cmdlets/CommonUtils.ps1

#01.2018©willynilly

#----------------------------------------------------------------------------------------
# Получает согласие пользователя на продолжение
function Get-Permission {
<#
    .Synopsis
        Получить разрешение от пользователя.
    .Description
        Задает вопрос пользователю и ожидает ответа Да/Нет клавишами Enter/Esc.
    .Parameter TextQuestion
        Текст однозначного вопроса (без знака вопроса) на который можно ответить Да/Нет.
        По умолчанию: "Продолжить?"
        Псевдонимы: t, q
    .Example
        if (Get-Permision "Очистить экран") {Clear-Host}
#>

    [CmdletBinding()]
    param(
        [Alias("t", "q", "Question")][string]$TextQuestion="Продолжить" # текст вопроса
    )
    # в ISE есть глобальная переменная psISE, а в VS Code есть psEditor
    # в ISE параметр $host.ui.RawUi.KeyAvailable равен NULL
    $ispsISE = (Test-path variable:psISE)
    $ispsEditor = (Test-path variable:psEditor)
    $isNullKeyAvailable = ($host.ui.RawUi.KeyAvailable -eq $null)
    Write-Verbose "psISE = $ispsISE"
    Write-Verbose "psEditor = $ispsEditor"
    Write-Verbose "isNullKeyAvailable = $isNullKeyAvailable"
    Write-Verbose ('host.ui.RawUi.KeyAvailable = {0}' -f $host.ui.RawUi.KeyAvailable)
    $Result = $true
    if ($ispsISE -or $ispsEditor -or $isNullKeyAvailable)
    {
        $InputUser = Read-Host ("`n{0}? (Да <Enter>\Нет <любой символ>)" -f $TextQuestion)
        if($InputUser.Length -gt 0) {$Result = $false}
    }
    else
    {
        Write-Host ("`n{0}? (Enter\Esc)" -f $TextQuestion) -ForegroundColor White
        while($True)
        {
            try {
                $Key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") # !!!НЕ РАБОТАЕТ в ISE и VS CODE!!!
                if ($key.VirtualKeyCode -eq 27) {$Result = $false; Break} # нажали Esc - выходим из скрипта
                if ($key.VirtualKeyCode -eq 13) {Break} # нажали Enter - выходим из цикла и идем удалять
            }
            catch {
                Write-Warning 'При считывании клавиатуры произошла ошибка. Запущен резервный вариант получения ответа.'
                $InputUser = Read-Host ('Да <Enter>\Нет <любой символ>')
                if($InputUser.Length -gt 0) {$Result = $false}
                Break
            }
        }
    }
    if (-not $Result){Write-Host "Отказ" -ForegroundColor Red}
    $Result
        
}# end Get-Permission

#----------------------------------------------------------------------------------------
# проверяет параметр скрипта WaitPressAnyKey и ждет нажатия любой клавиши если надо
function Wait-PressAnyKey {
<#
    .Synopsis
        Ждет нажатия любой клавиши.
    .Description
        Выводит сообщение "Для выхода нажмите любую клавишу..." и ждет.
    .Example
        if ($Wait) {Wait-PressAnyKey}
#>

    [CmdletBinding()]
    param()
    # в ISE есть глобальная переменная psISE, а в VS Code есть psEditor
    # в ISE параметр $host.ui.RawUi.KeyAvailable равен NULL
    $ispsISE = (Test-path variable:psISE)
    $ispsEditor = (Test-path variable:psEditor)
    $isNullKeyAvailable = ($host.ui.RawUi.KeyAvailable -eq $null)
    Write-Verbose "psISE = $ispsISE"
    Write-Verbose "psEditor = $ispsEditor"
    Write-Verbose "isNullKeyAvailable = $isNullKeyAvailable"
    Write-Verbose ('host.ui.RawUi.KeyAvailable = {0}' -f $host.ui.RawUi.KeyAvailable)
    if (-not($ispsISE -or $ispsEditor -or $isNullKeyAvailable))
    {
        #Остановка скрипта БЕЗ возможности отмены !!!НЕ РАБОТАЕТ В ISE и VS Code!!!
        Write-host "`nДля выхода нажмите любую клавишу..." -ForegroundColor White
        $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,AllowCtrlC") | Out-Null
    }
}# end Wait-PressAnyKey

#----------------------------------------------------------------------------------------
# получает кодировку файла
function Get-Encoding {
<#
    .Synopsis
        Получает кодировку текстового файла.
    .Description
        Определяет кодировку переданного текстового файла.
    .Parameter filePath
        Полный путь к текстовому файлу.
    .Outputs
        [System.Text.Encoding]
    .Example
        Get-Encoding C:\Names.txt
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$True)][string]$filePath
    )
    Try {
        $sr = New-Object System.IO.StreamReader($filePath, $true)
        [char[]] $buffer = new-object char[] 3
        $sr.Read($buffer, 0, 3) | Out-Null # задавим вывод, иначе попадет в результат функции
        $encoding = $sr.CurrentEncoding
        $sr.Close()
    } catch {
        #ничего делать не надо
    }
    #вернем полученную кодировку
    $encoding

}# end Get-Encoding

#----------------------------------------------------------------------------------------
#Открывает диалог выбора файла
function Select-FileDialog {
<#
    .Synopsis
        Выбор файла в диалоговом окне.
    .Description
        Открывает стандартное диалоговое окно выбора файла и возвращает полный путь выбранного файла.
    .Parameter Title
        Заголовок окна выбора файла.
    .Parameter Directory
        Начальный каталог выбора файла.
    .Parameter Filter
        Фильтр расширений файлов. По умолчанию все файлы.
    .Outputs
        [System.String]
    .Example
        $FileConfig = Select-FileDialog "Укажите файл настроек" "C:\Settings" "Файлы настроек (*.cfg)|*.cfg|Все файлы (*.*)|*.*"
#>

    param(
        [string]$Title,
        [string]$Directory,
        [string]$Filter="Все файлы (*.*)|*.*"
    )

    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
    $objForm = New-Object System.Windows.Forms.OpenFileDialog
    $objForm.InitialDirectory = $Directory
    $objForm.Filter = $Filter
    $objForm.Title = $Title
    $objForm.ShowDialog() | Out-Null
    $objForm.FileName
}

#----------------------------------------------------------------------------------------
# Конвертирует PSObject полученный после Convert-FromJSON в обычный Hastable
function Convert-PSObjectToHashtable
{
    <#
        .Synopsis
            Конвертирует PSObject в Hastable.
        .Description
            Рекурсивно конвертирует PSObject полученный после Convert-FromJSON в полноценный Hashtable.
        .Parameter InputObject
            Входной объект (может быть передан по конвейеру).
        .Outputs
            [System.Hastable]
        .Example
            $Config = Get-Content C:\Config.json | ConvertFrom-Json | Convert-PSObjectToHashtable
    #>

    param (
        [Parameter(ValueFromPipeline)]
        $InputObject
    )

    process
    {
        if ($null -eq $InputObject) { return $null }

        if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
            $collection = @(
                foreach ($object in $InputObject) { Convert-PSObjectToHashtable $object }
            )
            Write-Output -NoEnumerate $collection
        }
        elseif ($InputObject -is [psobject])
        {
            $hash = @{}
            foreach ($property in $InputObject.PSObject.Properties) {
                $hash[$property.Name] = Convert-PSObjectToHashtable $property.Value
            }
            $hash
        }
        else
        {
            $InputObject
        }
    }
}# end Convert-PSObjectToHashtable

#----------------------------------------------------------------------------------------
# Выполняет перекодировку строки
function ConvertTo-Encoding ([string]$From, [string]$To)
{
    Begin
    {
        $encFrom = [System.Text.Encoding]::GetEncoding($from)
        $encTo = [System.Text.Encoding]::GetEncoding($to)
    }
    Process
    {
        $bytes = $encTo.GetBytes($_)
        $bytes = [System.Text.Encoding]::Convert($encFrom, $encTo, $bytes)
        $encTo.GetString($bytes)
    }
}# end ConvertTo-Encoding