Как исправить проблему со строкой PowerShell ISE с помощью внешней команды в Windows 11

У меня есть сценарий PowerShell (ориентированный на 5.1), который при запуске с использованием PowerShell ISE в Windows 10 работает нормально, но в Windows 11 происходит сбой. Скрипт передает строковые литералы, например, в sqlite3.exe.

".show" | & .\sqlite3.exe db

Успешный вывод в Windows 10:

    echo: off
     eqp: off
 explain: auto
 headers: off
    mode: list    nullvalue: ""
  output: stdout colseparator: "|" rowseparator: "\n"
   stats: off
   width:
filename: db

Вывод в Windows 11:

sqlite3.exe: ошибка синтаксического анализа рядом со строкой 1: рядом с «.»: синтаксическая ошибка At
строка:1 символ:11
+ ".шоу" | & .\sqlite3.exe db
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo: NotSpecified: (Ошибка анализа около...": синтаксическая ошибка:String) [], RemoteException + FullyQualifiedErrorId: NativeCommandError
.show
^--- здесь ошибка

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

Я понимаю, что очевидный вариант — сказать пользователям, чтобы они просто не запускали его в ISE, но мне было интересно, есть ли какой-нибудь способ настроить ISE или переписать сценарий, чтобы избежать ошибки?

@iRon, спасибо. К сожалению, ISE является приложением по умолчанию в Windows 11 для открытия файлов .ps1 (в образе, который все равно использует наш ИТ-специалист), поэтому я бы предпочел, чтобы оно «просто работало», если это возможно.

saviourofdp 02.08.2024 16:27

Я не знаком с внешней командой .\sqlite3.exe, но пробовали ли вы использовать одинарные кавычки и применять ее в качестве аргумента: что-то вроде: & .\sqlite3.exe db '.show'.

iRon 02.08.2024 16:33
& .\sqlite3.exe db '.show' (одинарные или двойные кавычки) работает! Не знаю, почему я вообще сделал это по-трубному. В любом случае, если вы добавите это как ответ, я отмечу это как решение.
saviourofdp 02.08.2024 16:37

Команда отлично работает в стандартном окне PowerShell и командном окне Visual Studio Code, поэтому я думаю, что это ISE.

saviourofdp 02.08.2024 16:43

(Рад видеть, что проблема была решена для вас) «Если вы добавите это как ответ, я отмечу это как решение». Я просто стрелял от бедра, основываясь на некоторых общих передовых методах работы с конвейерами/внешними командами (строками) PowerShell, но у меня нет хорошего понимания фактической причины различного поведения, которое вы определили (как Windows 10/11 и т. д.). Поэтому я оставлю кому-нибудь другому написать адекватный ответ на этот вопрос...

iRon 02.08.2024 16:53
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
84
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

вр; доктор

  • Либо: обойти проблему, передав .show в качестве аргумента (а не через конвейер) в sqlite3.exe, как предлагает iRon (обратите внимание, что я опускаю &, вызывающий оператор, поскольку здесь он не нужен):

    .\sqlite3.exe db .show
    
  • Или: Исправьте проблему с кодировкой символов, в простейшем случае, следующим образом, но учтите, что это работает только для текста, состоящего только из символов ASCII:

    $OutputEncoding = [Console]::OutputEncoding
    '.show' | .\sqlite3.exe db
    
    • Подробности и альтернативу UTF-8 см. ниже.

Действительно, лучше избегать Windows PowerShell ISE (чтобы добавить к полезному комментарию iRon по этому вопросу):

Плохая поддержка вызова внешних программ, таких как sqlite3.exe, является одной из причин избегать этого, и вы столкнулись с одним аспектом:

  • Необъяснимо, что привилегированная переменная $OutputEncoding по умолчанию имеет значение UTF-8 в Windows 11; в Windows 10 это была, что более разумно, активная кодировка системы ANSI, синхронизированная с [Console]::OutputEncoding.

    • В обычной консоли Windows PowerShell по умолчанию используется ASCII(!), что не менее загадочно. В PowerShell 7 по умолчанию используется UTF-8 без спецификации, что, хотя и лучше из-за отсутствия спецификации, но также вызывает недоумение: см. следующий пункт.

    • Учитывая, что окна консоли (а также вкладки Windows Terminal и встроенный терминал Visual Studio Code (я не буду упоминать их подробно ниже), но не эмуляция консоли, встроенная в ISE) по умолчанию используют активную устаревшую кодовую страницу OEM системы, кодировка которой отражено в [Console]::OutputEncoding (который PowerShell использует для декодирования вывода внешней программы), имеет смысл по умолчанию $OutputEncoding использовать ту же кодовую страницу (кодировку).

    • Также необъяснимо то, что ISE по умолчанию использует кодовую страницу ANSI, что приводит к различному поведению по умолчанию между ним и окнами консоли.

      • И это несмотря на то, что внешние программы будут видеть кодовую страницу OEM системы как активную (запустите chcp для проверки), так что любые символы, отличные от ASCII, в выходных данных внешней программы будут неправильно интерпретированы PowerShell в ISE.
    • Что еще хуже, $OutputEncoding в ISE - неуместно - кодировка UTF-8 со спецификацией, что и вызывает вашу проблему - см. ниже.


Поскольку $OutputEncoding по умолчанию представляет собой кодировку UTF-8 со спецификацией в ISE, любые данные, которые вы отправляете через конвейер во внешнюю программу, будут иметь добавленную спецификацию - чего НЕ ожидают большинство внешних программ, включая sqlite3.exe, что заставляет их интерпретировать 3 байты, составляющие спецификацию как часть текста.

Поскольку [Console]::OutputEncoding в ISE является активной кодовой страницей ANSI, и PowerShell использует ее для декодирования вывода stdout и stderr из внешних программ, он неправильно декодирует спецификацию UTF-8, содержащуюся в выводе stderr sqlite3.exe перед словом .show (sqlite3.exe предполагает фиксированная кодировка UTF-8, но не распознает спецификацию): Спецификация UTF-8 состоит из 3 байтов, 0xef, 0xbb, 0xbf, которые неправильно декодируются как строка  (т. е. 3 символа), если активен ANSI. кодовая страница: Windows-1252, например, в американско-английском языке.[1]


Варианты решения:

Прежде чем звонить sqlite3.exe, выполните одно из следующих действий:

  • Совместите $OutputEncoding с [Console]::OutputEncoding, что отправит текст в той же кодировке ANSI, которую ожидают хорошо работающие внешние консольные программы - однако из-за несоответствия между кодировкой ANSI, которую использует ISE, и кодировкой OEM, используемой внешними программами, это решение эффективно только если входной текст, а также выходные данные внешней программы состоят только из символов диапазона ASCII или если целевая программа использует активную кодовую страницу ANSI, как, например, python. (Это фактически восстанавливает поведение Windows 10.)

    # Effective only for all-ASCII-characters input and output text.
    $OutputEncoding = [Console]::OutputEncoding
    
  • Переключите сеанс на последовательное использование UTF-8 без спецификации, что обеспечивает полную поддержку Unicode как на входной, так и на выходной стороне:

    chcp >$null # dummy command that forces allocation of a console
    $OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
    
    • Обратите внимание на необходимость выполнения фиктивной команды, вызывающей консольное приложение (chcp), чтобы гарантировать, что ISE незаметно выделил консоль; без этого присвоение [Console]::InputEncoding или [Console]::OutputEncoding вызовет ошибку.
      (Кроме того, прямое использование chcp 65001 для изменения активной кодовой страницы на UTF-8 не сработает, поскольку .NET кэширует кодировки, хранящиеся в [Console]::InputEncoding и [Console]::OutputEncoding.)

    • См. этот ответ для получения дополнительной информации.


[1] You can verify this as follows: [System.Text.Encoding]::GetEncoding(1252).GetString( [System.Text.UTF8Encoding]::new($true).GetPreamble())

Спасибо, @iRon. Я понял, что упустил еще один проблемный аспект ISE, из-за чего быстрое исправление $OutputEncoding = [Console]::OutputEncoding эффективно только для символов диапазона ASCII: ISE по умолчанию использует ANSI, тогда как все внешние программы по умолчанию используют OEM. Я обновил ответ соответственно.

mklement0 04.08.2024 14:58

@mklement0 отличный ответ, спасибо

saviourofdp 05.08.2024 09:43

Windows 10 22H2 ISE:

$outputencoding

IsSingleByte      : True
BodyName          : iso-8859-1
EncodingName      : Western European (Windows)
HeaderName        : Windows-1252
WebName           : Windows-1252
WindowsCodePage   : 1252
IsBrowserDisplay  : True
IsBrowserSave     : True
IsMailNewsDisplay : True
IsMailNewsSave    : True
EncoderFallback   : System.Text.InternalEncoderBestFitFallback
DecoderFallback   : System.Text.InternalDecoderBestFitFallback
IsReadOnly        : True
CodePage          : 1252

Windows 11 22H2 ИСЕ:

$outputencoding

BodyName          : utf-8
EncodingName      : Unicode (UTF-8)
HeaderName        : utf-8
WebName           : utf-8
WindowsCodePage   : 1200
IsBrowserDisplay  : True
IsBrowserSave     : True
IsMailNewsDisplay : True
IsMailNewsSave    : True
IsSingleByte      : False
EncoderFallback   : System.Text.EncoderReplacementFallback
DecoderFallback   : System.Text.DecoderReplacementFallback
IsReadOnly        : True
CodePage          : 65001

Один из способов установить старую кодировку:

$outputencoding = [text.encoding]::default

'.show' | .\sqlite3.exe file1.sql

        echo: off
         eqp: off
     explain: auto
     headers: off
        mode: list
   nullvalue: ""
      output: stdout
colseparator: "|"
rowseparator: "\n"
       stats: off
       width: 
    filename: file1.sql

Кодировка utf8, похоже, не мешает передаче внешних команд, таких как findstr или find...

'.show' | findstr /l .show

.show


'.show' | find '".show"'

.show


'hi' | findstr ^...hi$  # allow for BOM chars

hi

очень полезно, спасибо

saviourofdp 05.08.2024 09:44

Хорошо иметь подтверждение того, что поведение изменилось между Windows 10 и 11. findstr и find ищите подстроки, чтобы наличие посторонней спецификации не было проблемой; обратите внимание, что он там есть, как показывает следующая команда (при запуске в ISE на W11): ` '.show' | cmd/c 'findstr/l.show >out.txt'; Формат-Hex out.txt`. Любопытно, однако, что PowerShell, похоже, удаляет его при захвате вывода внешней программы.

mklement0 05.08.2024 18:50

Другие вопросы по теме

Похожие вопросы