PowerShell DataTable removing empty columns logic incorrect - powershell

This is the first time that I'm writing a function that can call itself. I'm trying to remove all the columns of a DataTable where the rows are empty.
The code works fine but spits out some errors. For one reason or another it's looping one last time through the for loop with the old $Columns number still in memory. Although I'm filling it again each time the function is called... I don't understand why..
The code:
Function Remove-EmptyColumns {
$Columns = $DataTable.Columns.Count
$Rows = $DataTable.Rows.Count
for ($c = 0; $c -lt $Columns; $c++) {
$Empty = 0
for ($r = 0; $r -lt $Rows; $r++) {
if ($DataTable.Rows[$r].Item($c).ToString() -eq '') {
$Empty++
}
}
if ($Empty -eq $Rows) {
$DataTable.Columns.Remove($DataTable.Columns[$c])
Remove-EmptyColumns
}
}
}
Thank you for your help.

Why is it a recursive method? Try removing the Remove-EmptyColumns and it should work, no?
EDIT: Try
Function Remove-EmptyColumns {
$Columns = $DataTable.Columns.Count
$Rows = $DataTable.Rows.Count
$columnsToRemove = #()
for ($c = 0; $c -lt $Columns; $c++) {
$Empty = 0
for ($r = 0; $r -lt $Rows; $r++) {
if ($DataTable.Rows[$r].Item($c).ToString() -eq '') {
$Empty++
}
}
if ($Empty -eq $Rows) {
$columnsToRemove.Add($DataTable.Columns[$c])
}
}
$columnsToRemove | ForEach-Object {
$DataTable.Columns.Remove($_) }
}

Just a thought, but I like to use the String static method of IsNullOrEmpty.
if ([string]::IsNullOrEmpty($DataTable.Rows[$r].Item($c).ToString()))
{ ... }
This removes issues where the value may not equal '' but be $Null instead and it does it in a single line.

Related

speed up inserting data from Data Set to SQL powershell

When I run this script to insert data into table in SQL from DataSet, the process takes a long time to insert it (row by row) .the inserting time depend on the row counts , more rows more time .
is there any way to speed up the process or split the rows to multiple records and insert it parallel on same time .
for ( $i = 0; $i -lt $DataSet.Tables[0].Rows.Count; $i++) {
try {
$valuestr = New-Object -TypeName System.Text.StringBuilder
for ( $x = 0; $x -lt 11; $x++) {
if ($x -lt 10) {
[void]$valuestr.Append("'" + $DataSet.Tables[0].Rows[$i][$x].ToString().Trim().Replace("'", "/") + "',")
}
else {
[void]$valuestr.Append("'" + $DataSet.Tables[0].Rows[$i][$x].ToString().Trim().Replace("'", "/") + "'")
}
}
[string]$inputstr = $valuestr.ToString()
[char[]]$values = $inputstr.ToCharArray()
[string]$output = ''
foreach ($letter in $values) {
[int]$value = [Convert]::ToInt32($letter)
[string]$hexOutput = [String]::Format("{0:X}", $value)
switch ($hexOutput) {
"627" { $output += "ا"; Break }
default { $output += [Convert]::Tostring($letter); break }
}
}
$sqlCmdd.CommandText = "INSERT INTO $sqlTable ([ICN],[ICN_NODE],[PARTITION_ID],[ICN_CREATE_DT] ,[ICN_SEQ_NUM],[ICN_SUBNUM],[ICN_TAG_ID],[ICN_TAG_VER],[BLK_NUM],[BLK_LEN] ,[FREE_FORM_TXT]) Values (" + $output.ToString() + ")"
$sqlCmdd.ExecuteNonQuery()
}
catch { $_.Exception.Message | Out-File C:\log\log.txt -Append }
}

How to use powershell to reorder a string to obfuscate a hidden message?

