Encoding CSV content to UTF-8 - powershell

So I have this powershell script Which import a csv file, replacing null into '0' and export this csv.
The issue is that the content and header of this csv is in hebrew
I tried almost everything
Used -Encoding for all types of encoding but nothing
Any Suggestions?
$propertyTranslation = #(
#{ Name = 'Customer__c'; Expression = { $_.'לקוח' } }
#{ Name = 'Name__c'; Expression = { $_.'שם' } }
#{ Name = 'CheckCount__c'; Expression = { $_.'כמות' } }
#{ Name = 'Deal'; Expression = { $_.'עסקהוזה' } }
#{ Name = 'Amount__c'; Expression = { $_.'סכום' } }
#{ Name = 'Discount__c'; Expression = { $_.'ניסיון' } }
# And so on
)
$csv = Import-Csv C:\Users\alon\Documents\again.csv -Header "Customer__c","Name__c","Deal","Amount__c","CheckCount__c","Discount__c"
$csv | ForEach-Object {
if($_.Customer__c -eq "") { $_.Customer__c = "0" }
if($_.Name__c -eq "") { $_.Name__c = "0" }
if($_.Deal -eq "") { $_.Deal = "0" }
if($_.Amount__c -eq "") { $_.Amount__c = "0" }
if($_.Discount__c -eq "") { $_.Discount__c = "0" }
if($_.CheckCount__c -eq "") { $_.CheckCount__c = "0" }
}
Select-Object -Property $propertyTranslation
$csv | Export-Csv C:\Users\alon\Documents\CheckDealBeforeUpsert.csv -NoTypeInformation -Encoding UTF8

The term "ANSI" as it is used in Windows is basically an umbrella term for a number of encodings (or code pages). Usually it refers to the windows-1252 encoding. Your input file, however, appears to be encoded using the windows-1255 code page.
I'm not sure if in PowerShell -Encoding ASCII always means windows-1252 encoding, or if that is adjusted for localized Windows versions. If it's not adjusted you probably need to convert your input file to an encoding Import-Csv can handle before you can import and modify the data:
$inFile = 'C:\path\to\input.csv'
$outFile = 'C:\path\to\input_utf8.csv'
$reader = New-Object IO.StreamReader ($inFile, [Text.Encoding]::GetEncoding(1255))
$writer = New-Object IO.StreamWriter ($outFile, $false, [Text.Encoding]::UTF8)
while ($reader.Peek() -ge 0) {
$writer.WriteLine($reader.ReadLine())
}
$reader.Close(); $reader.Dispose()
$writer.Close(); $writer.Dispose()

Related

Powershell - Exchange JSON output without needing to write to a file

EDIT: Added Setupconfigfiles.ps1
I'm a bit new to detailed scripting so please bear with me.
I have two Powershell scripts
Setupconfigfiles.ps1 generates JSON output to be fed to an API.
Script2 uses that JSON data to execute API commands.
Script 2 can call setupconfigfiles.ps1 as indicated below and use the output data.
.\SetupConfigFiles.ps1 -type $Type -outfile .\Templist.json
$servers = Get-Content -Raw -Path .\templist.json | ConvertFrom-Json
setupconfigfiles.ps1:
param (
# If this parameter is set, format the output as csv.
# If this parameter is not set, just return the output so that the calling program can use the info
[string]$outfile,
# this parameter can be 'production', 'development' or 'all'
[string]$type
)
enum MachineTypes {
production = 1
development = 2
all = 3
}
$Servers = Get-ADObject -Filter 'ObjectClass -eq "computer"' -SearchBase 'Obfuscated DSN' | Select-Object Name
$output = #()
$count = 0
# Set this to [MachineTypes]::production or [MachineTypes]::development or [MachineTypes]::all
if ($type -eq "all") {
$server_types = [MachineTypes]::all
}
ElseIf ($type -eq "production") {
$server_types = [MachineTypes]::production
}
else {
$server_types = [MachineTypes]::development
}
ForEach ($Server in $Servers)
{
$count = $count + 1
$this_server = #{}
$this_server.hostname = $Server.Name
$this_server.id = $count
$this_server."site code" = $this_server.hostname.substring(1,3)
$this_server."location code" = $this_server.hostname.substring(4,2)
if ($this_server.hostname.substring(7,1) -eq "P") {
$this_server.environment = "Production"
}
ElseIf ($this_server.hostname.substring(7,1) -eq "D") {
$this_server.environment = "Development"
}
Else {
$this_server.environment = "Unknown"
}
if (($server_types -eq [MachineTypes]::production ) -and ($this_server.environment -eq "Production")) {
$output += $this_server
}
ElseIf (($server_types -eq [MachineTypes]::development ) -and ($this_server.environment -eq "Development")) {
$output += $this_server
}
Else {
if ($server_types -eq [MachineTypes]::all ) {
$output += $this_server
}
}
}
if ($outfile -eq "")
{
ConvertTo-Json $output
}
else {
ConvertTo-Json $output | Out-File $outfile
}
How can I do it without needing to write to the Templist.json file?
I've called this many different ways. The one I thought would work is .\SetupConfigFiles.ps1 $servers
Y'all are great. #Zett42 pointed me in a direction and #Mathias rounded it out.
The solution was to change:
"ConvertTo-Json $output" to "Write-Output $output"
Then it's handled in the calling script.
thanks!

