Powershell output to csv file - powershell

I have a script that obtains that gathers information about a computer then outputs it to a csv file. You can see I have tried multiple approaches to this issue and cannot quite get any of them to work. A fix to this, or a completely different solution would be welcome.
#Objective is to create a csv file with
# "hostname of public IP","public IP","machine name",date,time-24hour
# for example: (note no header in file)
# "97-94-177-139.dhcp.ftwo.tx.charter.com","97.94.177.139","IT-BHOLLING",8/23/2014,06:52:35
# quotes around text fields are an optional objective (helps with some csv import engines)
# some approaches require that the path c:\IP\Working exists (or change lines 46 & 47
# Change line 61 to try each output method
#Variables
# I am defining website url in a variable
$url = "http://checkip.dyndns.com"
# Creating a new .Net Object names a System.Net.Webclient
$webclient = New-Object System.Net.WebClient
# In this new webdownlader object we are telling $webclient to download the url $url
#get public IP address
$Ip = $webclient.DownloadString($url)
# Just a simple text manuplation to get the ipadress form downloaded URL
# If you want to know what it contain try to see the variable $Ip
#$Ip2 = $Ip.ToString()
#$ip3 = $Ip2.Split(" ")
#$ip4 = $ip3[5]
#$ip5 = $ip4.replace("</body>","")
#-- or - do this
$FinalIPAddress = $Ip.ToString().Split(" ")[5].replace("</body>","").replace("</html>","")
#get machine name
$MNme = $env:computername
#Get Hostname from IP address
try {
$Resolved = [system.net.dns]::GetHostEntry($FinalIPAddress)
}
catch {
$Resolved = ((&nslookup $FinalIPAddress)|where {$_ -match "^Name:"}).split(':')[1].trim()
}
#$Qstring = "select * from win32_pingstatus where address=" + """$FinalIPAddress""" + " AND ResolveAddressNames=True"
#$Qstring = "'" + $Qstring + "'"
# $AName = Get-WmiObject -Query $Qstring |
# select ProtocolAddressResolved | ConvertTo-Csv
#$AName = Get-WMIObject -q 'select * from win32_pingstatus where address="97.93.177.138" AND ResolveAddressNames=True'|
# select ProtocolAddressResolved | ConvertTo-Csv
#$AName[2..2].ToString()
$FinalIPAddress = $FinalIPAddress.Replace("`r`n","")
#get date
$GDte = Get-Date -format("G")
$GTme = Get-Date -format("u")
$infile = "C:\IP\Working\" + $MNme + ".txt"
$outfile = "C:\IP\Working\" + $MNme + ".csv"
$Dsplit = $GDte.split(" ")
$Dte = $Dsplit[0]
$Tsplit = $GTme.split(" ")
$Tme = $Tsplit[1].replace("Z","")
#remove previous files
If (Test-Path $infile){
Remove-Item $infile
}
If (Test-Path $outfile){
Remove-Item $outfile
}
#Write accumulated data to a file; modify $CMethod to test each alternate approach to creating csv file
$CMethod = "D"
if ($CMethod -eq "A") {
#this method produces a file that works with Access but not with Excel. Excel sees it as unicode-text
$content = """$Resolved""" + "," + """$FinalIPAddress""" + "," + """$MNme""" + "," + $Dte + "," + $Tme
$content > $outfile
}
if ($CMethod -eq "B") {
#This method produces an empty csv file, with a few tweaks it procudes a file with Length info instead of results
#tried with quotes
$content = """$Resolved""" + " " + """$FinalIPAddress""" + " " + """$MNme""" + " " + $Dte + " " + $Tme
#also tried without quotes
#$content = $Resolved + " " + $FinalIPAddress + " " + $MNme + " " + $Dte + " " + $Tme
$content > $infile
import-csv $infile -delimiter " " | export-csv -NoTypeInformation $outfile
}
If ($CMethod -eq "C") {
$content = """$Resolved""" + " " + """$FinalIPAddress""" + " " + """$MNme""" + " " + $Dte + " " + $Tme
add-content $infile $content
import-csv $infile -delimiter " " | export-csv -NoTypeInformation $outfile
}
if ($CMethod -eq "D") {
#Yet another method, convert to object first
#$content = """$Resolved""" + " " + """$FinalIPAddress""" + " " + """$MNme""" + " " + $Dte + " " + $Tme
#$content = """$Resolved""" + "," + """$FinalIPAddress""" + "," + """$MNme""" + "," + $Dte + "," + $Tme
$content = $Resolved + "," + $FinalIPAddress + "," + $MNme + "," + $Dte + "," + $Tme
$psObject = $null
$psObject = New-Object psobject
$Csplit = $content.Split(",")
#alternate approaches to converting PSobjecdt to csv
foreach($o in $Csplit)
{
Add-Member -InputObject $psobject -MemberType noteproperty -Name $o -Value $o -PassThru
}
$psObject | Export-Csv $outfile -NoTypeInformation
#Add-Member -InputObject $psobject -MemberType noteproperty -Name $Csplit -Value $Csplit
#$psObject | Export-Csv $outfile -NoTypeInformation
}
#echo to console
"Mehtod Choosen = " + $CMethod
$content
$Csplit
"end of the script....."