Just for fun a friend and I are trying to find a creative way to send coded messages to eachother using steganography.I stumbled upon doing something like whats shown below and I have been struggling trying to write a function to automate the process.
this is a secret message
can be turned into:
("{2}{1}{0}{3}"-f'ecret m','is a s','this ','essage')
splitting the string and using reordering seems to be the way to go.
So the string needs to be split in random splits between 5-10 characters
.
The index of the original positions need to be saved
the splits need to be swapped around
and the new indexes sorted as to reorder the message properly
i've just really been struggling
help is appreciated
Just for fun .... 😉🤡
$InputMessage = 'this is a secret message'
$SplittedString = $InputMessage -split '' | Select-Object -Skip 1 | Select-Object -SkipLast 1
[array]::Reverse($SplittedString)
foreach ($Character in $SplittedString) {
if ($Character -notin $CharacterList) {
[array]$CharacterList += $Character
}
}
foreach ($Character in ($InputMessage -split '' | Select-Object -Skip 1 | Select-Object -SkipLast 1)) {
$Index = [array]::indexof($CharacterList, $Character)
$Output += "{$Index}"
}
$Result = "'$Output' -f $(($CharacterList | ForEach-Object {"'$_'"}) -join ',')"
$Result
And the output of this would be:
'{6}{10}{9}{3}{5}{9}{3}{5}{2}{5}{3}{0}{8}{7}{0}{6}{5}{4}{0}{3}{3}{2}{1}{0}' -f 'e','g','a','s','m',' ','t','r','c','i','h'
And the output of this would be:
this is a secret message
And now if you want to go fancy with it you remove the curly braces and the quotes and the commas and the -f and add only the numbers and characters to the data. ;-)
Not exactly what you're looking for but this might give you something to start with:
class Encode {
[string] $EncodedMessage
[int[]] $Map
[int] $EncodingComplexity = 3
Encode ([string] $Value) {
$this.Shuffle($Value)
}
Encode ([string] $Value, [int] $Complexity) {
$this.EncodingComplexity = $Complexity
$this.Shuffle($Value)
}
[void] Shuffle([string] $Value) {
$set = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!##$%^&*()_-+=[{]};:<>|./?'
$ref = [Collections.Generic.HashSet[int]]::new()
$ran = [random]::new()
$enc = [char[]]::new($Value.Length * $this.EncodingComplexity)
for($i = 0; $i -lt $enc.Length; $i++) {
$enc[$i] = $set[$ran.Next($set.Length)]
}
for($i = 0; $i -lt $Value.Length; $i++) {
do {
$x = $ran.Next($enc.Length)
} until($ref.Add($x))
$enc[$x] = $Value[$i]
}
$this.EncodedMessage = [string]::new($enc)
$this.Map = $ref
}
}
class Decode {
static [string] DecodeMessage ([Encode] $Object) {
return [Decode]::DecodeMessage($Object.EncodedMessage, $Object.Map, $Object.EncodingComplexity)
}
static [string] DecodeMessage ([string] $EncodedMessage, [int[]] $Map) {
return [Decode]::DecodeMessage($EncodedMessage, $Map, 3)
}
static [string] DecodeMessage ([string] $EncodedMessage, [int[]] $Map, [int] $Complexity) {
$decoded = [char[]]::new($EncodedMessage.Length / $Complexity)
for($i = 0; $i -lt $decoded.Length; $i++) {
$decoded[$i] = $EncodedMessage[$Map[$i]]
}
return [string]::new($decoded)
}
}
Encoding a message:
PS /> $message = 'this is a secret message'
PS /> $encoded = [Encode] $message
PS /> $encoded
EncodingComplexity EncodedMessage Map
------------------ -------------- ---
3 B$h^elu2w#CeeHH^qa siQJ)t}es:.a3 ema=eN(GiIcsO;tst1 .fsg}eSUk7ms4 N>rfe# {49, 2, 41, 27…}
For decoding the message you can either use the object of the type Encode or you can give your friend the Encoded Message and the Map to decode it ;)
PS /> [Decode]::DecodeMessage($encoded)
this is a secret message
PS /> [Decode]::DecodeMessage('B$h^elu2w#CeeHH^qa siQJ)t}es:.a3 ema=eN(GiIcsO;tst1 .fsg}eSUk7ms4 N>rfe#', $encoded.Map)
this is a secret message

PowerShell 5.0 Class Method Returns "Not all code path returns value within method"

As an experiment with PowerShell 5.0 classes I tried to translate JavaScript code for the Stable Marriage problem at Rosetta Code. It seemed very straight forward, but The second method (Rank) returns the error: Not all code path returns value within method.
class Person
{
# --------------------------------------------------------------- Properties
hidden [int]$CandidateIndex = 0
[string]$Name
[person]$Fiance = $null
[person[]]$Candidates = #()
# ------------------------------------------------------------- Constructors
Person ([string]$Name)
{
$this.Name = $Name
}
# ------------------------------------------------------------------ Methods
static [void] AddCandidates ([person[]]$Candidates)
{
[Person]::Candidates = $Candidates
}
[int] Rank ([person]$Person)
{
for ($i = 0; $i -lt $this.Candidates.Count; $i++)
{
if ($this.Candidates[$i] -eq $Person)
{
return $i
}
return $this.Candidates.Count + 1
}
}
[bool] Prefers ([person]$Person)
{
return $this.Rank($Person) -lt $this.Rank($this.Fiance)
}
[person] NextCandidate ()
{
if ($this.CandidateIndex -ge $this.Candidates.Count)
{
return $null
}
return $this.Candidates[$this.CandidateIndex++]
}
[int] EngageTo ([person]$Person)
{
if ($Person.Fiance)
{
$Person.Fiance.Fiance = $null
}
return $this.Fiance = $Person
}
[void] SwapWith ([person]$Person)
{
Write-Host ("{0} and {1} swap partners" -f $this.Name, $Person.Name)
$thisFiance = $this.Fiance
$personFiance = $Person.Fiance
$this.EngageTo($personFiance)
$Person.EngageTo($thisFiance)
}
}
The error is because if $this.Candidates.Count is 0, no return will execute.
Should the second return be outside of your for loop?
The current way, if it does not match the first candidate, it will return $this.Candidates.Count + 1.
[int] Rank ([person]$Person)
{
for ($i = 0; $i -lt $this.Candidates.Count; $i++)
{
if ($this.Candidates[$i] -eq $Person)
{
return $i
}
}
return $this.Candidates.Count + 1
}