Powershell scripting - replace text in files

Given these powershell functions below. I'm trying to replace the version text with another version that I specify. All my paths are correct, and I output both the current version and new version appropriatlely. When i go to replace them, the file indicates that it has changed, but no text was changed.
Alternately, I've found if i specify the exact current version as a string variable and bypass the Get-VersionXXX calls, it replaces the version without issues. Is there anything obvious I'm doing wrong?
function Get-VersionAndDateFromFile([string] $versionFile, [string] $versionString)
{
if (-Not(Test-Path -path $versionFile))
{
return ""
}
else
{
#foreach($line in [System.IO.File]::ReadLines($versionFile))
(Get-Content -Path $versionFile -Raw) | ForEach-Object {
$line = $_
if ($line.Contains($versionString))
{
if ($line.Contains(""""))
{
$tokens = $line.Split('\"')
return $tokens[1]
}
}
}
}
return ""
}
function Get-VersionOnlyFromFile([string] $versionFile, [string] $versionString)
{
[string] $versionAndDate = Get-VersionAndDateFromFile $versionFile $versionStriong
return VersionFromVersionAndDateString $versionAndDate
}
function Get-VersionFromVersionAndDateString([string] $versionAndDateString)
{
if (!$versionAndDateString)
{
return ""
}
else
{
if ($versionAndDateString.Contains(" "))
{
$newTokens = $versionAndDateString.Trim().Split(" ")
return $newTokens[0]
}
else
{
return $versionAndDateString
}
}
}
function ReplaceTextInFile([string] $fullPath, [string] $oldString, [string] $newString)
{
Write-Host "Old " $oldString
Write-Host "New " $newString
((Get-Content -path $fullPath -Raw) -replace $oldString,$newString) | Set-Content -Path $fullPath
}
Calling code:
[string] $newVersionString = "1.2.3.4.6 09-16-2021"
[string] $currentVersionString = Get-VersionAndDateFromFile $pathToVersionFile "SW_VERSION"
ReplaceTextInFile -fullPath $pathToVersionFile -oldString $currentVersionString -newString $newVersionString
finally: file is a header file called Version.h
#define SW_VERSION "1.2.3.4.5 09-14-2021"
Unless explicitly redirected all output in a function is returned to the pipeline. The return command is redundant. As such, you're sending back two things in your function, first is the version number and second is an empty string (lines starting with >>>):
function Get-VersionAndDateFromFile([string] $versionFile, [string] $versionString)
{
if (-Not(Test-Path -path $versionFile))
{
return ""
}
else
{
#foreach($line in [System.IO.File]::ReadLines($versionFile))
(Get-Content -Path $versionFile -Raw) | ForEach-Object {
$line = $_
if ($line.Contains($versionString))
{
if ($line.Contains(""""))
{
$tokens = $line.Split('\"')
>>> return $tokens[1]
}
}
}
}
>>> return ""
}
You convert the output to a string, and by all accounts it looks right, but if you look more closely you'll see there's a space after the date:
PS C:\WINDOWS\system32> ">$currentVersionString<"
>1.2.3.4.5 09-14-2021 <
If we comment out the last return in that function the space goes away, and your replace should work fine.
function Get-VersionAndDateFromFile([string] $versionFile, [string] $versionString)
{
if (-Not(Test-Path -path $versionFile))
{
return ""
}
else
{
#foreach($line in [System.IO.File]::ReadLines($versionFile))
(Get-Content -Path $versionFile -Raw) | ForEach-Object {
$line = $_
if ($line.Contains($versionString))
{
if ($line.Contains(""""))
{
$tokens = $line.Split('\"')
return $tokens[1]
}
}
}
}
# return ""
}
To be honest, it would be much simpler to do this:
function Get-VersionAndDateFromFile([string] $versionFile, [string] $versionString)
{
if (Test-Path -path $versionFile)
{
Get-Content -Path $versionFile -Raw | Where-Object{$_ -match "$versionString\s+""(.+?)"""} | ForEach-Object {
$Matches[1]
}
}
}
Run your Replace Function with these 2 added lines, as shown below...
$old = $oldString -replace '\s','\s';
$old = $old -replace '\s$';
You'll find you're capturing an extra space at the end of your OldString variable. Like so: 1.2.3.4.5\s\s09-16-2021\s
The two lines fix the issue and it does the replace... or you can fix your Get-Version function to capture the correct string.
function ReplaceTextInFile([string] $fullPath, [string] $oldString, [string] $newString)
{
Write-Host "Old " $oldString
Write-Host "New " $newString
$old = $oldString -replace '\s','\s'; #replaces space with \s
$old = $old -replace '\\s$'; #removes trailing space at end of line
$old = $oldString -replace '\s','\s'; $old = $old -replace '\\s$'
((Get-Content -path $fullPath -Raw) -replace $old,$newString) | Set-Content -Path $fullPath
}

Powershell - Store hash table in file and read its content

As follow-up, suggested by Doug, on my previous question on anonymizing file (
PowerShell - Find and replace multiple patterns to anonymize file) I need to save all hash tables values in single file "tmp.txt" for further processing.
Example: after processing the input file with string like:
<requestId>>qwerty-qwer12-qwer56</requestId>
the tmp.txt file contains:
qwerty-qwer12-qwer56 : RequestId-1
and this is perfect. The problem is when working with many strings, in the tmp.txt file there are more pairs than there should be. In my example below in tmp.txt I should see 4 times the "RequestId-x" but there are 6. Also when there are 2 or more "match" on the same line, only the first is updated/replaced. Any idea from where these extra lines comes from? Any why the script doesn't continue to check till the end of the same line?
Here is my test code:
$log = "C:\log.txt"
$tmp = "C:\tmp.txt"
Clear-Content $log
Clear-Content $tmp
#'
<requestId>qwerty-qwer12-qwer56</requestId>qwertykeyId>Qwd84lPhjutf7Nmwr56hJndcsjy34imNQwd84lPhjutZ7Nmwr56hJndcsjy34imNPozDr5</ABC reportId>poGd56Hnm9q3Dfer6Jh</msg:reportId>
<requestId>zxcvbn-zxcv12-zxcv56</requestId>
<requestId>qwerty-qwer12-qwer56</requestId>abcde reportId>plmkjh8765FGH4rt6As</msg:reportId>
<requestId>1234qw-12qw12-12qw56</requestId>
keyId>Qwd84lPhjutf7Nmwr56hJndcsjy34imNQwd84lPhjutZ7Nmwr56hJndcsjy34imNPozDr5</
keyId>Qwd84lPhjutf7Nmwr56hJndcsjy34imNQwd84lPhjutZ7Nmwr56hJndcsjy34imNPozDr5</
keyId>Zdjgi76Gho3sQw0ib5Mjk3sDyoq9zmGdZdjgi76Gho3sQw0ib5Mjk3sDyoq9zmGdLkJpQw</
reportId>plmkjh8765FGH4rt6As</msg:reportId>
reportId>plmkjh8765FGH4rt6As</msg:reportId>
reportId>poGd56Hnm9q3Dfer6Jh</msg:reportId>
'# | Set-Content $log -Encoding UTF8
$requestId = #{
Count = 1
Matches = #()
}
$keyId = #{
Count = 1
Matches = #()
}
$reportId = #{
Count = 1
Matches = #()
}
$output = switch -Regex -File $log {
'(\w{6}-\w{6}-\w{6})' {
if(!$requestId.matches.($matches.1))
{
$req = $requestId.matches += #{$matches.1 = "RequestId-$($requestId.count)"}
$requestId.count++
$req.keys | %{ Add-Content $tmp "$_ : $($req.$_)" }
}
$_ -replace $matches.1,$requestId.matches.($matches.1)
}
'keyId>(\w{70})</' {
if(!$keyId.matches.($matches.1))
{
$kid = $keyId.matches += #{$matches.1 = "keyId-$($keyId.count)"}
$keyId.count++
$kid.keys | %{ Add-Content $tmp "$_ : $($kid.$_)" }
}
$_ -replace $matches.1,$keyId.matches.($matches.1)
}
'reportId>(\w{19})</msg:reportId>' {
if(!$reportId.matches.($matches.1))
{
$repid = $reportId.matches += #{$matches.1 = "Report-$($reportId.count)"}
$reportId.count++
$repid.keys | %{ Add-Content $tmp "$_ : $($repid.$_)" }
}
$_ -replace $matches.1,$reportId.matches.($matches.1)
}
default {$_}
}
$output | Set-Content $log -Encoding UTF8
Get-Content $log
Get-Content $tmp
If you don't care about the order in which they were found, which I assume you wouldn't if you don't want duplicates, just export them all at the end. I would still keep them in an "object" form so you can easily import/export them. Csv would be an ideal candidate for the data.
$requestId,$keyid,$reportid | Foreach-Object {
foreach($key in $_.matches.keys)
{
[PSCustomObject]#{
Original = $key
Replacement = $_.matches.$key
}
}
}
The data output to console for this example
Original Replacement
-------- -----------
qwerty-qwer12-qwer56 RequestId-1
zxcvbn-zxcv12-zxcv56 RequestId-2
1234qw-12qw12-12qw56 RequestId-3
Qwd84lPhjutf7Nmwr56hJndcsjy34imNQwd84lPhjutZ7Nmwr56hJndcsjy34imNPozDr5 keyId-1
Zdjgi76Gho3sQw0ib5Mjk3sDyoq9zmGdZdjgi76Gho3sQw0ib5Mjk3sDyoq9zmGdLkJpQw keyId-2
poGd56Hnm9q3Dfer6Jh Report-1
plmkjh8765FGH4rt6As Report-2
Just pipe it into Export-Csv
$requestId,$keyid,$reportid | Foreach-Object {
foreach($key in $_.matches.keys)
{
[PSCustomObject]#{
Original = $key
Replacement = $_.matches.$key
}
}
} | Export-Csv $tmp -NoTypeInformation

