Why is verbose redirection creating a line between data in exported CSV file?

不羁岁月 提交于 2019-12-24 00:58:53

问题


I have the following code that imports two csv files and merges them together. As part of the merge I remove the two files used and export one main CSV file.

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

The CSV generated looks like this with the 4>&1 verbose redirection:

Notice the extra empty in between row between server1 and server2.

The CSV I'm expecting should be like this (no in between extra row):

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

Of course without this part

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

the exported CSV looks good. However, I want the verbose messages printed to the console and file (with 4>&1 | tee .\log.txt -Append it only seems to print to file unfortunately).

VERBOSE: Performing remove...

and the only way it works is with 4>&1.

I tried redirecting to a variable. I tried many things but to no avail.


UPDATE: In regards to the second solution HAL offered below.

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

the output i'd normally see on the console with just -verbose looked like this:

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

however, HAL's code results in this verbose console output:

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

of course its insignificant but its worth noting the strange output style anyways

as far as the csv file generated, it STILL results in empty line, but even more:


回答1:


The issue is all about objects, streams and trying to force two different objects into the same stream. What is happening is illustrated with my SO answer here: Running Line by Line Produces Odd Result Compared to Running Lines as a Single Line with Semicolons

TLDR: Basically what is happening is you are trying to pass 2 different objects down the same pipeline (see about_Redirection), and PowerShell does a "best effort" interpretation which is a "bleahh -> blank" interpretation when it gets to Export-CSV. It also explains why no output to the console is displayed.

The best way to interpret what is going down the pipe is to break it up and assign to variables to see what is going on:

Unrolling the loop, the First command, and type is:

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

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

Second command is:

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

Basically what is happening is, The first command:

Import-CSV ...

Output an Object[] array to the Information (1) stream.

The second command:

Remove-Item -Verbose

Outputs to the Verbose (4) stream.

The third command, the redirect: 4>&1 ...

Outputs the Verbose (4) stream content to the Information (1) stream.

The fourth command,

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

Takes the Remove-Item redirected output on the Information (1) stream, and outputs to both the Information (1) stream and the log file.

When we combine them inside the ForEach-Object loop, we are creating an array of competing objects on the Information (1) stream:

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

These competing objects then get passed down the pipeline to the next Export-Csv command:

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

This gets us a "Raw" CSV:

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

Where we can clearly see the 4 objects were being passed down the pipeline. The Export-Csv gets 4 objects:

  1. Object[] array
  2. VerboseRecord object (the Remove-Item -Verbose redirected output)
  3. The second Object[] array
  4. The second VerboseRecord object

This is where PowerShell will take the first Object[] array object to build the structure of the Export-Csv file. It will then take the VerboseRecord object, and since it doesn't have a way to "fit" it into the CSV, it outputs an empty record. It then takes the second Object[], and since it "fits", will output it to the CSV. It will then take the second VerboseRecord object, and, again, since it doesn't have a way to "fit" it into the CSV, it outputs an empty record.

Notice that all 4 objects are in the CSV, even if PowerShell didn't know what to do with them. This is why you see no output on the PowerShell console. The Information (1) stream content is fully being directed down the pipeline to be consumed by the Export-Csv cmdlet, and not the console Host. This is why you don't "see" the -Verbose output from the Remove-Item command on the console, but you do see it in the file.

TLDR2: Lesson 1: Don't output multiple things to the pipeline if you have something that consumes it in the end. With pipelines, only try to work with one thing at a time.

The code can be fixed 2 ways: If you need pipelines, split the two operations into ... well... two operations to prevent corruption of the pipeline:

$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
}

Or, IMHO, don't ForEach into a pipeline and expect everything to work, as things may have to be accumulated into memory before being passed on. Eliminate the end pipeline consumer and pull the Export-Csv into the ForEach-Object loop, and append your content into it.

Edit: Don't forget to start off with an empty CSV File first :-)

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
} 



回答2:


Well, you merge verbose records into the standard stream, none of which have server, Database, role or members properties, so each verbose message will result in an empty row after the records of each csv file.

Move the merge redirector to the outer pipeline instead - at that point Export-Csv will have written everything in the standard output stream to file, and you'll have only the verbose messages left:

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


来源:https://stackoverflow.com/questions/56364249/why-is-verbose-redirection-creating-a-line-between-data-in-exported-csv-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!