Adding to a CSV - csv

I have the following code that basically gets the servername and adds it to a CSV on a UNC. The problem I am having is that if I run it multiple times it keeps appending the header and adding the 2nd server.
First run this is how the file looks:
ServerName
Server1
Second run this is what happens:
ServerName
Server1
ServerName
Server2
How can I prevent this so that the server just gets appended instead of the header being written each time?
This is how my code looks:
$servername = $env:computername
# convert string to an object, otherwise if you output to a CSV it will save the
# strings length instead of the servername
# This entry is for testing
#$servername = "test1"
$obj_list = $servername | Select-Object #{Name='ServerName';Expression={$_}}
# converts to an object and sets no header for the column
#$obj_list | ConvertTo-Csv -NoTypeInformation | % {$_.Replace('"','')} | select -Skip 1 | Add-Content \\test\d$\citrixservers\test1.csv
$obj_list | ConvertTo-Csv -NoTypeInformation | % {$_.Replace('"','')} | Add-Content \\test\d$\citrixservers\servers.csv

Skip the header if the file already exists:
$csv = '\\test\d$\citrixservers\servers.csv'
$skip = if (Test-Path -LiteralPath $csv) { 1 } else { 0 }
$obj_list | ConvertTo-Csv -NoType |
Select-Object -Skip $skip |
ForEach-Object { $_.Replace('"','') } |
Add-Content \\test\d$\citrixservers\test1.csv

You need to use Export-CSV with the -Append parameter rather than ConvertTo-Csv. Here's one way to do it.
$servers = #('server1','server2')
foreach ($server in $servers) {
[pscustomobject]#{'ServerName' = $server} | Export-Csv -Append -Path C:\Servers.csv
}

Related

Modifying JSON file using data from CSV in PowerShell

I'm trying to modify some specific values in a .json file based on two columns in a .csv file. If the current value in the .json file is identical to the one in the left column, I want to change it to the one in the right column.
This is my first time with PowerShell though, so I'm struggling to figure out how to go about doing this. I feel like my solution is not only wrong, but is using a double for loop when it might not need to. Here's what I have so far.
$jsonData = Get-Content -Path $jsonFile | ConvertFrom-Json
$csvData = Get-Content -Path $csvFile | Select-Object -Skip 1 # Skipping the header
foreach ($jsonItem in $jsonData.'Placeable List') {
foreach ($csvRow in $csvData) {
$splitRow = $csvRow -split ","
$lCol = $splitRow[0]
$rCol = $splitRow[1]
$currentItem = $jsonItem.'value'.'Appearance'.'value'
if ($currentItem -eq $lCol) {
$currentItem -eq $rCol
}
}
}
I managed to figure it out.
$csvData = Get-Content -Path $csvFile | Select-Object -Skip 1 # Skipping the header
$jsonData = Get-Content -Path $jsonFile -raw | ConvertFrom-Json
foreach($csvRow in $csvData) {
$splitRow = $csvRow -split ","
$lCol = $splitRow[0]
$rCol = $splitRow[1]
foreach($item in $jsonData.'Placeable List'.value) {
$item.Appearance | % {
if ($_.value -eq $lCol) {
$_.value = $rCol
}
}
}
}
$jsonData | ConvertTo-Json -depth 32 | Set-Content $jsonFile

How can I combine fields in a .csv based off of a shared value in powershell?

I have two files in identical formats, one containing destination IP addresses and URLs, and one that contains only the destination IP addresses. I am attempting to write a powershell script to add the URL field from the first file to the second file for that row if the destination IP addresses are equal. Here is an example of the two files:
File Containing URLs:
Date;Time;Source;Destination;Port;User;URL
3/7/2016;0:00:07;168.254.25.6;10.0.1.27;80;jsmith;abcnet
File to add URLs to:
Date;Time;Source;Destination;Port;User;URL
3/7/2016;0:00:09;168.254.25.6;10.0.1.27;80;;
Whenever I run the code below, it appears to be caught in an infinite loop because it does not run to completion, but it throws no errors. My data set is thousands of lines long, but it works when I test it with a sample set that is only a few lines long.
$noURLs = Import-Csv C:\Path\to\noURLs.csv
$containsURLs = Import-Csv C:\Path\to\containsURLs.csv | Select-Object Destination, URL
$outputFile = "C:\Path\to\output.csv"
if(Test-Path $outputFile){
Remove-Item $outputFile
}
foreach($line in $noURLs){
$cpDest = $line.Destination
$destURL = $containsURLs | Where-Object {$_.Destination -eq $cpDest} | Select-Object -ExpandProperty URL | Select-Object -Unique
if($destURL -ne $null){
if( $destURL.Count -gt 1) {
$destURL = $destURL -join ';'
}
}
$line.URL = $destURL
}
$noURLs | Export-Csv $outputFile
I forgot to add a -unique switch to my select object, so for every one record in the first csv, it was looping through every single line of the second csv. Fixed code looks like this:
$noURLs = Import-Csv C:\Path\to\noURLs.csv
$containsURLs = Import-Csv C:\Path\to\containsURLs.csv | Select-Object -Unique Destination, URL
$outputFile = "C:\Path\to\output.csv"
if(Test-Path $outputFile){
Remove-Item $outputFile
}
foreach($line in $noURLs){
$cpDest = $line.Destination
$destURL = $containsURLs | Where-Object {$_.Destination -eq $cpDest} | Select-Object -ExpandProperty URL | Select-Object -Unique
if($destURL -ne $null){
if( $destURL.Count -gt 1) {
$destURL = $destURL -join ';'
}
}
$line.URL = $destURL
}
$noURLs | Export-Csv $outputFile -NoTypeInformation