Compare Multiple Spreadsheets and Produce CSV output

I am trying to compare two csv files, the first is called tickets.csv and contains the column headers: "tcn", "two", "twod". The second file is called tracker.csv and contains the headers: "cn", "wo", and "wod". I would like to output the contents of tracker.csv, and if any "tcn" (tickets.csv) match "cn" (tracker.csv), then write its associated "two" (tickets.csv) to "wo" (tracker.csv), and "twod" (tickets.csv) to "wod" (tracker.csv).
I'm running into the issue where my code isn't moving the "two" or "wod", and its also writing each object 4 times.
Input CSVs
Import-Csv "$home\Desktop\nestTest\tracker.csv" |
ForEach-Object {
$machine = $_.machine
$wo = ""
$wod = ""
$tickets = import-csv "$home\Desktop\nestTest\tickets.csv"
foreach ($ticket in $tickets) {
try {
$tcn = ""
$two = ""
$twod = ""
$two = $_.wo
$twod = $_.wod
$tcn = $_.cn
if ($tcn -eq $machine) {
$wo = $two
$wod = $twod
}
else {
$wo = ""
$wod = ""
}
}
catch {
}
[pscustomobject]#{
"Machine Name"=$machine
"Work Order #"=$wo
"Work Order Date"=$wod
}
}
} |
select "Machine Name","Work Order #","Work Order Date" |
Export-Csv "$home\Desktop\nestTest\output.csv"
I was looking in the wrong direction before. But I figured it out.
$tracker = Import-Csv "$home\Desktop\nestTest\tracker.csv" |
ForEach-Object {
$machine = ""
$wo = ""
$wod = ""
$machine = $_.machine
$tickets = import-csv "$home\Desktop\nestTest\tickets.csv" |
ForEach-Object {
$two = ""
$twod = ""
$two = $_."wo"
$twod = $_."wod"
$tcn = $_."cn"
if ($tcn -eq $machine) {
$wo = $two
$wod = $twod
}
else {
}
}
[pscustomobject]#{
"Machine Name"=$machine
"Work Order #"=$wo
"Work Order Date"=$wod
}
} |
select "Machine Name","Work Order #","Work Order Date" |
Export-Csv "$home\Desktop\nestTest\output.csv" -NoTypeInformation