IndexOutOfRange

I'm getting this error:
Array assignment failed because index '3' was out of range.
At Z:\CSP\deploys\aplicacional\teste.ps1:71 char:12
+ $UNAME[ <<<< $i]= $line
+ CategoryInfo : InvalidOperation: (3:Int32) [], RuntimeException
+ FullyQualifiedErrorId : IndexOutOfRange
I really can't find why the index end there.
$CSNAME = #(KPScript -c:GetEntryString $PASSHOME\$PASSFILE -pw:$PASS -Field:csname $SEARCH)
$UNAME = #()
$i = 0
Write-Host "Length="$CSNAME.Length
while($i -le $CSNAME.Length)
{
Write-Host "Start "$i
#$CSNAME[$i].GetType()
if ($CSNAME[0].StartsWith("OK:")) {
Write-Host "ACES $ACES does not exist" -Foreground "red"
}
if ($CSNAME[$i].StartsWith("OK:")) {
break
}
Write-Host "CSNAME="$CSNAME[$i]
$UNAME = $UNAME + $i
$UNAME = KPScript -c:GetEntryString $PASSHOME\$PASSFILE -pw:$PASS -Field:UserName -ref-csname:$CSNAME[$i]
foreach ($line in $UNAME) {
if (! ($line.StartsWith("OK:"))) {
Write-Host $i
$UNAME = $UNAME + $i
Write-Host "uname var"$i
$UNAME[$i] = $line
} else {
Write-Host "break"
break
}
}
#$UNAME[$i].GetType()
#if ($UNAME[$i].StartWith("OK:*")){
# break
#}
Write-Host "UNAME="$UNAME[$i]
#$UNAME[$i]
Write-Host "End "$i
$i += 1
Write-Host "switch"
}
Since the second while is based in the first array length and it has values, why is the it getting out of range?
PowerShell arrays are zero-based, so an array of length 3 has index values from 0 through 2. Your code, however, would iterate from 0 to 3, because the loop condition checks if the variable is less or equal the length (-le):
while($i -le $CSNAME.Length)
{
...
}
You need to check if the variable is less than the length (or less or equal the length minus one):
while($i -lt $CSNAME.Length)
{
...
}
Also, you'd normally use a for loop for iterating over an array, so you can handle the index variable in one place:
for ($i=0; $i -lt $CSNAME.Length; $i++) {
...
}
Edit: You initialize $UNAME as an array, but inside the loop you assign $UNAME = KPScript ..., which replaces the array with whatever the script returns (another array, a string, $null, ...). Don't use the same variable for different things in a loop. Assign the script output to a different variable. Also, your way of appending to the array is rather convoluted. Instead of $UNAME = $UNAME + $i; $UNAME[$i] = $line simply do $UNAME += $line.
$res = KPScript -c:GetEntryString $PASSHOME\$PASSFILE -pw:$PASS -Field:UserName -ref-csname:$CSNAME[$i]
foreach ($line in $res) {
if (! ($line.StartsWith("OK:"))) {
$UNAME += $line
} else {
break
}
}

Method returning string but is assigned as string[]

I have this code:
[string]$emailBody = getEmailBody $firstName $emailTemplateFileContent
function getEmailBody($firstName, $emailTemplateFileContent)
{
$sb = New-Object Text.StringBuilder
for ($i = 1; $i -lt $emailTemplateFileContent.Length; $i++)
{
$sb.AppendLine($emailTemplateFileContent[$i])
}
$emailTemplateText = $sb.ToString()
$emailTemplateTextCustomised = $emailTemplateText.Replace("#name", $firstName)
return $emailTemplateTextCustomised
}
When I type $emailTemplateTextCustomised.getType() I can see that it is a string.
However when I type $emailBody.getType() I can see that it is an Array.
I can also see that the array has 8 strings, each string containing the output from getEmailBody().
UPDATE:
Powershell seems really buggy, it is no longer a String[] but just a String with 8 repetitions of the output from getEmailBody().
Why is it doing this?
Thanks in advance.
PowerShell isn't buggy, well at least not in this case. :-) You have to understand that in PowerShell the "output" of a function is anything that is not captured to a variable. The line that does the StringBuilder.AppendLine() returns the StringBuilder and that is added to the output of your function. Try this:
function getEmailBody($firstName, $emailTemplateFileContent)
{
$sb = New-Object Text.StringBuilder
for ($i = 1; $i -lt $emailTemplateFileContent.Length; $i++)
{
$sb.AppendLine($emailTemplateFileContent[$i]) > $null
}
$emailTemplateText = $sb.ToString()
$emailTemplateText.Replace("#name", $firstName)
}
If you are on V3 (maybe V2) you can use the -replace operator as well:
function getEmailBody($firstName, $emailTemplateFileContent)
{
$sb = New-Object Text.StringBuilder
for ($i = 1; $i -lt $emailTemplateFileContent.Length; $i++)
{
$sb.AppendLine($emailTemplateFileContent[$i]) > $null
}
$emailTemplateText = $sb.ToString() -replace '#name',$firstName
$emailTemplateText
}