You can use Add-Content -Path "FilePath" -Value "$Resolved,$FinalIPAddress,$MNme,$Dte,$Tme".
Then you have no header and the data seperated with ",".

I would suggest letting PowerShell do the CSV creation for you. All that you need to do for that is to create a custom object with the properties you're after, then you can use the Export-Csv or ConvertTo-Csv cmdlets. You can also control the encoding used since it sounds like you might be having some encoding issues with the programs that are consuming your CSV. I've modified your code below to create a PSObject. See if this works for you:
$url = "http://checkip.dyndns.com"
$Now = Get-Date
$webclient = New-Object System.Net.WebClient
$ErrorString = "<ERROR>"
$CsvEncoding = "ASCII" # Any encoding from [System.Text.Encoding] should work
$outfile = "C:\IP\Working\{0}.csv" -f $env:computername
If (Test-Path $outfile){
Remove-Item $outfile
}
try {
$Ip = $null
$Ip = $webclient.DownloadString($url)
}
catch {
Write-Warning ("Error getting IP address: {0}" -f $_.Exception.Message)
}
if ($Ip -match ".*IP Address:\s*((\d{1,3}\.){3}\d{1,3}).*") {
$FinalIpAddress = $matches[1]
#Get Hostname from IP address
try {
$Resolved = [system.net.dns]::GetHostEntry($FinalIpAddress) | select -ExpandProperty HostName
}
catch {
try {
$Resolved = ((&nslookup $FinalIpAddress)|where {$_ -match "^Name:"}).split(':')[1].trim()
}
catch {
$Resolved = $ErrorString
Write-Warning ("Error resolving IP address '$FinalIpAddress': {0}" -f $_.Exception.Message)
}
}
}
else {
$FinalIpAddress = $ErrorString
$Resolved = $ErrorString
}
# This won't work as is in PSv2, but a simple mod would fix it:
$ReturnObject = [PSCustomObject] #{
Resolved = $Resolved
FinalIpAddress = $FinalIpAddress
MNme = $env:computername
Dte = $Now.ToShortDateString()
Tme = $Now.ToString("HH:mm:ss")
}
# At this point, you can let PS do all of the work to create a CSV
# Create normal CSV with encoding defined above:
$ReturnObject | Export-Csv -Path $outfile -Encoding $CsvEncoding -NoTypeInformation
# Create a CSV w/o header (this line will actually append a second line to $outfile):
$ReturnObject | ConvertTo-Csv -NoTypeInformation | select -Skip 1 | Out-File -FilePath $outfile -Encoding $CsvEncoding -Append

Related

powershell script can't find a file that exists

