Output to Pipeline from within IF ELSE statement - powershell

UPDATE: I just found out that the client I am running this on is PS V1. I can't use splatting.
I have a script for processing csv files. I don't know ahead of time if the file will have a header or not so I'm prompting the user to either input a header or use the one in the file:
$header = Read-Host 'Input your own header?'
What I want to do is be able to check whether the header variable has been set to decide if I want to use that flag when executing the Import-CSV commandlet.
I have tried a number of things along the lines of:
IF($Header){Import-Csv $File -delimiter $delimiter -Header $header }
ELSE
{Import-Csv $File -delimiter $delimiter} |
Or
IF($Header){Import-Csv $File -delimiter $delimiter -Header $header | %{$_} }
ELSE
{Import-Csv $File -delimiter $delimiter | %{$_}}
The first example results in complaints of an empty pipeline. The second results in the first column of the file just being ouptut to the console and then errors when the file is done processing because the rest of the pipeline is empty.
As always any help would be appreciated.
Thanks,
Bill

Eris already suggested splatting, but I wanted to give a more comprehensive example, using your code.
# Declare a hash containing the parameters you will always need
$csvParams = #{
File = $File;
delimiter = $delimiter;
}
# if the header is specified, add a Header to $csvParams
if ($Header) { $csvParams.Header = $header }
# Call Import-Csv, splatting $csvParams
Import-Csv #csvParams
Splatting is an extremely useful technique. Get-Help about_Splatting for more information about it.

The easiest way is just to use the -OutVariable option on Import-Csv
Import-Csv -Path:$File -Delimiter:$Delimiter -OutVariable:CSVContents will save it in $CSVContents
From the Import-CSV Technet page:
This cmdlet supports the common parameters: -Verbose, -Debug, -ErrorAction, -ErrorVariable, -OutBuffer, and -OutVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/p/?LinkID=113216).
Another alternative is to use an args hash and "splat" it:
$myArgs = #{
Path = "$HOME\temp\foo"
}
Get-Content #myArgs
Update for Version 1 (untested):
( IF($Header) { Import-Csv $File -delimiter $delimiter -Header $header }
ELSE { Import-Csv $File -delimiter $delimiter} ) |
#More pipeline commands here, example:
Format-Table
Horrible disgusting version (untested):
$ImportCommand = "Import-Csv $File -delimiter $delimiter"
If($header -ne $null) { $ImportCommand += " -header $header" }
Invoke-Expression $ImportCommand |
Format-Table

