В процессе разработки очень часто возникает необходимость запустить из powershell скрипта консольное приложение. Что может быть проще?

#test.ps1
& $PSScriptRoot\ConsoleApp.exe



Изучим поведение консольных приложений при запуске их из командной строки, через PowerShell и через PowerShell ISE:

Результат выполнения


В PowerShell ISE возникла проблема с кодировкой, так как ISE ожидает вывод в кодировке 1251. Воспользуемся гуглом и найдем два решения проблемы: c использованием [Console]::OutputEncoding и через powershell pipeline. Воспользуемся первым решением:

test2.ps1
$ErrorActionPreference = "Stop"

function RunConsole($scriptBlock)
{
    $encoding = [Console]::OutputEncoding 
    [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("cp866")
    try
    {
        &$scriptBlock
    }
    finally
    {
        [Console]::OutputEncoding = $encoding
    }
}

RunConsole {
    & $PSScriptRoot\ConsoleApp1.exe
}


Результат выполнения


В командной строке все хорошо, а вот в ISE ошибка. Exception setting «OutputEncoding»: «The handle is invalid.». Снова берем в руки гугл, и в первом же результате находим решение — надо запустить какое-нибудь консольное приложение для создания консоли. Ну что-же, попробуем.

test3.ps1
$ErrorActionPreference = "Stop"

function RunConsole($scriptBlock)
{
    # Популярное решение "устранения" ошибки: Exception setting "OutputEncoding": "The handle is invalid."
    & cmd /c ver | Out-Null

    $encoding = [Console]::OutputEncoding 
    [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("cp866")
    try
    {
        &$scriptBlock
    }
    finally
    {
        [Console]::OutputEncoding = $encoding
    }
}

RunConsole {
    & $PSScriptRoot\ConsoleApp1.exe
}


Результат выполнения


Все красиво, все работает. Кто читал мою прошлую заметку, обратил внимание, что WinRM приносит нам много острых впечатлений. Попробуем запустить тест через WinRM. Для запуска воспользуемся вот таким скриптом:

remote1.ps1
param($script)

$ErrorActionPreference = "Stop"

$s = New-PSSession "."
try
{
    $path = "$PSScriptRoot\$script"
    Invoke-Command -Session $s -ScriptBlock { &$using:path }
}
finally
{
    Remove-PSSession -Session $s
}



Результат выполнения


Что-то пошло не так. Решение с созданием консоли не работает. Ранее мы находили два решения проблемы кодировки. Попробуем второй:

test4.ps1
$ErrorActionPreference = "Stop"
#$VerbosePreference = "Continue"

function RunConsole($scriptBlock)
{
    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)
        }
    }

    Write-Verbose "RunConsole: Pipline mode"
    &$scriptBlock | ConvertTo-Encoding cp866 windows-1251 
}

RunConsole {
    & $PSScriptRoot\ConsoleApp1.exe
}


Результат выполнения


В ISE и через WinRM решение работает, а вот через командную строку и shell — нет.
Надо объединить эти два способа и проблема будет решена!

test5.ps1
$ErrorActionPreference = "Stop"
#$VerbosePreference = "Continue"

function RunConsole($scriptBlock)
{
    if([Environment]::UserInteractive)
    {
        # Популярное решение "устранения" ошибки: Exception setting "OutputEncoding": "The handle is invalid."
        & cmd /c ver | Out-Null

        $encoding = [Console]::OutputEncoding 
        [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("cp866")

        try
        {
            Write-Verbose "RunConsole: Console.OutputEncoding mode"
            &$scriptBlock
            return
        }
        finally
        {
            [Console]::OutputEncoding = $encoding
        }
    }

    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)
        }
    }

    Write-Verbose "RunConsole: Pipline mode"
    &$scriptBlock | ConvertTo-Encoding cp866 windows-1251 
}

RunConsole {
    & $PSScriptRoot\ConsoleApp1.exe
}


Результат выполнения


Кажется, что проблема решена, но продолжим исследование и усложним наше консольное приложение, добавив в него вывод в stdError.

Результат выполнения


Становится все веселее :) В ISE исполнение скрипта прервалось на середине, а через WinRM мало того, что прервалось, так еще сообщение из stdErr прочитать невозможно. Первым шагом решим проблему с остановкой запускаемого из скрипта приложения, для этого перед запуском приложения изменим значение глобальной переменной $ErrorActionPreference.

