Разработка командлетов PowerShell: передовой опыт в отношении IEnumerable, передаваемый между командлетами - PullRequest
0 голосов
/ 10 января 2020

Допустим, у вас есть два командлета: первый возвращает выходные данные, а второй использует этот вывод в качестве входного параметра.

public class Cmdlet1 : PSCmdlet
{
    protected override void ProcessRecord()
    {
        List<string> result = ...
        WriteObject(result, true);
    }
}

public class Cmdlet2 : PSCmdlet
{
    [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true)]
    public string Input { get; set; }

    protected override void ProcessRecord()
    {
        // Do a long operation on Input.
    }
}

Cmdlet2 будет вызываться несколько раз таким образом (один раз для каждого элемента в IEnumerable, который возвращает Cmdlet1), и вы можете красиво соединить их вместе: Cmdlet1 | Select-Object -First 10 | Cmdlet2 Только первые 10 элементов вывода Cmdlet1 отправляются на Cmdlet2.

Поскольку Cmdlet2 занимает некоторое время, было бы неплохо отобразить прогресс. В настоящее время мы не можем этого сделать, потому что мы получаем строки ввода только одну за другой. Давайте изменим вход Cmdlet2 на List

public class Cmdlet1 : PSCmdlet
{
    protected override void ProcessRecord()
    {
        List<string> result = ...
        WriteObject(result, true);
    }
}

public class Cmdlet2 : PSCmdlet
{
    [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true)]
    public List<string> Input { get; set; }

    protected override void ProcessRecord()
    {
        // Do a long operation on Input.
    }
}

Мы по-прежнему получаем входные строки одну за другой, потому что вызов WriteObject в Cmdlet1 имеет true в качестве второго параметра, что позволяет PowerShell перечислять результат отдельным объектам , Если мы изменим это на false, PowerShell не будет перечислять результат, и Cmdlet2 получит весь список строк в качестве входных данных, и мы сможем работать со всеми строками и отображать хороший индикатор выполнения.

public class Cmdlet1 : PSCmdlet
{
    protected override void ProcessRecord()
    {
        List<string> result = ...
        WriteObject(result, false);
    }
}

public class Cmdlet2 : PSCmdlet
{
    [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true)]
    public List<string> Input { get; set; }

    protected override void ProcessRecord()
    {
        // Do a long operation on Input, displaying a progress bar.
    }
}

НО: сейчас мы не можем легко их больше трубить. Cmdlet1 | Select-Object -First 10 | Cmdlet2 теперь отправляет весь ввод в Cmdlet2! Select-Object не берет первые 10, потому что есть только 1 элемент, который является IEnumerable, и этот элемент полностью отправляется в Cmdlet2!

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

1 Ответ

0 голосов
/ 10 января 2020

Переместите вашу длительную операцию в блок EndProcessing, используйте ProcessRecord для агрегирования ввода:

public class Cmdlet2 : PSCmdlet
{
    [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true)]
    public string[] InputObject { get; set; }

    private List<string> inputList = new List<string>();

    protected override void ProcessRecord()
    {
        inputList.AddRange(InputObject);
    }

    protected override void EndProcessing()
    {
        for(int i = 0; i < inputList.Count; i++)
        {
            this.WriteProgress(new ProgressRecord(1, "Doing long running operation", $"Currently processing item {i+1} / {inputList.Count}"));
            DoLongRunningOperation(inputList[i]);
        }
    }
}
...