I have this script that creates files using bcp. As BCP doesn't export data with header i need to join both files into. If i run the code in another script it creates the csv file but if i run this script it fails saying files DOES NOT EXIST, that is the last section of the script below (#THE PROBLEM STARTS HERE).
Appreciate any help.
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction']='Stop'
$bcp = 'c:\Program Files\Microsoft SQL Server\100\Tools\Binn\bcp.exe'
$delimiter = '","'
$server = 'BLABLABLA'
$database = 'DMB'
$schema = '.dbo.'
$dbschema = $database + $schema
$atresult = "#result"
$commas = " + ',','') + "
$head1 = 'Use '
$head2 = ' Declare ' # atresult
$head3 = ' varchar(max) select ' # atresult
$head4 = ' = COALESCE(' #atresult $commas
$head5 = 'COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ' # tablename
$head6 = ' select ' #atresult
$tables = #('EntityTypes_20181205','FieldFieldTypeInfo')
$output_path = 'D:\Temp\DataModelBuild\'
# Deletes any file in Destination Folder
#if ( (Get-ChildItem $output_path | Measure-Object).Count -ne 0) {}
Get-ChildItem -Path $output_path -Include *.* -File -Recurse | foreach { $_.Delete()}
foreach ($element in $tables)
{
# GET COLUMN NAMES into header file
$outputFile = $output_path + $element + ".header"
$NQ = '"' + $head1 + $database + $head2 + $atresult + $head3 + $atresult + $head4 + $atresult + $commas + $head5 + ''' + $element + ''' + $head6 + $atresult + '"'
#$arglist = "(" + $NQ + " queryout " + $outputFile + ", -S " + $server + " -T -t" + $delimiter + " -c -k)"
$arglist = #('"',
$head1,
$database,
$head2,
$atresult,
$head3,
$atresult,
$head4,
$atresult,
$commas,
$head5,
"'$element'",
$head6,
$atresult,
'"',
" queryout $outputFile",
"-S $server -T -t$delimiter -c -k")
#write-output $arglist
Start-Process -FilePath $bcp -ArgumentList $arglist
#Read-Host
# GET DATA into data file
$outElement = $dbschema + "[" + $element + "]"
$outputFile = $output_path + $element + ".data"
$arglist = #($outElement, "out $outputFile", "-S $server -T -t$delimiter -c -k")
Start-Process -FilePath $bcp -ArgumentList $arglist
#write-output $arglist
#Read-Host
# Merge header and data to csv
#Invoke-Expression "&'$ExtPs1' -FilePath $output_path -FileName $element"
#Write-Output "call "
#Invoke-Expression "&'D:\idna\PedroTemp\cp2csv.ps1' $output_path $element"
#Read-Host
#THE PROBLEM STARTS HERE
$File1 = #("$output_path$element.header")
$File2 = #("$output_path$element.data")
$File3 = #("$output_path$element.csv")
write-output $File1, $File2, $File3
If (-not (Test-Path -Path $File1)) {
Write-Error "File DOES NOT EXIST '$File1'" -ErrorAction Stop
} else {
Write-Host "File EXIST"
}
Add-Content $File3 -value (Get-Content $File1)
Add-Content $File3 -value (Get-Content $File2)
#write-output $File1, $File2, $File3 -ErrorAction Stop
Read-Host
}
My question is why isn't the script detecting the file?
Is there any problem with the string?
The strings for the pathes aren't build correctly. When you use a member in a string you have to enclose the variable and the member with $().
$File1 = "$output_path$($element.header)"

Powershell loop csv file export csv line one at a time