test7.ps1
$ErrorActionPreference = "Stop"
#$VerbosePreference = "Continue"

function RunConsole($scriptBlock)
{
    if([Environment]::UserInteractive)
    {
        # Популярное решение "устранения" ошибки: Exception setting "OutputEncoding": "The handle is invalid."
        & cmd /c ver | Out-Null

        $encoding = [Console]::OutputEncoding 
        [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("cp866")

        try
        {
            Write-Verbose "RunConsole: Console.OutputEncoding mode"
            $prevErrAction = $ErrorActionPreference
            $ErrorActionPreference = "Continue"
            try
            {
                &$scriptBlock
                return
            }
            finally
            {
                $ErrorActionPreference = $prevErrAction
            }
        }
        finally
        {
            [Console]::OutputEncoding = $encoding
        }
    }

    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)
        }
    }

    Write-Verbose "RunConsole: Pipline mode"
    $prevErrAction = $ErrorActionPreference
    $ErrorActionPreference = "Continue"
    try
    {
        &$scriptBlock | ConvertTo-Encoding cp866 windows-1251 
        return
    }
    finally
    {
        $ErrorActionPreference = $prevErrAction
    }
}

RunConsole {
    & $PSScriptRoot\ConsoleApp2.exe
}
Write-Host "ExitCode = $LASTEXITCODE"


Результат выполнения


Для тех что знает о существовании параметра -ErrorAction
error.cmd
echo error message 1>&2

errorActionTest.ps1
#error.cmd
#echo error message 1>&2

#errorActionTest.ps1
$ErrorActionPreference = "Stop"
Write-Host "before"
Invoke-Expression -ErrorAction SilentlyContinue -Command $PSScriptRoot\error.cmd
Write-Host "after"

Какой будет результат выполнения такого скрипта?

Вторым шагом доработаем скрипт удаленного запуска через WinRM, чтобы он не падал:

remote2.ps1
param($script)

$ErrorActionPreference = "Stop"

