Почему подробное перенаправление создает линию между данными в экспортированном файле CSV? - PullRequest
2 голосов
/ 29 мая 2019

У меня есть следующий код, который импортирует два CSV-файла и объединяет их вместе.В рамках слияния я удаляю два используемых файла и экспортирую один основной файл CSV.

$csvfiles | ForEach-Object {
    Import-Csv "$csvLocation\$_";
    Remove-Item "$csvLocation\$_" -Verbose 4>&1 | tee .\log.txt -Append
} | Export-Csv -NoTypeInformation "$csvLocation\$DBName.csv" -Force

Сгенерированный CSV выглядит следующим образом с подробным перенаправлением 4>&1:

extra line

Обратите внимание на дополнительную пустую строку между сервером1 и сервером2.

CSV, который я ожидаю, должен быть таким (нет между дополнительной строкой):

server  Database    role    members
server1 DB1 role1   memberx
server1 DB1 role2   membery
server2 DB1 role1   memberx
server2 DB1 role2   membery

Конечно, без этой части

4>&1|tee .\log.txt -append

экспортированный CSV выглядит хорошо.Однако я хочу, чтобы подробные сообщения печатались на консоли и в файле (с 4>&1 | tee .\log.txt -Append, к сожалению, он печатается только в файл).

VERBOSE: Выполняется удаление ...

и работает только с 4>&1.

Я попытался перенаправить на переменную.Я пробовал много вещей, но безрезультатно.


ОБНОВЛЕНИЕ : В отношении второго решения HAL, предложенного ниже.

$csvfiles | ForEach-Object {
    Import-Csv "$csvLocation\$_" | Export-Csv -Append -NoTypeInformation "$csvLocation\$DBName.csv" -Force;
    Remove-Item "$csvLocation\$_" -Verbose 4>&1 | tee .\log.txt -Append
}

вывод, который я обычно вижу на консоли только с -verbose, выглядел так:

VERBOSE: Performing the operation "Remove File" on target
"D:\MyFiles\Scripts\Migration\CSVFiles\Database1_RolesMembers_server1.domain.com.c
sv".
VERBOSE: Performing the operation "Remove File" on target
"D:\MyFiles\Scripts\Migration\CSVFiles\Database1_RolesMembers_server2.domain.com.c
sv".

однако код HAL приводит к этому подробному выводу на консоль:

VERBOSE: Performing the operation "Remove File" on target
"D:\MyFiles\Scripts\Migration\CSVFiles\Datab
VERBOSE: ase1_RolesMembers_server1.domain.com.csv".
VERBOSE: Performing the operation "Remove File" on target
"D:\MyFiles\Scripts\Migration\CSVFiles\Datab
VERBOSE: ase1_RolesMembers_server2.domain.com.csv".

конечно, он незначителен, но в любом случае стоит отметить странный стиль вывода

, поскольку генерируемый файл csv ВСЕ ЕЩЕ приводит к пустой строке, но даже больше:

csv

Ответы [ 2 ]

3 голосов
/ 29 мая 2019

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

Вместо этого переместите перенаправитель слияния во внешний конвейер - в этот момент Export-Csv запишет все в стандартный поток вывода в файл, и у вас останутся только подробные сообщения:

$csvfiles | ForEach-Object {
    Import-Csv "$csvLocation\$_"
    Remove-Item "$csvLocation\$_" -Verbose 
} | Export-Csv -NoTypeInformation "$csvLocation\$DBName.csv" -Force 4>&1 |tee .\log.txt -Append
2 голосов
/ 29 мая 2019

Проблема связана с объектами, потоками и попыткой заставить два разных объекта объединиться в один поток.То, что происходит, проиллюстрировано моим SO-ответом здесь: Запуск строки за строкой дает странный результат по сравнению с бегущими строками в виде одной строки с точками с запятой

TLDR: в основном происходит то, что вы пытаетесьчтобы передать 2 разных объекта по одному и тому же конвейеру (см. about_Redirection ), и PowerShell выполняет интерпретацию «из лучших сил», которая представляет собой «bleahh -> blank» интерпретацию, когда она достигает Export-CSV.Это также объясняет, почему вывод на консоль не отображается.

Лучший способ интерпретировать происходящее по каналу - разбить его и присвоить переменным, чтобы увидеть, что происходит:

