Как я могу получить `Предложение` команды GetShell для PowerShell в C #? - PullRequest
0 голосов
/ 24 октября 2018

Если я создаю файл с именем «dir.exe» и запускаю команду PowerShell Get-Command dir -Type Application, я получаю сообщение об ошибке, так как dir не является приложением (хотя этот файл существует):

gcm : The term 'dir' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the
spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:2
+ (gcm dir -Type Application)
+  ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (dir:String) [Get-Command], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException,Microsoft.PowerShell.Commands.GetCommandCommand


Suggestion [3,General]: The command dir was not found, but does exist in the current location. Windows PowerShell does not load commands from the current location by default. If you trust this command, instead type: ".\dir". See "get-help about_Command_Precedence" for more details.

Обратите внимание на Suggestion внизу: Suggestion [3,General]: The command dir was not found, but does exist in the current location. Windows PowerShell does not load commands from the current location by default. If you trust this command, instead type: ".\dir". See "get-help about_Command_Precedence" for more details.

Я пытаюсь уловить это предложение в моем коде C #:

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management.Automation;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Helpers.Tests {
    [TestClass]
    public class PowerShellRunner_Tests {
        [TestMethod]
        public void GetCommand_Test() {
            // create file called "dir.exe" to see how PowerShell handles
            // "Get-Command dir -Type Application":
            File.Create("dir.exe").Dispose();               

            using (PowerShell powerShell = PowerShell.Create()) {
                powerShell.AddCommand("get-command")
                    .AddArgument("dir")
                    .AddParameter("Type", CommandTypes.Application.ToString());

                // run "Get-Command dir -Type Application":
                CommandInfo commandInfo = powerShell.Invoke<CommandInfo>().FirstOrDefault();

                // get the error:
                ErrorRecord error = powerShell.Streams.Error.FirstOrDefault();

                // emit the "Suggestion":
                Trace.WriteLine(error.ErrorDetails.RecommendedAction);
            }
        }
    }
}

Однако error.ErrorDetails равно null.Как я могу получить это Suggestion?

(я пытаюсь получить поведение where.exe, но без хлопот запуска всего процесса для этого).

1 Ответ

0 голосов
/ 24 октября 2018

Учитывая, что конечной целью является эмулировать поведение where.exe , попробуйте следующее:

(Get-Command -Type Application .\dir, dir -ErrorAction Ignore).Path

Обратите внимание на использование -Type Application для ограничения результатовдля исполняемых файлов и исключить внутренние команды PowerShell, такие как function и aliases.

Это будет выглядеть в текущем каталоге first , как where.exe.Дайте простое имя , такое как dir, Get-Command не смотрит в текущий каталог, потому что PowerShell не позволяет вызывать исполняемые файлы, расположенные в текущем каталоге, по только имя -по соображениям безопасности;использование относительного пути .\, однако, заставляет Get-Command найти такой исполняемый файл.

Начиная с cmd.exe, однако - чье поведение предполагает where.exe - вызывая только текущий каталог dir.exe с помощью всего лишьdir (только по имени) работает нормально.

Если на выходе есть просто один путь, и этот путь является файлом в текущем каталоге , вы можетесделать вывод, что исполняемый файл dir существует только в текущем каталоге, что является условием, при котором PowerShell выдает предложение использовать явный путь при вызове.

$fullPaths = (Get-Command -Type Application .\dir, dir -ErrorAction Ignore).Path
$emitSuggestion = $fullPaths.Count -eq 1 -and 
                  (Test-Path ('.\' + (Split-Path -Leaf $fullPaths[0]))

Примечание: Строго говоря, вы также должны исключить случай, когда текущий каталог будет таким, который указан в $env:PATH:
$env:PATH -split ';' -ne '' -notcontains (Split-Path -Parent $fullPaths[0])

Вы можете сообщитьэто в ваш код C # путем записи пользовательской версии предложения в поток ошибок через Write-Error или, предпочтительно, в поток warning , с Write-Warning.


Чтобы использовать вышеуказанные команды через PowerShell SDK, проще всегоиспользуйте метод .AddScript();Например:

powerShell.AddScript("(Get-Command -Type Application .\dir, dir -ErrorAction Ignore).Path");

Что касается захвата или глушения предложений PowerShell:

К сожалению, вы не можете получить доступ к предложениям программно (написано как для Windows PowerShell v5.1 / PowerShell Core 6.1.0):

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

Только консольный хост , используемый в окнах консоли (терминала), генерирует предложения, но даже там предложения выводятся на печать непосредственно на экран , минуя систему потоков вывода PowerShell.

Вкратце: Предложения отображаются только в консольных окнах (терминалы) и могут быть только просмотрено , не захвачено там.


Быстрая демонстрация поведения подсказок в окне консоли (предполагается Windows, с файлом с именем dir.exe в текущемреж, а также не в $env:PATH):

PS> & { try { Get-Command dir.exe } catch {} } *>$null

Suggestion [3,General]: The command dir.exe was not found, but does exist in the current location. Windows PowerShell does not load commands from the current location by default. If you trust this command, instead type: ".\dir.exe". See "get-help about_Command_Precedence" for more details.

Как вы можете видеть, несмотря на попытку подавить вывод all (*>$null), предложение все еще выводится на экран, что также означает, что вы не можете захват предложений.

Однако там есть способ тишины предложений, а именно с -ErrorAction Ignore(PSv3 +);напротив, с -ErrorAction SilentlyContinue предложение все еще печатает (!):

PS> & { try { Get-Command dir.exe -ErrorAction Ignore } catch {} } *>$null
# no output
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...