You second approach is the way I would go. I'm not sure why, what you have shown for your second example, would be failing. Here is what I would do:
filter ProcessCsv {
$_ | ... further processing ...
}
$header = Read-Host 'Input your own header?'
if ($header) {
Import-Csv $file -Delimiter $delimiter -Header $header | ProcessCsv
else {
Import-Csv $file -Delimiter $delimiter | ProcessCsv
}

Related

PowerShell string replacement with csv as search and replace pattern for a separate file

I'm trying to find a way to reliably replace all occurrences of a string found in a file with data from a column in a CSV using one column as the search pattern with data from the same row on the next column for the replace pattern. The new data is then written to a new file as to keep the original intact. The purpose of this is to simplify exchanging IDs between environments that are hardcoded into the Master pages of a SharePoint site collection. Here's what I have so far.
$file = "C:\Users\jeffery\Documents\ids.csv"
$csv = Import-Csv -Path $file -Delimiter `,
$prd2016 = $csv.'2016 PRD ID'
$stg2016 = $csv.'2016 STG ID'
$prd2010 = $csv.'2010 PRD ID'
$srcFile = "C:\Users\jeffery\Downloads\v5.master"
$dstFile = "C:\Users\jeffery\Downloads\v6.master"
Set-Variable 2010,2016
$content = Get-Content -Path $srcFile
For($i=0; $i -lt $prd2016.Count; $i++){
Clear-Variable 2010
Clear-Variable 2016
$2010 = $prd2010[$i]
$2016 = $prd2016[$i]
$content.replace("$2016", "$2010") | Set-Content -Path $dstFile -Force
}
I've also tried nested loops and using foreach loops to no avail as of yet. Any help will be greatly appreciated. Also, here's some sample data to assist with any answers.
CSV Data:
Navigation,a5a0c64c-17b1-4cba-a8ff-a6a61d8466f3,a66d1d48-ab5e-4aed-9eb9-e8763b88ff2a,2d3cd026-7e2a-4241-8500-abd9a83a0803
Source file data:
<WebPartPages:DataFormWebPart runat="server" IsIncluded="True" AsyncRefresh="false" NoDefaultStyle="TRUE" ViewFlag="8" Title="Navigation" PageType="PAGE_NORMALVIEW" __markuptype="vsattributemarkup" __WebPartId="{9CDA54AA-5C9F-4E62-A0D6-BE149C8B27F0}" partorder="2" id="g_9cda54aa_5c9f_4e62_a0d6_be149c8b27f0" listname="{a5a0c64c-17b1-4cba-a8ff-a6a61d8466f3}" pagesize="1" chrometype="None" __AllowXSLTEditing="true" WebPart="true" Height="" Width="">
<DataSources><SharePoint:SPDataSource runat="server" DataSourceMode="List" UseInternalName="true" UseServerDataFormat="true" selectcommand="<View><Query><Where><Eq><FieldRef Name="Title"/><Value Type="Text">Top Nav</Value></Eq></Where></Query></View>" id="dataformwebpart8"><SelectParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="{a5a0c64c-17b1-4cba-a8ff-a6a61d8466f3}"/><asp:Parameter Name="MaximumRows" DefaultValue="1"/></SelectParameters><DeleteParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="{a5a0c64c-17b1-4cba-a8ff-a6a61d8466f3}"/></DeleteParameters><UpdateParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="{a5a0c64c-17b1-4cba-a8ff-a6a61d8466f3}"/></UpdateParameters><InsertParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="{a5a0c64c-17b1-4cba-a8ff-a6a61d8466f3}"/></InsertParameters></SharePoint:SPDataSource></DataSources>
<datafields>#Title,Title;#Navigation,Navigation;#ID,ID;#ContentType,Content Type;#Modified,Modified;#Created,Created;#Author,Created By;#Editor,Modified By;#_UIVersionString,Version;#Attachments,Attachments;#File_x0020_Type,File Type;#FileLeafRef,Name (for use in forms);#FileDirRef,Path;#FSObjType,Item Type;#_HasCopyDestinations,Has Copy Destinations;#_CopySource,Copy Source;#ContentTypeId,Content Type ID;#_ModerationStatus,Approval Status;#_UIVersion,UI Version;#Created_x0020_Date,Created;#FileRef,URL Path;#ItemChildCount,Item Child Count;#FolderChildCount,Folder Child Count;#AppAuthor,App Created By;#AppEditor,App Modified By;</datafields>
<XSL><xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal">
Thank you in advance for any and all help with this query.
You're jumping through some serious hoops to avoid using the objects Import-Csv gives you and foreach.
You're also overwriting the destination file every time you loop, which is unnecessary.
$srcFile = "C:\Users\jeffery.grantham\Downloads\v5.master"
$dstFile = "C:\Users\jeffery.grantham\Downloads\v6.master"
$file = "C:\Users\jeffery\Documents\ids.csv"
$csv = Import-Csv -Path $file
$content = Get-Content -Path $srcFile -Raw # Read the file into a single string.
foreach ($row in $csv) {
$content = $content.Replace($row.'2016 PRD ID', $row.'2010 PRD ID')
}
$content | Set-Content -Path $dstFile -Force
I found my own answer finally. The problem was that the string was getting replaced in realtime but it was not updating the variable prior to trying to write the result to the $dstFile.
So my final script looks like the following:
$file = "C:\Users\jeffery.grantham\Documents\peer-ids.csv"
$csv = Import-Csv -Path $file -Delimiter `,
$prd2016 = $csv.'2016 PRD ID'
$stg2016 = $csv.'2016 STG ID'
$prd2010 = $csv.'2010 PRD ID'
$srcFile = "C:\Users\jeffery.grantham\Downloads\v5.master"
$dstFile = "C:\Users\jeffery.grantham\Downloads\v6.master"
Set-Variable 2010,2016
$content = Get-Content -Path $srcFile
For($i=0; $i -lt $prd2016.Count; $i++){
Clear-Variable 2010
Clear-Variable 2016
$2010 = [string]$prd2010[$i]
$2016 = [string]$prd2016[$i]
$content = $content.replace("$2016", "$2010")
$content | Set-Content -Path $dstFile -Force
}
I hate when I find my own answer less than an hour after posting a question, but hopefully, finding this will help someone else in the future attempt to do the same thing or something similar.

File data not displaying in table form using Powershell Script

I have been grabbing the file data from student_data.dat and trying to display it into tabular form.
The first 3 lines of the file are written like this:
Jamie Zawinski,78.8,81.0,77.3,80.0,80.0,77.0
Adam Douglas,86.2,69.0,77.8,81.0,87.5,88.0
Wallace Steven,66.2,68.0,91.3,78.6,80.3,86.4
I wish to set it up into a table with headers of Student Name, Assignment-1, Assignment-2, etc. I will later be manipulating the data to calculate the class averages for each assignment and the students overall average in the course so far. Each method of setting up the table results in the file being displayed as:
#{Name=Jamie Zawinski; Assignment-1=78.8; Assignment-2=81.0; Assignment-3=77.3; Assignment-4=80.0;
Midterm_Exam=80.0; Final_Exam=77.0} #{Name=Adam Douglas; Assignment-1=86.2; Assignment-2=69.0;
Assignment-3=77.8; Assignment-4=81.0; Midterm_Exam=87.5; Final_Exam=88.0} #{Name=Wallace Steven;
Assignment-1=66.2; Assignment-2=68.0; Assignment-3=91.3; Assignment-4=78.6; Midterm_Exam=80.3;
Final_Exam=86.4}
My coding has looked like this:
$file = Import-Csv C:\**real path**\student_data.dat -Delimiter ',' -Header 'Name', 'Assignment-1','Assignment-2','Assignment-3','Assignment-4','Midterm_Exam','Final_Exam'
Write-Host $file
I tried adding:
foreach ($line in $file){
$data += [pscustomobject]#{
Name = $line.name
Assignment-1 = $line.Assignment-1
Assignment-2 = $line.Assignment-2
}
}
and writing instead:
$filedata = Get-Content ./student_data.dat
$newline = $filedata.Split("`n")
$newline.count
foreach ($l in $newline){
$Names = $l.Split(",")[0].Trim()
$Assignment-1 = $l.Split(",").Trim()
[pscustomObject]#{
Names = $Names;
Assignment-1 = $Assignment-1
}
}
but errors occurred.
First of all, note that you get a more readable output if you use Write-Output instead of Write-Host, which tries to push the entire input into a single string. (Or just remove it altogether, $file is equivalent to Write-Output $file)
As Matthias R. Jessen commented, you are probably looking for the Format-Table command:
$path = "C:\**real path**\student_data.dat"
$header = 'Name', 'Assignment-1','Assignment-2','Assignment-3','Assignment-4','Midterm_Exam','Final_Exam'
$data = Import-Csv $path -Delimiter ',' -Header $header
$data | Format-Table
Note this just "pretty-prints" the data for display in the console. The data is not changed and you should not use this for any kind of file output. And it's not really necessary for working with the data. Once you're done manipulating your data, you can just export it back as CSV:
$outpath = "C:\**real path**\student_data_result.csv"
$data | Export-Csv $outpath -Delimiter "," -NoTypeInformation

