{ "index": 335, "slug": "editorial-2018-09-mechanism-windows-dev-env", "title": "Windows: почему одна команда запускает не тот бинарник", "excerpt": "PowerShell выбирает команду из нескольких слоёв: профиля, alias, функций, PATH и PATHEXT. Разбираем, как увидеть фактический выбор, отделить кодировку консоли и проверить гипотезу без глобальной правки системы.", "contentHtml": "
Симптом: в одной консоли node --version показывает ожидаемую версию, а в другой — старую. Иногда команда отрабатывает, но русское сообщение превращается в нечитаемые символы. Цена ошибки — не только одна неудачная сборка. Можно исправить не тот node.exe, повредить текстовый файл или навсегда засорить системный PATH ради разовой проверки.
Имя команды не равно конкретному файлу. PowerShell учитывает команды текущей сессии, затем ищет приложения через переменные среды. Кодировка вывода живёт рядом, но не управляет выбором бинарника. Эти слои надо проверять раздельно.
\nПервый вопрос диагностики: какой объект получил это имя в текущем процессе? Им может оказаться alias, функция, cmdlet, скрипт или приложение. Только последний вариант связывает команду с файлом на диске.
\nPATH задаёт каталоги для поиска исполняемых файлов. В Windows каталоги разделяет точка с запятой. PATHEXT перечисляет расширения, которые оболочка считает исполняемыми. Поэтому команда без расширения может привести к tool.exe или tool.cmd. До поиска в PATH PowerShell может выбрать функцию или alias с тем же именем.
Каждый процесс получает собственный набор переменных среды. Дочерний процесс наследует копию от родителя. Если в PowerShell выполнить $env:Path = ..., изменится текущая сессия и программы, запущенные из неё. Системные настройки от этого не меняются.
Изменение в окне настроек Windows не обновляет уже открытый терминал. Новая консоль получит новое значение, старая продолжит работать со старым. Поэтому после правки нужно открыть новый процесс. Иначе проверка сравнивает ожидание с устаревшим снимком.
\nGet-Command -Name node -All показывает все найденные команды в порядке приоритета PowerShell. Колонка CommandType отделяет приложение от функции, alias, cmdlet и скрипта. Первый элемент — кандидат, который оболочка выберет при обычном вводе имени.
where.exe node ищет файлы в текущем каталоге и каталогах из PATH. Он полезен для инвентаризации физических файлов, но не видит функцию из профиля PowerShell и не описывает полный приоритет оболочки. Поэтому одна команда не заменяет другую.
$names = @('node', 'npm', 'php', 'git')\nforeach ($name in $names) {\n Write-Host ('== ' + $name + ' ==')\n Get-Command -Name $name -All -ErrorAction SilentlyContinue |\n Select-Object CommandType, Name, Version, Source, Definition |\n Format-Table -AutoSize\n where.exe $name 2>$null\n if (Get-Command -Name $name -ErrorAction SilentlyContinue) {\n & $name --version\n Write-Host ('exitCode=' + $LASTEXITCODE)\n }\n}\nУчебный пример показывает способ наблюдения, а не production-результат. В реальном проекте список должен соответствовать README и lock-файлам. Аргумент --version подходит не каждой утилите. Для такой команды укажите документированный аргумент.
Безопасная гипотеза звучит так: проект запускает старый файл, потому что он раньше нужного каталога в PATH. Её проверяют в текущем процессе, не меняя системные настройки.
$requiredTools = 'C:\\Tools\\node-20'\nif (-not (Test-Path (Join-Path $requiredTools 'node.exe'))) {\n throw ('Нет node.exe: ' + $requiredTools)\n}\n$env:Path = $requiredTools + ';' + $env:Path\nGet-Command node -All | Select-Object CommandType, Definition, Version\nnode --version\n# Затем повторяется исходная команда проекта.\nПуть в примере условный. Его нельзя копировать без требования проекта и проверки файла. Команды меняют только текущий PowerShell. Если версия и исходная ошибка не изменились, гипотеза не подтверждена. Окно можно закрыть без отката постоянных настроек.
\nchcp показывает активную кодовую страницу консоли. [Console]::OutputEncoding.WebName показывает настройку вывода .NET. Кодировка сохранённого файла — третья сущность. Она может не совпадать ни с одной из двух.
Нечитаемый текст только в одной консоли не доказывает неправильный бинарник. Сначала зафиксируйте кодовую страницу, настройку вывода и байты конкретного файла. Не переключайте системный язык и не добавляйте UTF-8 в глобальные настройки до проверки. Старое приложение может ожидать другую кодировку.
\n& cmd.exe /d /c chcp\n[Console]::OutputEncoding.WebName\n[Text.Encoding]::Default.WebName\n$env:Path = 'C:\\Tools\\node-20;' + $env:Path\nGet-Command node -All\nnode --version\nЭти команды дают снимок процесса и консоли. Они не исправляют кодировку файла и не доказывают совместимость всей сборки. Если проблема относится к файлу, откройте его байты или настройку конкретной программы. Если к выводу — повторите запуск в новом процессе.
\n| Симптом | Причина | Проверка | Действие |
|---|---|---|---|
node --version показывает старую версию | Другой кандидат стоит раньше в PATH | Get-Command node -All и where.exe node | Временно поставить документированный каталог первым |
where.exe показывает файлы, но запускается функция | Профиль перекрывает приложение | CommandType и запуск с -NoProfile | Проверить профиль, не менять PATH наугад |
| Русский вывод нечитаем только в одной консоли | Различается кодовая страница или OutputEncoding | chcp и свойства Console | Сравнить новый процесс и кодировку файла |
| После правки результат прежний | Работает старый процесс | Новая сессия без профиля | Повторить исходную команду в новом окне |
| Версия совпала, но сборка падает | Причина в lock-файле, правах, DLL или сети | Полный лог следующей ошибки | Прекратить правку PATH |
Get-Command -All, where.exe и команду версии.-NoProfile и повторите наблюдения.$env:Path текущего процесса.chcp, OutputEncoding и кодировку файла.Совпадение команды, пути и версии не делает машины одинаковыми. На результат влияют разрядность, DLL, права, антивирус, прокси, кеш, локальные файлы и политика выполнения. Не отключайте защиту и не переустанавливайте Windows, пока отдельное наблюдение не укажет на такую причину.
\nchcp 65001 не является универсальным решением. Старые программы могут читать ввод и писать вывод по собственным правилам. Если смена кодовой страницы не меняет симптом, вернитесь к байтам файла и API чтения. Если временный PATH не меняет сборку, перестаньте редактировать PATH.
Диагностика завершена, когда можно показать, какой объект выбран, какой файл напечатал проверенную версию и какая кодировка относится к проблемному тексту. После исправления новый PowerShell повторяет исходную команду с тем же commit, получает требуемую версию и не возвращает исходную ошибку. Если совпала только версия, готовность не достигнута.
\n