I'm trying to loop a pipe delimited file, check if the line has 28 columns and just export that line to the file. This is the primary question and answer I'm looking for (I have researched a lot, but I'm new to PS and so many different ways I need some help). The following works but there are two issues. The export file will not let me choose pipe delimited, AND the output goes in alphabetical format by field name, not by ordinal.
Also, is there a way I can make the output not "text" qualified?
$path = Get-ChildItem c:\temp\*.txt
$staticPath = '0859'
$year = Get-Date -format yy
$month = Get-Date -format MM
$day = Get-Date -format dd
$output = 'c:\temp\' + $year + $month + $day + $staticPath + '.TXT'
$outputbad = 'c:\temp\BAD' + $year + $month + $day + $staticPath + '.TXT'
$input = 'c:\temp\' + $path.Name
$input
$csv = Import-Csv -path $input -Delimiter "|"
foreach($line in $csv)
{
$properties = $line | Get-Member -MemberType Properties
$row = ''
$properties.Count
$obj = new-object PSObject
for($i=0; $i -lt $properties.Count;$i++)
{
$column = $properties[$i]
$columnvalue = $line | Select -ExpandProperty $column.Name
#$row += $columnvalue
$obj | add-member -membertype NoteProperty -name $column.Name -value $columnvalue
}
if($properties.Count -eq 28)
{
$obj | export-csv -Path $output -Append -NoTypeInformation
#$row | Export-Csv -Path $output -Append -Delimiter "|" -NoTypeInformation
}
else
{
$obj | Export-Csv -Path $outputbad -Append -Delimiter "|" -NoTypeInformation
}
}
If you want to avoid any chance of changing the formatting of these lines files, perhaps you don't want to use the -CSV commands. Export-csv can add quotation marks etc. Here's different way that might do what you want:
$path | ForEach-Object {
$good = #()
$bad = #()
Get-Content $_ | ForEach-Object {
if (($value = $_ -split '\|').length -eq 28) {
$good += $_
} else {
$bad += $_
}
}
if ($good) { Out-File -Append -InputObject $good $output }
if ($bad) { Out-File -Append -InputObject $bad $outputbad }
}
Note however that this will count quoted values containing a pipe differently than import-csv. Pipe separated values are sometimes generated without any quoting logic.
The $values variable will be an array of individual columns, so if you want to write some code to fix them up inside the if, you can use that then join them back up with $good += $values -join '|', or perhaps use another regex to fix errors.
silly me, here is the answer. But if somebody could answer why the PSObject or the properties loop is alphabetical it would be helpful. Soon i would like to add intelligence to this to check ordinal (not alphabetic field name order) a field if its a integer or not, then i know how to fix the BAD record.
$path = Get-ChildItem c:\temp\*.txt
$staticPath = '0859'
$year = Get-Date -format yy
$month = Get-Date -format MM
$day = Get-Date -format dd
$output = 'c:\temp\' + $year + $month + $day + $staticPath + '.TXT'
$outputbad = 'c:\temp\BAD' + $year + $month + $day + $staticPath + '.TXT'
$input = 'c:\temp\' + $path.Name
$csv = Import-Csv -path $input -Delimiter "|"
foreach($line in $csv)
{
$properties = $line | Get-Member -MemberType Properties
if($properties.Count -eq 28)
{
$line | export-csv -Path $output -Append -NoTypeInformation -Delimiter "|"
}
else
{
$line | Export-Csv -Path $outputbad -Append -Delimiter "|"
}
}

Close a file handle opened by .NET

I'm working on a script to rename files based on EXIF data.
param([string]$path)
# http://blogs.technet.com/b/jamesone/archive/2007/07/13/exploring-photographic-exif-data-using-powershell-of-course.aspx
[reflection.assembly]::loadfile( "C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll") | out-null
function MakeString {
$s=""
for ($i=0 ; $i -le $args[0].value.length; $i ++) {
$s = $s + [char]$args[0].value[$i]
}
return $s
}
$files = Get-ChildItem -Path $path
foreach ($file in $files) {
if ($file.Extension -ne ".jpg") { continue }
if ($file.Name -match "^(\d+)-(\d+)-(\d+)") { continue }
$exif = New-Object -TypeName system.drawing.bitmap -ArgumentList $file.FullName
$captureDate = MakeString $exif.GetPropertyItem(36867)
$captureDate = ($captureDate -replace ":", '-').Substring(0,19)
$newFilename = $captureDate + " " + $file.Name.Trim()
$file.Name + " -> " + $newFilename
$file |Rename-Item -NewName $newFilename
}
Reading the date from EXIF is no problem, but when I try to rename the file I get this error message:
Rename-Item : The process cannot access the file because it is being used by another process.
At D:\Norwegen\RenamePhotos.ps1:25 char:12
+ $file |Rename-Item -NewName $newFilename
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (D:\Norwegen\P1270465 (1920x1433).jpg:String) [Rename-Item], IOException
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
When I monitor the directory in ProcMon I can see that the files are closed rather late:
(look at the highlighted line, it's from an earlier file in the middle of the entries from the file currently being processed)
So, how can I close the file (which is probably still open from the EXIF read) so I can rename it?
What I already tried to close the open file:
Remove-Variable exif
$file.Close()
Since you only read from the the file, then a call to Dispose() should do the trick. Ex.
foreach ($file in $files) {
if ($file.Extension -ne ".jpg") { continue }
if ($file.Name -match "^(\d+)-(\d+)-(\d+)") { continue }
$exif = New-Object -TypeName system.drawing.bitmap -ArgumentList $file.FullName
$captureDate = MakeString $exif.GetPropertyItem(36867)
#Disposing object as soon as possible
$exif.Dispose()
$captureDate = ($captureDate -replace ":", '-').Substring(0,19)
$newFilename = $captureDate + " " + $file.Name.Trim()
$file.Name + " -> " + $newFilename
$file |Rename-Item -NewName $newFilename
}

Change the name of the output file to servername

I want to dynamically output the file using the name of the server its been run instead of specifying what name to use. as i want to run the code on multiple servers using multi-instance approach.
$OutFile = "C:\Users\munjanga\Documents\AoN Project\Execute\Output.csv"
$Header = "FolderPath,IdentityReference,AccessControlType,IsInherited,InheritanceFlags,PropagationFlags"
Del $OutFile
Add-Content -Value $Header -Path $OutFile
$RootPath = "C:\Users\munjanga\Documents\Operations Orchestration"
$Folders = dir $RootPath -recurse | where {$_.psiscontainer -eq $true}
foreach ($Folder in $Folders){
$ACLs = get-acl $Folder.fullname | ForEach-Object { $_.Access }
Foreach ($ACL in $ACLs){
$OutInfo = $Folder.Fullname + "," + $ACL.IdentityReference + "," + $ACL.AccessControlType + "," + $ACL.IsInherited + "," + $ACL.InheritanceFlags + "," + $ACL.PropagationFlags
Add-Content -Value $OutInfo -Path $OutFile
}
}
If I understand you correctly, you want to change $OutFile depending on name of server. You can do that using MachineName property of Environment class:
$OutFile = "C:\Users\munjanga\Documents\AoN Project\Execute\$([Environment]::MachineName).csv"

Proper use of Get-Childitem with a PowerShell Foreach loop

I want to take a text file with usernames, and then search a specific location (UNC path) and return any matches found, outputting them to a log file:
#Searches target folder for all folders matching input
$start = (get-date -uformat "%Y-%m-%d_%H%M")
$defaultLogFileName = "folder_matcher" + $start + ".log"
#Log file header
$header = #()
$header += "=============================="
$header += "Search results"
$header += ""
$header += "Execution Start: " + (get-date -uformat "%Y-%m-%d %H%M") + ""
$header += ""
#Get the logfile location
do
{
$logdir = Read-Host "Enter log directory (or press Enter for default c:\)"
if ($logdir -eq "")
{$logfile = ("c:\" + $defaultLogFileName); break;}
if(Test-Path $logdir)
{$logfile += ($logdir + "\" + $defaultLogFileName); break;}
Write-Host -ForegroundColor Red "Directory does not exist"
} while (!(Test-Path $logfile))
$SourceFile = Read-Host "Enter file path"
$SearchValue = Read-host "Enter target directory to sweep"
$header | Out-File -FilePath $logfile
foreach($user in $sourcefile){
Get-ChildItem $SearchValue -filter $user | Out-file -Append -FilePath $logfile
}
When I attempt to use Get-ChildItem in this loop, this is the result:
Get-ChildItem : Second path fragment must not be a drive or UNC name.
Parameter name: path2
At \\erebus\erebus_users$\rraymond1\ps\searchandmatch.ps1:32 char:14
+ Get-ChildItem <<<< $SearchValue -filter $user | Out-file -Append -FilePath $logfile
+ CategoryInfo : InvalidArgument: (\\harvard\tsprofiles$:String)
[Get-ChildItem], ArgumentException + FullyQualifiedErrorId DirArgumentError,Microsoft.PowerShell.Commands.GetChildItemCommand
I have tried replacing the variables with fixed values (even mapping the UNC path locally and trying to run it that way), and the problem remains. How can I fix it?