Развертывание цикла, Первая команда и тип:

PS C:\> $csv = Import-Csv a.csv
PS C:\> $csv.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

Вторая команда:

PS C:\> $ri = Remove-Item a.csv -Verbose 4>&1 | tee .\log.txt -Append
PS C:\> $ri.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    VerboseRecord                            System.Management.Automation.InformationalRecord

В основном происходит следующее: Первая команда:

Import-CSV ...

Вывести массив Object[] в поток информации (1).

Вторая команда:

Remove-Item -Verbose

Вывод в поток Verbose (4).

Третья команда, перенаправление: 4> & 1 ...

Выводит содержимое подробного потока (4) в поток информации (1).

Четвертая команда,

... | tee .\log.txt -Append 

Принимает перенаправленный вывод Remove-Item для информации (1).) и выводит в оба потока информации (1) и файла журнала.

Когда мы объединяем их внутри цикла ForEach-Object, мы создаеммассив конкурирующих объектов в потоке информации (1) :

PS C:\> 1..2|foreach{$csv; $ri}

server  Database role  members
------  -------- ----  -------
server1 DB1      role1 memberx
server1 DB1      role2 membery
VERBOSE: Performing the operation "Remove File" on target "C:\a.csv".
server1 DB1      role1 memberx
server1 DB1      role2 membery
VERBOSE: Performing the operation "Remove File" on target "C:\a.csv".

Затем эти конкурирующие объекты передаются по конвейеру следующей команде Export-Csv:

 1..2 | foreach{$csv,$ri} | Export-Csv .\out.csv -NoTypeInformation

Это дает нам «сырой» CSV:

"server","Database","role","members"
"server1","DB1","role1","memberx"
"server1","DB1","role2","membery"
,,,
"server1","DB1","role1","memberx"
"server1","DB1","role2","membery"
,,,

, где мы можем ясно видеть, как 4 объекта передавались по конвейеру.Export-Csv получает 4 объекта:

  1. Object[] массив
  2. VerboseRecord объект (перенаправленный вывод Remove-Item -Verbose)
  3. Второй Object[]массив
  4. второй VerboseRecord объект

Именно здесь PowerShell возьмет первый Object[] объект массива для построения структуры файла Export-Csv.Затем он возьмет объект VerboseRecord и, поскольку у него нет способа «вписать» его в CSV, он выводит пустую запись.Затем он берет второй Object[] и, поскольку он «подходит», выведет его в CSV.Затем он возьмет второй VerboseRecord объект и, опять же, поскольку у него нет способа «вписать» его в CSV, он выводит пустую запись.

Обратите внимание, что все 4 объектав CSV, даже если PowerShell не знал, что с ними делать.Вот почему вы не видите вывод на консоли PowerShell.Содержимое потока информации (1) полностью направляется вниз по конвейеру для использования командлетом Export-Csv, а , а не консольным хостом. Именно по этой вы не «видите» вывод -Verbose команды Remove-Item на консоли, но видите его в файле.

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

Код можно исправить двумя способами: если вам нужны конвейеры, разделите две операции на ... ну ... две операции, чтобы предотвратить повреждениеконвейера:

$csvfiles | ForEach-Object {
    Import-Csv "$csvLocation\$_";
} | Export-Csv -NoTypeInformation "$csvLocation\$DBName.csv" -Force

$csvfiles | ForEach-Object {
    Remove-Item "$csvLocation\$_" -Verbose 4>&1 | tee .\log.txt -Append
}

Или, ИМХО, не переводите ForEach в конвейер и ожидайте, что все будет работать, поскольку перед передачей вещи могут быть накоплены в памяти.Исключите конечного потребителя конвейера, вставьте Export-Csv в цикл ForEach-Object и добавьте в него свой контент.

Редактирование: не забудьте сначала начать с пустого файла CSV: -)

Remove-Item "$csvLocation\$DBName.csv" -ErrorAction SilentlyContinue

$csvfiles | ForEach-Object {
    Import-Csv "$csvLocation\$_" | Export-Csv -Append -NoTypeInformation "$csvLocation\$DBName.csv" -Force;
    Remove-Item "$csvLocation\$_" -Verbose 4>&1 | tee .\log.txt -Append
} 
...