Вот один грубый отряд из ада, который сделает это:
PS> Get-Content foo.txt |
Foreach -Begin {$names=@();$values=@();$hdr=$false;$OFS=',';
function output { if (!$hdr) {"$names"; $global:hdr=$true}
"$values";
$global:names=@();$global:values=@()}}
-Process {$n,$v = $_ -split ',';
if ($names -contains $n) {output};
$names+=$n; $values+=$v }
-End {output}
a,b,c
1,2,3
4,5,6
Это не то, что я бы назвал элегантным, но должно помочь вам. Это должно копировать / вставлять правильно, как есть. Однако, если вы переформатируете его так, как показано выше, вам понадобится поставить галочки после последнего вьющегося в скриптовых блоках Begin и Process. Для этого сценария требуется PowerShell 2.0, так как он использует новый оператор -split.
В этом подходе интенсивно используется командлет Foreach-Object. Обычно, когда вы используете Foreach-Object (псевдоним Foreach) в конвейере, вы указываете только один скрипт-блок, например:
Get-Process | Foreach {$_.HandleCount}
Это распечатывает количество дескрипторов для каждого процесса. Такое использование Foreach-Object неявно использует блок-скрипта -Process, что означает, что он выполняется один раз для каждого объекта, который он получает из конвейера. А что если мы хотим подвести итоги по всем ручкам для каждого процесса? Не обращая внимания на тот факт, что вы можете просто использовать Measure-Object HandleCount -Sum
, я покажу вам, как Foreach-Object может это сделать. Как видно из исходного решения этой проблемы, Foreach может использовать как блок сценариев Begin, который выполняется один раз для первого объекта в конвейере, так и блок конца End, который выполняется, когда в конвейере больше нет объектов. Вот как можно подсчитать количество дескрипторов с помощью Foreach-Object:
gps | Foreach -Begin {$sum=0} -Process {$sum += $_.HandleCount } -End {$sum}
Возвращаясь к решению проблемы, в блоке скриптов Begin я инициализирую некоторые переменные для хранения массива имен и значений, а также bool ($ hdr), который сообщает мне, был ли выведен заголовок или нет (мы только хочу вывести его один раз). Следующим поразительным занятием является то, что я также объявляю функцию (вывод) в блоке скриптов Begin, которую я вызываю из блоков скриптов Process и End для вывода текущего набора данных, хранящихся в $ names и $ values.
Единственный другой трюк состоит в том, что в блоке сценария Process используется оператор -contains, чтобы увидеть, было ли ранее найдено имя поля текущей строки. Если это так, выведите текущие имена и значения и сбросьте эти массивы на пустые. В противном случае просто сохраните имя и значение в соответствующих массивах, чтобы их можно было сохранить позже.
Кстати, причина, по которой функция вывода должна использовать глобальный спецификатор: для переменных, заключается в том, что PowerShell выполняет подход «копирование при записи», когда вложенная область действия изменяет переменную, определенную вне ее области действия. Однако, когда мы действительно хотим, чтобы это изменение происходило в более высоком объеме, мы должны сообщить PowerShell об этом, используя такой модификатор, как global: или script:.