Powershell Convert a string to datatable

I have the below string which is returned from an Invoke-RestMethod call. How can I transform the results into an object or data table to be able to use it? The first row would be the properties.
I tried the Out-File and Export-CSV first but the only length is added to the file.
Then I tried the format table which did nothing.
Any ideas?
Name,Main No, Sec No
Alex,34.6743,22.7800
Tom,33.8798,21.9098
Tim,34.6743,41.7800
Mark,33.8798,21.9098
The ConvertFrom-Csv cmdlet is smart enough to exclude the empty lines we see in your example.
To output as CSV, simply use:
$data | ConvertFrom-Csv
#output to csv file:
$data | ConvertFrom-Csv | Export-Csv -Path 'D:\test.csv' -NoTypeInformation
Your CSV file will look like this:
"Name","Main No","Sec No"
"Alex","34.6743","22.7800"
"Tom","33.8798","21.9098 "
"Tim","34.6743","41.7800"
"Mark","33.8798","21.9098"
If you want it converted to datatable, you can use below function:
function ConvertTo-DataTable {
[CmdletBinding()]
Param(
[Parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)]
[PSObject[]]$InputObject
)
Begin {
$dataTable = New-Object System.Data.DataTable
$first = $true
function _GetSafeTypeName($type) {
# internal helper function to return the correct typename for a datatable
$types = #('System.Boolean', 'System.Byte', 'System.SByte', 'System.Char', 'System.Datetime',
'System.TimeSpan', 'System.Decimal', 'System.Double', 'System.Guid', 'System.Single')
$ints = #('System.Int16', 'System.Int32', 'System.Int64')
$uints = #('System.UInt16', 'System.UInt32', 'System.UInt64')
if ($types -contains $type) { return "$type" }
# if the type is Int or UInt, always return the largest variety
if ($ints -contains $type) { return 'System.Int64' }
if ($uints -contains $type) { return 'System.UInt64' }
return 'System.String'
}
}
Process {
foreach ($object in $InputObject) {
$dataRow = $dataTable.NewRow()
foreach($property in $object.PSObject.Properties) {
# read the data type for this property and make sure it is a valid type for a DataTable
$dataType = _GetSafeTypeName $property.TypeNameOfValue
# ensure the property name does not contain invalid characters
$propertyName = $property.Name -replace '[\W\p{Pc}-[,]]', '_' -replace '_+', '_'
if ($first) {
$dataColumn = New-Object System.Data.DataColumn $propertyName, $dataType
$dataTable.Columns.Add($dataColumn)
}
if ($property.Gettype().IsArray -or ($property.TypeNameOfValue -like '*collection*')) {
$dataRow.Item($propertyName) = $property.Value | ConvertTo-XML -As String -NoTypeInformation -Depth 1
}
else {
$value = if ($null -ne $property.Value) { $property.Value } else { [System.DBNull]::Value }
$dataRow.Item($propertyName) = $value -as $dataType
}
}
$dataTable.Rows.Add($dataRow)
$first = $false
}
}
End {
Write-Output #(,($dataTable))
}
}
Then use it like this:
$data | ConvertFrom-Csv | ConvertTo-DataTable
P.S. $data is the result of your Invoke-RestMethod call.