Powershell saving as csv file

Good day!
I am trying to get the opened applications that you can see on the taskbar, the computers' IP address and its username. After which, I would save it in a text file with the values separated by commas. Now, I want everything to be saved in the text file but in my set of codes, the results from Get-process are the only ones that are saved on the text file. How can I include the IP address and the username saved together with the results from Get-process on the same text file.
Here is my code:
$savepath = "C:\Users\$([Environment]::UserName)\Desktop\apps\runningapps.txt"
Get-process | where {$_.mainwindowtitle.length -ne 0} | select name, mainwindowtitle| Export-Csv $savepath -notype
Get-WMIObject -class Win32_ComputerSystem | select username| Export-Csv $savepath -append -notype
Get-WmiObject Win32_NetworkAdapterConfiguration |
Where { $_.IPAddress } | # filter the objects where an address actually exists
Select -Expand IPAddress | # retrieve only the property *value*
Where { $_ -notlike "*:*" }|Export-Csv $savepath -append -notype
I think your issue is because the three objects you are exporting have different attributes, you are having the trouble?
If you really want to put it all on one file, then you could
ConvertTo-Csv -notype | Add-Content $savepath

Include file owner to powershell pipe

I've got a script which works fine which lists all files modified since last 7 days and want to modify it to add file owner to the export csv file.
$dir_to_look="F:\"
$month_backdate=$(Get-Date).AddDays(-7)
Get-Childitem $dir_to_look -Recurse | ? { !($_.psiscontainer) -and $_.LastWriteTime -gt $month_backdate } | ForEach-Object {Get-Acl $_.FullName}.owner | Select-object LastWriteTime, Directory, FullName | export-csv -path \\sharename\report.csv -encoding "unicode"
But not sure how to correctly add get-acl to the pipe as currently it prints nothing to my report file
Your Foreach-Object command should be:
... | Foreach-Object {Add-Member -Input $_ -Type NoteProperty -Name Owner -Value (Get-Acl $_.Fullname).Owner -PassThru} | Select-object LastWriteTime, Directory, FullName, Owner | ...
Add-Member is handy for adding properties (and methods) to objects.
Use a hash table with Select-Object.
$dir_to_look="F:\"
$month_backdate=$(Get-Date).AddDays(-7)
Get-Childitem $dir_to_look -Recurse | ? { !($_.psiscontainer) -and $_.LastWriteTime -gt $month_backdate } |
Select-object LastWriteTime, Directory, FullName, #{n='Owner';e={(Get-ACL $_.FullName).owner}}
$dir_to_look="F:\"
$month_backdate=$(Get-Date).AddDays(-7)
Get-Childitem $dir_to_look -Recurse | ? { !($_.psiscontainer) -and $_.LastWriteTime -gt $month_backdate } |
Select-object LastWriteTime, Directory, FullName, #{n='Owner';e={(Get-ACL $_.FullName).owner}}

Import-Csv TrimEnd Column Header

I need import a CSV and run it through a foreach loop. I want to trim the end on the column header DeviceName to avoid any potential issues. I have tried the following but it is not working as expected.
$Import = Import-CSV $csv
foreach ($i in ($import.DeviceName).TrimEnd())
{do something}
Any help? Thank you!
If you need to change both the header and the content in the column for devicename which has spaces I have come up with this forgiving code.
$csvData = import-csv $csv
$properties = $csvData[0].psobject.Properties.name
$csvHeader = "`"$(($properties | ForEach-Object{$_.Trim()}) -join '","')`""
$deviceHeader = $properties -match "DeviceName"
$csvHeader
$csvHeader | Set-Content $file
$csvData | ForEach-Object{
$_.$deviceHeader = ($_.$deviceHeader).trim()
$_
} | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1 | Add-Content $file
What this does is read in the CSV like normal. Parse the property names of the object in the order they appear. We find the one that has DeviceName no matter how many spaces (if there is more that one you could have a problem). Keep that so we can use it to call the correct property of each "row".
Export the new cleaned header to the file. Then we go through each "row" removing all the leading and trailing space from the DeviceName. Once that is done write back the CSV to the original file.
The best solution would be to tell the other team to fix their generation procedure. However, if for some reason that's not an option, I'd recommend pre-processing the file before you import it as a CSV.
$filename = 'C:\path\to\your.csv'
(Get-Content $filename -Raw) -replace '^(.*DeviceName)[ ]*(.*)', '$1$2' |
Set-Content $filename
Reading the file as a single string (-Raw) and anchoring the expression at the beginning of the string (^) ensures that only the column title is replaced.
For large input files you may want to consider a different approach, though, since the above reads the entire file into memory before replacing the first line.
$infile = 'C:\path\to\input.csv'
$outfile = 'C:\path\to\output.csv'
$firstLine = $true
Get-Content $infile | % {
if ($firstLine) {
$_ -replace '(DeviceName)[ ]*', '$1'
$firstLine = $false
} else {
$_
}
} | Set-Content $outfile
Thinking about it some more and taking inspiration from a comment to #Zeek's answer, you could also extract the headers first and then convert the rest of the file.
$infile = 'C:\path\to\input.csv'
$outfile = 'C:\path\to\output.csv'
$header = (Get-Content $infile -First 1) -split '\s*,\s*'
Get-Content $infile |
select -Skip 1 |
ConvertFrom-Csv -Header $header |
Export-Csv $outfile -NoType
Is this all you're trying to do? This will give you a collection of objects imported from your csv file but trim the end of the DeviceName property on each object.
$items = Import-CSV -Path $csv
$items.ForEach({ $_.DeviceName = $_.DeviceName.TrimEnd() })