Powershell Import-csv with return character

I tried the following to turn a text file into a document by leveraging import-csv where each item in the original document was a new line
Sample file.txt
James Cameron
Kirk Cobain
Linda Johnson
Code:
$array = import-csv file.txt | ConvertFrom-Csv -Delim `r
foreach ($Data in $array)
{
if (sls $Data Master.txt -quiet)
{Add-Content file.txt $Data}
}
It never created the document
Import-Csv takes a CSV and outputs PSCustomObjects. It's intended for when the file has a header row, and it reads that as the properties of the objects. e.g.
FirstName,LastName
James,Cameron
Kirk,Cobain
# ->
#{FirstName='James';LastName='Cameron'}
#{FirstName='Kirk';LastName='Cobain'}
etc.
If your file has no header row, it will take the first row and then ruin everything else afterwards. You need to provide the -Header 'h1','h2',... parameter to fix that. So you could use -Header Name, but your data only has one property, so there's not much benefit.
ConvertFrom-Csv is intended to do the same thing, but from CSV data in a variable instead of a file. They don't chain together usefully. It will try, but what you end up with is...
A single object, with a property called '#{James=Kirk}' and a value of '#{James=Linda}', where 'James' was taken from line 1 as a column header, and the weird syntax is from forcing those objects through a second conversion.
It's not at all clear why you are reading in from file.txt and adding to file.txt. But since you don't have a CSV, there's no benefit from using the CSV cmdlets.
$lines = Get-Content file.txt
$master = Get-Content master.txt
foreach ($line in $lines)
{
if ($master -contains $line)
{
Add-Content file2.txt $line
}
}
or just
gc file.txt |? { sls $_ master.txt -quiet } | set-content file2.txt
Auto-generated PS help links from my codeblock (if available):
gc is an alias for Get-Content (in module Microsoft.PowerShell.Management)
? is an alias for Where-Object
sls is an alias for Select-String (in module Microsoft.PowerShell.Utility)
Set-Content (in module Microsoft.PowerShell.Management)

Storing output in CSV file

I have wriiten the following SQL script to execute using PowerShell.
cls
foreach ($svr in get-content "demo.txt")
{
$con = "server=MC1-PQ10X.RF.LILLY.COM\SQL01;database=mylilly_WSS_Content_INCTSK0014840;Integrated Security=sspi"
$cmd = "SELECT
Docs.DirName + '/' + Docs.LeafName AS 'Item Name',
DocVersions.UIVersion,
(DocVersions.UIVersion/512) as Version_Label, DocVersions.Level, DocVersions.TimeCreated
FROM DocVersions FULL OUTER JOIN Docs ON Docs.Id = DocVersions.Id
-- INNER JOIN Webs On Docs.WebId = Webs.Id
--INNER JOIN Sites ON Webs.SiteId = SItes.Id
WHERE (DirName LIKE '%globalcontentrepository%')
AND (IsCurrentVersion = '0')
AND (DocVersions.Id IN ('$svr'))"
$da = new-object System.Data.SqlClient.SqlDataAdapter ($cmd, $con)
$dt = new-object System.Data.DataTable
$da.fill($dt) |out-null
$dt | Export-Csv music.csv -Encoding ascii -NoTypeInformation
}
The problem I'm facing with the above code is regarding the output. For every $svr this code is creating a new CSV file. The input file is containing around 1000 inputs. My requirement is that all the output should get stored in the same file rather than creating new file.
There are several ways to achieve your goal. One is to append to the output file as #PeterMmm suggested in the comments to your question:
foreach ($svr in Get-Content "demo.txt") {
...
$dt | Export-Csv music.csv -Encoding ascii -NoTypeInformation -Append
}
Note, however, that Export-Csv doesn't support appending prior to PowerShell v3. Also, appending usually has a negative impact on performance, because you have to repeatedly open the output file. It's better to put the export cmdlet outside the loop and write the output file in one go. foreach loops don't play nicely with pipelines, though, so you'd have to assign the loop output to a variable first:
$music = foreach ($svr in Get-Content "demo.txt") {
...
$dt
}
$music | Export-Csv music.csv -Encoding ascii -NoTypeInformation
or run it in a subexpression:
(foreach ($svr in Get-Content "demo.txt") {
...
$dt
}) | Export-Csv music.csv -Encoding ascii -NoTypeInformation
Personally I'd prefer a ForEach-Object loop over a foreach loop, because the former does work well with pipelines:
Get-Content "demo.txt" | ForEach-Object {
...
$dt
} | Export-Csv music.csv -Encoding ascii -NoTypeInformation
Note that you must replace every occurrence of your loop variable $svr with the "current object" variable $_ for this to work, though.

Powershell INI editing

I want to changing some values in an INI file. Unfortunately, I have keys in 2 different sections which share an identical name but need different values. My code uses the Get-IniContent function from PsIni.
Example INI file:
[PosScreen]
BitmapFile=C:\Temp\Random.bmp
Bitmap=1
[ControlScreen]
BitmapFile=C:\Temp\Random.bmp
Bitmap=1
I need to change the above to the following:
[PosScreen]
BitmapFile=C:\Temp\FileC.bmp
Bitmap=1
[ControlScreen]
BitmapFile=C:\Temp\FileD.bmp
Bitmap=1
The PowerShell code I am using seems to work, but it changes every value to "File D". It is obviously parsing everything twice, and the name is the same for each section.
$NewFileC = "C:\Temp\FileC.bmp"
$NewFileD = "C:\Temp\FileD.bmp"
$POSIniContent = Get-IniContent "C:\scripts\Update-EnablerImage\WINSUITE.INI"
$BOIniContent = Get-IniContent "C:\scripts\Update-EnablerImage\WINSUITE.INI"
If ($POSIniContent["PosScreen"]["BitmapFile"] -ne $NewFileC) {
Get-Content "C:\scripts\Update-EnablerImage\WINSUITE.INI" |
ForEach-Object {$_ -replace "BitmapFile=.+" , "BitmapFile=$NewFileC" } |
Set-Content "C:\scripts\Update-EnablerImage\WINSUITE.INI"
}
If ($BOIniContent["ControlScreen"]["BitmapFile"] -ne $NewFileD) {
Get-Content "C:\scripts\Update-EnablerImage\WINSUITE.INI" |
ForEach-Object {$_ -replace "BitmapFile=.+" , "BitmapFile=$NewFileD" } |
Set-Content "C:\scripts\Update-EnablerImage\WINSUITE.INI"
}
My struggle is how to change each one independently. I'm a bit of a scripting newbie, so calling out for some help. Tried using Conditional Logic (ForEach $line in $INIFile, for example), but no luck with that.
You are overcomplicating things. You can use Get-IniContent and Out-IniFile as follows:
$ini = Get-IniContent c:\temp\ini.ini
$ini["posscreen"]["BitmapFile"] = "C:\Temp\FileC.bmp"
$ini | Out-IniFile -FilePath c:\temp\ini2.ini
Note that if you want to overwrite the original file, you must add -Force to the Out-IniFile call.