$s = New-PSSession "."
try
{
    $path = "$PSScriptRoot\$script"

    $err = @()
    $r = Invoke-Command -Session $s -ErrorAction Continue -ErrorVariable err -ScriptBlock `
    {
        $ErrorActionPreference = "Stop"
        & $using:path | Out-Host
        return $true
    } 

    if($r -ne $true)
    {
        Write-Error "The remote script was completed with an error"
    }

    if($err.length -ne 0)
    {
        Write-Warning "Error occurred on remote host"
    }
}
finally
{
    Remove-PSSession -Session $s
}


Результат выполнения


И осталось самое сложное — скорректировать сообщение формируемое через stdErr и при этом не изменить его положение в логе. В процессе решения этой задачи коллеги предложили самостоятельно создать консоль, воспользовавшись win api функцией AllocConsole.

test8.ps1
$ErrorActionPreference = "Stop"
#$VerbosePreference = "continue"

$consoleAllocated = [Environment]::UserInteractive
function AllocConsole()
{
    if($Global:consoleAllocated)
    {
        return
    }

    &cmd /c ver | Out-Null
    $a = @' 
[DllImport("kernel32", SetLastError = true)] 
public static extern bool AllocConsole(); 
'@

    $params = New-Object CodeDom.Compiler.CompilerParameters 
    $params.MainClass = "methods" 
    $params.GenerateInMemory = $true 
    $params.CompilerOptions = "/unsafe" 
 
    $r = Add-Type -MemberDefinition $a -Name methods -Namespace kernel32 -PassThru -CompilerParameters $params

    Write-Verbose "Allocating console"
    [kernel32.methods]::AllocConsole() | Out-Null
    Write-Verbose "Console allocated"
    $Global:consoleAllocated = $true
}

function RunConsole($scriptBlock)
{
    AllocConsole

    $encoding = [Console]::OutputEncoding 
    [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("cp866")
    $prevErrAction = $ErrorActionPreference
    $ErrorActionPreference = "Continue"
    try
    {
        & $scriptBlock
    }
    finally
    {
        $ErrorActionPreference = $prevErrAction
        [Console]::OutputEncoding = $encoding
    }
}

RunConsole {
    & $PSScriptRoot\ConsoleApp2.exe
}
Write-Host "ExitCode = $LASTEXITCODE"




Избавится от информации, которую добавляет powershell к stdErr мне так и не удалось.

Надеюсь, что эта информация окажется полезной не только мне! :)

update 1
В некоторых сценариях использования создавалась дополнительная консоль, в которую выдавался результат выполнения скриптов. В скрипт test8.ps1 внесены исправления.

update 2
Так как у многих комментаторов статьи возникла путаница между понятиями набор символов (char set) и кодировка (encoding) хотел бы еще раз обратить внимание, что в статье решается проблема именно несоответствия кодировок консоли и вызываемого приложения.

Как можно увидеть из скрипта test8.ps1, кодировка указывается в статическом свойстве [Console]::OutputEncoding, и никто не мешает указать в нем одну из кодировок семейства unicode:
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("utf-8")

Но, для работы скрипта в стандартной консоли windows (aka cmd.exe) необходимо изменить шрифт консоли со стандартного «Rasters Fonts» на Consolas или «Lucida Console». Если бы данные скрипты мы использовали только на собственных рабочих станциях или серверах, то такое изменение было бы допустимо, но так как нам приходится распространять наши решения заказчикам, вмешиваться в системные настройки серверов мы не имеем права. Именно по этой причине в скриптах используется cp866, как кодировка настроенная по умолчанию для консоли.
Поделиться с друзьями
-->

Комментарии (36)


  1. d-stream
    03.02.2017 21:39
    +1

    Я извиняюсь, а разве chcp unicode либо chcp 1251 не помогут?


    1. kuda78
      03.02.2017 21:51
      +2

      Не поможет.

      chcp 1251

      test-1.ps1
      $ErrorActionPreference = "Stop"
      chcp 1251
      & $PSScriptRoot\ConsoleApp1.exe
      


  1. rumkin
    04.02.2017 00:49
    +2

    Русские символы? Кажется, вы хотели сказать "кириллица".


  1. immaculate
    04.02.2017 07:41
    +19

    Не обижайтесь, но… Идет 2017 год, все человечество уже лет 15-17 точно перешло на Unicode. И только в Windows нужны какие-то эзотерические заклинания, вспоминать, что такое магические 866 и 1251. Понимаю, что все это видимо ради совместимости с приложениями 30-летней давности, но все же, можно было бы придумать и более изящное решение, особенно учитывая, что PowerShell — относительно недавнее изобретение и ему по большому счету совместимость с 30-летними приложениями не нужна.


    1. kuda78
      04.02.2017 09:23
      -10

      Как хорошо, что есть люди, которые верят в существование серебряной пули.
      Если немного глубже погрузиться в изучение стандарта unicode то можно узнать, что он состоит из двух основных разделов: универсальный набор символов (universal character set) и семейство кодировок (unicode transformation format). Так как в статье озвучена проблема кодировок, то посмотрим, какой зоопарк нам предоставляет unicode, а это UTF-8, UTF-16, UTF-32 + модификации LE / BE, ну и экзотика типа UTF-7, UTF-9, UTF-18. Так что проблемы кодировок символов с приходом unicode никуда не делись.


      1. immaculate
        04.02.2017 09:38
        +12

        Unicode конечно несовершенен, как и все в этом мире. Но UTF-8 покрывает значительно большее количество случаев и намного более удобен, чем подобные костыли с code pages.


        Я не знаток всех мировых языков. На повседневном уровне использую три: русский, английский, испанский. У меня в альтернативной операционной системе, что в консоли, что в любом другом приложении можно свободно использовать и комбинировать эти языки как угодно. При необходимости можно использовать еще сколько угодно языков. Да, наверное, какие-то проблемы с редкими символами из японского/китайского или малаяламского могут возникнуть, специально этим не интересовался.


        В общем, Unicode — может быть и не всеобъемлющее решение, но однозначно намного более универсальное, чем полуработающие костыли (автор, насколько я понимаю, так и не добился работы без предупреждений во всех ситуациях). Допустим, если у нас существует необходимость работать с 2-3 языками одновременно, то я не вижу возможного решения вообще.


        И это в 2017 году, когда по улицам скоро начнут ездить робомобили с искусственным интеллектом, память компьютеров измеряется десятками, если не сотнями гигабайт, а скорость процессоров дошла до физического предела… Но до сих пор в консоли Windows существуют ограничения 30-летней давности, когда компьютеры были слабее, чем самый слабый процессор в наручных часах...


        1. kuda78
          04.02.2017 10:40

          Но UTF-8 покрывает значительно большее количество случаев и намного более удобен, чем подобные костыли с code pages.

          А почему именно UTF-8?

          Был опыт в попытке подружить вывод консоли TalendStudio с со сборочным агентом bamboo работающем под linux, вот только первый отправлял в stdout данные в кодировке UTF-16 а агент считывал их используя кодировку UTF-8. Так что использование unicode и не windows стека технологий в общем случае не спасет от возникновения проблем.

          Ну и не надо забывать, что первоначально была задача решить проблему производства. :)


    1. ForNeVeR
      04.02.2017 11:14
      -1

      PowerShell — относительно недавнее изобретение и ему по большому счету совместимость с 30-летними приложениями не нужна.

      Да, конечно, вы правы. И поэтому так такой совместимости по умолчанию и не предусмотрено: приложения, которые пользуются современными юникодовыми API (WriteConsoleW), будут печатать вывод нормально, а для устаревших приложений нужны бубны (некоторые сорта которых описаны в посте). К сожалению, приложений, которые не пользуются современными юникодовыми API, ещё очень много. Многие рантаймы языков программирования также этот режим вывода не поддерживают, что ещё печальнее.


      А в линуксе как вы будете запускать программу, которая выводит UTF-16 в stdout, и чтоб этот текст отобразился на терминале?


      (Я не утверждаю, что вы не сможете это сделать, просто мне действительно интересно посмотреть на решение.)


      1. GreyCat
        04.02.2017 11:17
        +1

        Во-первых, приложение, которое выводит на stdout интерактивный контент не в том, что указано в переменных локализации LC_*, надо исправлять, а не жить с этим. Это — ошибка, а не нормальное поведение.


        Во-вторых, если очень нужно — большинство эмуляторов терминала умеют установить произвольную кодировку в качестве отображаемой, в том числе варианты UTF16.


        1. ForNeVeR
          04.02.2017 11:23

          приложение, которое выводит на stdout интерактивный контент не в том, что указано в переменных локализации LC_*, надо исправлять, а не жить с этим. Это — ошибка, а не нормальное поведение.

          В этом я с вами тоже полностью согласен! Приложения, которые пытаются использовать функции семейства WriteConsoleA (которые по определению работают в текущей OEM-кодировке), чтобы выводить текст в другой кодировке, тоже надо исправлять. В Windows, если вам угодно, переменные локализации LC_* всегда константы и указывают UTF-16 кодировку, для которой есть соответствующее семейство функций вывода на экран. К сожалению, многие программы это игнорируют и пытаются совать в OEM-функции тексты в своих кодировках, что иногда и вызывает проблемы.


          если очень нужно — большинство эмуляторов терминала умеют установить произвольную кодировку в качестве отображаемой, в том числе варианты UTF16.

          А это не испортит вывод предыдущих команд в том же терминале? Пожалуй, для каких-то единичных программ всё-таки лучше пайпать их вывод в iconv.


          1. GreyCat
            04.02.2017 19:11

            Приложения, которые пытаются использовать функции семейства WriteConsoleA [...] тоже надо исправлять

            Только вот Microsoft с вами, боюсь, не согласен. Иначе бы делали какие-то телодвижения по объявлению *A функций deprecated и пытались бы как-то форсировать переход человечества в светлое будущее. Вот здесь говорят, что их пытаются deprecate'ить, но только по каким-то одиночным библиотекам типа winsock2.


            А это не испортит вывод предыдущих команд в том же терминале?

            Не должно. Хотя бы потому, что терминал, вообще говоря, не имеет возможности переинтерпретировать какую-то часть старого потока символов при таком переключении кодировки (хотя бы потому, что эту часть еще как-то выделить и запомнить надо, а это потребует весьма нетривиальных телодвижений). Это же не браузер, в котором кодировку всего документа переключают — документа-то нет.


            1. ForNeVeR
              05.02.2017 08:09

              Депрекейтить необязательно, достаточно только лишь подробно рассмотреть вопрос. У функции WriteConsoleA есть вполне определённое поведение: вывести строку в текущей OEM-кодировке (ну, вообще говоря, просто в текущей кодировке консоли в соответствии с SetConsoleOutputCP, которая по умолчанию инициализируется из OEM).


              Если конкретному приложению нужно именно это (мне сложно представить себе современное приложение с такими нуждами, но мало ли) — оно вполне легитимно вправе использовать это семейство функций.


              Если же конкретное приложение хочет выводить какой-то национальный текст вне зависимости от кодировки терминала — оно должно использовать соответствующие юникодовые функции (a la WriteConsoleW). Вот и всё.


    1. YaakovTooth
      04.02.2017 13:19
      +4

      Не обижайтесь, но во-первых, отучайтесь говорить за всё человечество, во-вторых, unicode в той же ntfs появился в 1993 году, когда… в каком состоянии был софт, который вы используете, что вам позволяет не зная в принципе матчасти игнорируя реальный кейс по powershell пускаться в публичные пространные рассуждения внезапно об операционной системе в целом, человечестве и необходимости обратной совместимости?

      Про обратную совместимость: благодаря этим бесконечным костылям в windows 10.0 запускается и работает то, что писалось во времена windows 3.5. А уж без специфических программ если — то windows 5 софт работает без проблем в windows 10.

      Вы на каком красноглазии сидите, что позволяете себе свысока подобные сентенции публично? Там можно десятилетний бинарник запустить без проблем, да?


      1. sumanai
        04.02.2017 14:02

        unicode в той же ntfs появился в 1993 году

        И там UTF-16.


        1. YaakovTooth
          04.02.2017 14:04
          +2

          Что не имеет никакого отношения к тому, что тот человек, которому я отвечал — зачем-то проигнорировал тему хорошего поста про powershell и кодировки, да начал спрашивать с автора поста как с Марка Руссиновича с каким-то неуместным выпендрёжем про Windows без знания матчасти. :)


      1. immaculate
        05.02.2017 12:16
        +2

        Простите, что так задел вас.


        Не знаю, как с бинарной совместимостью с Linux. Она там есть, но насколько хорошая — не знаю, т.к. весь необходимый мне софт — opensource — поэтому никогда не нужна была бинарная совместимость. Раньше у меня было время читать lkml и множество других источников информации о развитии Linux, и помню, что этот вопрос обсуждался и совместимость старались сохранить.


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


        Что касается поддержки Unicode в ntfs — не знаю, что тут сказать. Может быть это требовалось по каким-то причинам для ntfs, про файловые системы Linux/Unix можно сказать, что они поддерживали Unicode с самого начала существования, еще до возникновения Unicode, так как для большинства файловых систем Linux/Unix — имя файла — это просто набор байтов, который они никак не пытаются интерпретировать. То же самое относится, естественно, к содержимому файла. Поэтому можно использовать хоть Unicode, хоть что угодно вместо него.


        Но тут речь не о сравнении операционных систем — я не пытался и не хотел начинать такой разговор. Просто описанное в статье мне показалось костылями, да еще и неработающими. Выразил свое удивление, вот и все. Может быть все эти неудобства стоят того, чтобы кто-то мог запустить на Windows 10 программу для Windows 3.11. Меня это удивляет, конечно, потому что таких людей наверняка меньше 1%, и почему бы им не использовать копию Win 3.11 или виртуальную машину, вместо того, чтобы миллионы людей по всему миру испытывали неудобства.


        1. Danik-ik
          06.02.2017 08:36

          Мне кажется, эта полемика возникла из взаимонепонимания двух ортогональных точек зрения: Ваша, "как надо писать программы"(точка зрения разработчика) и автора, "как заставить работать то, что написали другие"(т.зр. сисадмина). Вы поавы, ситуацию надо менять в корне. Но пока она не исправлена, проблема есть и её надо решать в рамках задач по эксплуатации независимо от того, что "есть правильный путь". Так что да, автор тоже прав, что ищет пути.


          1. immaculate
            06.02.2017 08:53

            Я не обладаю всей информацией, чтобы предложить решение. Всю жизнь использую Linux — так сложилось, что Linux увидел раньше Windows, еще где-то в 95 или 96 году. С Windows знаком постольку-поскольку, сталкиваюсь лишь на компьютерах знакомых.


            Поэтому мне сложно предложить техническое решение, решающее данную проблему. Просто удивило — все это выглядит как нагромождение костылей. Представляю какое астрономическое количество человеко-лет тратит Microsoft на поддержку этой совместимости, в том числе откровенно дебильных codepages, которые, мне казалось, давно остались лишь в музеях. А затем пользователи Windows по всему миру тратят свои человеко-часы, чтобы построить новые костыли поверх старых, чтобы обойти эти грабли древних кодировок.


            В итоге, полмира тратит время впустую, лишь бы кто-то мог запустить supercalc.exe 1986 года на операционной системе 2015-го года. Мне почему-то это не кажется поводом для гордости. Есть какой-то баланс между ломанием совместимости в каждой новой версии и фанатичной поддержкой совместимости. Кому нужно работать с приложениями 20-30 летней давности, скорее всего будет делать это на старом железе со старой системой. В крайнем случае, почему бы не установить виртуальную машину.


            Зачем подвергать людей таким пыткам, что они даже банальный скрипт невозможно написать без танцев с бубном? Неужели не существует разумного способа решить данную проблему раз и навсегда? Например, сказать: «пусть cmd и все прочее, что потенциально может использоваться для старых приложений, будет использовать codepages, а для PowerScript мы делаем стандартом Unicode?»


            1. ggo
              06.02.2017 09:49
              +1

              Когда ваша ОС стоит в куче мегакорпораций, и мегакорпорации заносят к тебе кучу бабла, и какой-нибудь supercalc.exe перестает работать, то с этим что-то надо делать.


              1. immaculate
                06.02.2017 10:55
                -1

                Ну, Linux тоже стоит в куче мегакорпораций, и ничего, как-то умудряется поддерживать всюду сквозной unicode без костылей. В любом приложении, консольном или GUI, написанном на любом языке программирования, с использованием любого фреймворка, могу вводить текст на нескольких языках, и он будет отображаться всюду без проблем, сохраняться, загружаться. По-моему, это просто необходимое условие для минимально комфортного уровня работы, потому что за пределами Северной Америки, почти всем людям приходится ежедневно использовать два и более языка.


                Про supercalc — не верю, что кто-то покупает новую машину с i7, устанавливает Windows 10, и все это, для того, чтобы запустить supercalc. Если такие пользователи и есть, то у них стоит какое-нибудь древнее железо, на котором крутится DOS, Windows 3.11 или что-то подобное.


                Понятно, что полная статистика есть только у Microsoft, да и есть ли?.. Но все же сомнительно, чтобы такие пользователи составляли хоть сколько-нибудь значительный процент среди покупателей тех же Windows 7, Windows 10.


                1. kuda78
                  06.02.2017 12:09

                  А мне вот интересно, если бы в статье был описан более частный случай с несогласованностью кодировок UTF-16 в приложении, UTF-8 в «консоли» Вы бы тоже писали, что эта проблема вот уже 100 лет как решена в linux, а Microsoft только на бабки пользователей разводит? :)

                  Ну и как я писал выше — путаете набор символов (char set) и кодировку (encoding).


                  1. immaculate
                    06.02.2017 12:23

                    Я нигде не писал, что «Microsoft разводит пользователей на бабки». И отлично знаю разницу между char set и encoding, хотя частенько использую термин unicode как синоним utf-8, прошу за это прощения.


                    На самом деле удивило, что до сих пор людям приходится бороться с code pages причем в современных языках и приложениях. Когда уже много лет не испытывал абсолютно никаких проблем в этой области, подобные проблемы на самом деле удивляют. 866? 1251? Какие-то хаки, чтобы прочитать кириллицу в выводе скрипта? Это действительно удивительно при взгляде с другой стороны баррикад.


                    Причем я ни в одном комментарии не критиковал Microsoft, только лишь выражал искреннее недоумение.


        1. YaakovTooth
          06.02.2017 11:34
          +1

          Простите, что так задел вас

          Какой унылый демагог. Ваше мнение, безусловно, очень важно, держите в курсе.


      1. muon
        06.02.2017 07:17

        благодаря этим бесконечным костылям в windows 10.0 запускается и работает кое-что, что писалось во времена windows 3.5

        жизнефикс


    1. PsyHaSTe
      04.02.2017 15:39
      +3

      Это просто баг на самом деле.

      PowerShell — относительно недавнее изобретение и ему по большому счету совместимость с 30-летними приложениями не нужна.

      Это вам не нужно и мне не нужно, а у майкрософта это основная бизнес-фича и священная корова.


    1. AndreyDmitriev
      05.02.2017 09:36
      +2

      Идет 2017 год, все человечество уже лет 15-17 точно перешло на Unicode. И только в Windows нужны какие-то эзотерические заклинания

      Тут не очень понятно, к чему вы это. Я вот при установке Windows на ноутбук, погорячившись, назвал профиль «Андрей». Ну и в результате, то там то сям вылазит:

      Qt проект, положенный в такую папку, вообще отказывается собираться, и так далее.
      Нет пока что в мире совершенства, хоть и 2017 год на дворе.
      Особенно доставляет то, что у меня все языковые настройки немецкие — с этой стороны тоже вылазят свои трогательные косяки.
      С другой стороны, есть пользователи (коих, вероятно, большинство), которые с такой проблемой не сталкивались, просто потому, что не используют «проблемное» сочетание продуктов за ненадобностью.
      Автор в одном конкретном случае получил вывод в неверной кодировке, разобрался что к чему, исправил это дело и поделился.
      Мне вот по долгу службы приходится разрабатывать десктопное приложение, локализованное для многих языков, при этом используя IDE с весьма кривой поддержкой юникода, так у меня целая коллекция таких вот случаев (хотя они слишком частные, и не будут интересны широкому кругу).
      Шаг за шагом эти проблемы решаются, но пройдёт ещё довольно много времени, прежде чем все эти болячки окончательно исчезнут.


  1. PsyHaSTe
    04.02.2017 18:38
    +2

    А при чем тут кстати вообще powershell? Вот пример приложения:

    using System;
    using System.Text;
    
    class Program
    {
    	static void Main(string[] args)
    	{
    		Console.OutputEncoding = Encoding.UTF8;
    		Console.WriteLine("c'est-a-dire хелло ???????????");
    	}
    }
    

    вот скрипт (1.ps1 в UTF8-BOM):
    Write-Output "c'est-a-dire хелло ???????????"
    &"C:\Users\Alex\Documents\visual studio 2017\Projects\SamplePowershellApp\SamplePowershellApp\bin\Debug\SamplePowershellApp.exe"
    

    Результат:image


    1. kuda78
      04.02.2017 22:58
      -1

      Самая большая проблема данного решения — требует изменения кода устанавливаемого приложения. Это далеко не всегда возможно.


      1. PsyHaSTe
        04.02.2017 23:45
        +1

        А при чем тут powershell? в исходниках экзешника уже захардкожена кодировка вывода, тут уж с какими бубнами ни пляши, ничего иного и не получится.


        1. kuda78
          05.02.2017 08:11
          -1

          Так большая же часть программ написана вот таким образом:

          	static void Main(string[] args)
          	{
          		Console.WriteLine("c'est-a-dire хелло ???????????");
          	}
          


          1. PsyHaSTe
            05.02.2017 12:17
            +1

            Ну так запустите экзешник из студии, и получите ровно тот же результат. Так и не стало понятно, при чем тут powershell


            1. Danik-ik
              06.02.2017 17:01
              -1

              Действительно, при чём здесь PowerShell, если автор решает проблему в приложении к powershell скриптам? Как, как он посмел подумать про powershell, когда решал, как отобразить некракозябры через powershell?!!


  1. pak-nikolai
    05.02.2017 08:20

    я лично в exchange использую guid вместо имени чтобы не привязываться к кодировкам, вдруг кто на украинской винде будет запускать.

    если надо сделать парсинг консольной проги то часто даже не делаю encoding, просто делаю вызов на английском и на русском в кракозяблах (например query session вернет вместо слова Активный какозяблики), если отработает англ часть то пропустится на русском и наоборот, читаемость на экране нужна только человеку и при анализе. При превращении в объект она не нужна.

    определение культуры и раскладки за все время пригодилось всего лишь 1 раз

    если у вас есть самописная dos прога то можно сделать test.exe > test.txt и после анализировать содержание, я лично так делал когда возникала потребность.

    именно сцепка потока ввода вывода и анализ на лету мне не встречался еще в природе, это какойто интерактивный процесс разве что


    1. kuda78
      05.02.2017 08:30

      Данные скрипты один из кирпичиков для Continuous Deployment, соответственно стояла задача сделать все логи в нормальной кодировке, чтобы при разборе проблем уметь сразу всю необходимую информацию.


  1. slavius
    06.02.2017 09:53

    Эх, еще-бы такую-же проблему для CMD/PsExec решить…


  1. RogueShy
    06.02.2017 12:42

    Занятно, спасибо.

    Для себя эту проблему (в терминале, а не в ISE) решил установкой кастомного терминала и макросами на смену шрифта вывода — на удалённом хосте включаем шрифт Terminal, и кракозябры превращаются в читаемый вывод.