Как перенести данные в powershell - PullRequest
1 голос
/ 15 ноября 2009

У меня есть файл, который выглядит так:
а, 1
б, 2
с, 3
а, 4
б, 5
с, 6
(... повторить 1000 строк)

Как я могу перенести это в это?
а, б, в
1,2,3
4,5,6

Спасибо

1 Ответ

7 голосов
/ 15 ноября 2009

Вот один грубый отряд из ада, который сделает это:

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:.

...