Sort hashtable and put into a new hashtable - powershell

I cannot figure out why the loop below is return null keys.
I think $hashSorted is not of type Hashtable; so how do I force it to be?
Thinking now I need a deep/copy? I just want to sort my hashtable and put it into another hashtable.
This might be a dup of: Sort Hashtable and assign it to a new variable in Powershell
My original questiion was why the keys were null when doing the loop.
Then I realized it probably wasn't a hashtable. Still trying the answers there.
Reference: How can I enumerate a hashtable as key-value pairs / filter a hashtable by a collection of key values
cls
$hashFilesAndSizes = #{}
$hashSorted = #{}
$hashFilesAndSizes.Add("file1.txt",1000)
$hashFilesAndSizes.Add("file2.txt",200)
$hashFilesAndSizes.Add("file3.txt",750)
$hashFilesAndSizes.GetEnumerator() | sort value #Displays sorted hash table
Write-Host "Second Try - put in another hashtable"
$hashSorted = $hashFilesAndSizes.GetEnumerator() | sort value #put into a new variable
Write-Host "Original variable in original Order"
$hashFilesAndSizes
Write-Host "Sorted"
$hashSorted #show results
Write-Host "Why loop has null keys?"
foreach($key in $hashSorted.Keys)
{
Write-Host "Key=$key"
#if (-not ([string]::IsNullOrEmpty($key)))
#{
$keyPadded = $key.PadRight(50," ")
$fileSize = $hashSorted[$key]
$fileSizeFormatted = $fileSize.ToString("000,000")
Write-Host "$keyPadded size=$fileSizeFormatted "
#}
}
Write-Host "Test with enumerator"
foreach($item in $hashSorted.GetEnumerator())
{
$key = $hashSorted.Key
Write-Host "Key=$key"
#if (-not ([string]::IsNullOrEmpty($key)))
#{
$keyPadded = $key.PadRight(50," ")
$fileSize = $hashSorted.Value
$fileSizeFormatted = $fileSize.ToString("000,000")
Write-Host "$keyPadded size=$fileSizeFormatted "
#}
}
Results:
> Name Value
>
> ---- ----- file2.txt 200
> file3.txt 750
> file1.txt 1000
> Second Try - put in another hashtable Original variable in original
> Order file3.txt 750
> file1.txt 1000
> file2.txt 200
> Sorted file2.txt 200
> file3.txt 750
> file1.txt 1000
> Why loop has null keys? Key= You cannot call a method on a null-valued
> expression. At C:\Scripts\HashTableSortTest.ps1:23 char:37
> + $keyPadded = $key.PadRight <<<< (50," ")
> + CategoryInfo : InvalidOperation: (PadRight:String) [], RuntimeException
> + FullyQualifiedErrorId : InvokeMethodOnNull Index operation failed; the array index evaluated to null. At
> C:\Scripts\HashTableSortTest.ps1:24 char:35
> + $fileSize = $hashSorted[ <<<< $key]
> + CategoryInfo : InvalidOperation: (:) [], RuntimeException
> + FullyQualifiedErrorId : NullArrayIndex You cannot call a method on a null-valued expression. At
> C:\Scripts\HashTableSortTest.ps1:25 char:50
> + $fileSizeFormatted = $fileSize.ToString <<<< ("000,000")
> + CategoryInfo : InvalidOperation: (ToString:String) [], RuntimeException
> + FullyQualifiedErrorId : InvokeMethodOnNull
> size=
>Test with enumerator Key= You cannot call a method on a null-valued expression. At C:\Scripts\HashTableSortTest.ps1:38 char:37
> + $keyPadded = $key.PadRight <<<< (50," ")
> + CategoryInfo : InvalidOperation: (PadRight:String) [], RuntimeException
> + FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At
> C:\Scripts\HashTableSortTest.ps1:40 char:50
> + $fileSizeFormatted = $fileSize.ToString <<<< ("000,000")
> + CategoryInfo : InvalidOperation: (ToString:String) [], RuntimeException
> + FullyQualifiedErrorId : InvokeMethodOnNull
> size= Key= You cannot call a method on a null-valued expression. At C:\Scripts\HashTableSortTest.ps1:38 char:37
> + $keyPadded = $key.PadRight <<<< (50," ")
> + CategoryInfo : InvalidOperation: (PadRight:String) [], RuntimeException
> + FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At
> C:\Scripts\HashTableSortTest.ps1:40 char:50
> + $fileSizeFormatted = $fileSize.ToString <<<< ("000,000")
> + CategoryInfo : InvalidOperation: (ToString:String) [], RuntimeException
> + FullyQualifiedErrorId : InvokeMethodOnNull
> size= Key= You cannot call a method on a null-valued expression. At C:\Scripts\HashTableSortTest.ps1:38 char:37
> + $keyPadded = $key.PadRight <<<< (50," ")
> + CategoryInfo : InvalidOperation: (PadRight:String) [], RuntimeException
> + FullyQualifiedErrorId : InvokeMethodOnNull You cannot call a method on a null-valued expression. At
> C:\Scripts\HashTableSortTest.ps1:40 char:50
> + $fileSizeFormatted = $fileSize.ToString <<<< ("000,000")
> + CategoryInfo : InvalidOperation: (ToString:String) [], RuntimeException
> + FullyQualifiedErrorId : InvokeMethodOnNull
> size=

This
$hashSorted = $hashFilesAndSizes.GetEnumerator() | sort value
does not produce a new hashtable. Instead, it produces an array of DictionaryEntry objects. Now since $hashSorted is, in fact, not a hashtable, there is no Keys property and indexing by value won't work. To properly construct a new hashtable that preserves key order you have to do
$hashSorted = [ordered] #{}
$hashFilesAndSizes.GetEnumerator() | sort value | foreach {$hashSorted[$_.Key] = $_.Value}

Try to run in new clear powershell context. I suggest your environment has some variables saved in cache.
The only error I see is in the second loop. You're using $hashSorted inside the loop instead of $item. Should be:
$key = $item.Key
....
$fileSize = $item.Value
EDIT: Btw, sorting with GetEnumerator() works just fine.
EDIT 2: $hashSorted.Keys is empty as $hashSorted is not a hashtable, just a collection of key-value pairs.

Write-Host "Test with enumerator"
foreach($item in $hashSorted.GetEnumerator())
{
$key = $item.Key # Not $hashSorted.Key !
Write-Host "Key=$key"
#if (-not ([string]::IsNullOrEmpty($key)))
#{
$keyPadded = $key.PadRight(50," ")
$fileSize = $item.Value # Not $hashSorted.Value !
$fileSizeFormatted = $fileSize.ToString("000,000")
Write-Host "$keyPadded size=$fileSizeFormatted "
#}
}

Related

Update global variable from workflow

function function1(){
Param($a)
"YOU HAVE entered : $a"
$arr+=$a
$arr2.Add($a)
}
workflow wf{
Param($b)
Parallel{
sequence{
function1 $b
}
}
}
$arr = #()
$arr2 = [System.Collections.Arraylist]#()
wf -b 10
$arr
$arr2
The output I am getting as below
YOU HAVE entered : 10
Microsoft.PowerShell.Utility\Write-Error : You cannot call a method on a null-valued expression.
At wf:12 char:12
+
+ CategoryInfo : NotSpecified: (:) [Write-Error], RemoteException
+ FullyQualifiedErrorId : System.Management.Automation.RemoteException,Microsoft.PowerShell.Commands.WriteErrorCommand
+ PSComputerName : [localhost]
I am unable to update both the arrays which I declared outside the function and workflow.

Recursively Changing File extensions from .docx and .pdf to .txt

$findPDF = Get-ChildItem -Path "$fileDrive" -Filter *.pdf -r
$findDOCX = Get-ChildItem -Path "$fileDrive" -Filter *.docx -r
$pullFiles += $findPDF
$pullFiles += $findDOCX
#[array]$pullFiles
#$pullFiles.length
$holdPath = #()
for($i = 0; $i -lt $pullFiles.length; $i++){
#get the full path of each document
$fullPath = Resolve-Path $pullFiles.fullname[$i]
#stores the information in a global array
$holdPath += $fullPath.path
}
#$holdPath
<#
.DESCRIPTION Uses the word.APPLICATION object to open and convert the word documents into .txt.
#>
#https://stackoverflow.com/questions/13402898/how-can-i-use-powershell-to-save-as-a-different-file-extension
#wdFormatDOSTextLineBreaks 5 Microsoft DOS text with line breaks preserved.
foreach($fi in $holdPath){
$Doc = $word.Documents.Open($fi.name)
$NameDOCX = ($Doc.name).replace("docx","txt")
$Doc.saveas([ref] $NameDOCX, [ref] 5)
$NamePDF = ($Doc.name).replace("pdf","txt")
$Doc.saveas([ref] $NamePDF, [ref] 5)
$Doc.close()
}
The problem Statement
The program needs to get any pdf and doc/x file and convert it to a .txt file. Now, I am able to recursively search and pull all .docx and .pdf documents from the file system. Now, I just need to convert them.
The Error
You cannot call a method on a null-valued expression.
At C:\Users\p617824\Documents\files\powershell\fileExtRename.ps1:38 char:2
+ $Doc = $word.Documents.Open($fi.name)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At C:\Users\p617824\Documents\files\powershell\fileExtRename.ps1:40 char:2
+ $NameDOCX = ($Doc.name).replace("docx","txt")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
[ref] cannot be applied to a variable that does not exist.
At C:\Users\p617824\Documents\files\powershell\fileExtRename.ps1:41 char:2
+ $Doc.saveas([ref] $NameDOCX, [ref] 5)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (NameDOCX:VariablePath) [], RuntimeException
+ FullyQualifiedErrorId : NonExistingVariableReference
You cannot call a method on a null-valued expression.
At C:\Users\p617824\Documents\files\powershell\fileExtRename.ps1:43 char:2
+ $NamePDF = ($Doc.name).replace("pdf","txt")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
[ref] cannot be applied to a variable that does not exist.
At C:\Users\p617824\Documents\files\powershell\fileExtRename.ps1:44 char:2
+ $Doc.saveas([ref] $NamePDF, [ref] 5)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (NamePDF:VariablePath) [], RuntimeException
+ FullyQualifiedErrorId : NonExistingVariableReference
You cannot call a method on a null-valued expression.
At C:\Users\p617824\Documents\files\powershell\fileExtRename.ps1:46 char:2
+ $Doc.close()
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
$word variable are not initialized, and your are complicated for Nothing (without offense you). Modify all your script like this :
$fileDrive ="C:\temp"
$word = new-object -ComObject Word.Application
Get-ChildItem -Path $fileDrive -file -r -Include "*.docx", "*.pdf" | %{
$Doc = $word.Documents.Open($_.FullName)
$NameDOC = $_.FullName.replace(".docx",".txt").replace(".pdf",".txt")
$Doc.saveas([ref] $NameDOC, [ref] 5)
$Doc.close()
}
$word.Quit()
But i have doubt on convert pdf to .txt with Word application like this... I think you should use itextsharp libray like here

Powershell won't read header text in word documents?

I am in need of checkingh a larger number of word documents (doc & docx) for a specific text and found a great tutorial and script by the Scripting Guys;
https://blogs.technet.microsoft.com/heyscriptingguy/2012/08/01/find-all-word-documents-that-contain-a-specific-phrase/
The script reads all documents in a directory and gives the following output;
Number of times mentioned
Total word count in all documents where the specific text is found
The directory of all files containing the specific text.
This is all I need, however their code doesn't seem to actually check the headers of any document, which incidentally is where the specific text I'm looking for is located. Any tips & tricks in making the script read header text would make me very happy.
An alternative solution might be to remove the formatting so that the header text becomes part of the rest of the document? Is this possible?
Edit: Forgot to link the script:
[cmdletBinding()]
Param(
$Path = "C:\Users\use\Desktop\"
) #end param
$matchCase = $false
$matchWholeWord = $true
$matchWildCards = $false
$matchSoundsLike = $false
$matchAllWordForms = $false
$forward = $true
$wrap = 1
$application = New-Object -comobject word.application
$application.visible = $False
$docs = Get-childitem -path $Path -Recurse -Include *.docx
$findText = "specific text"
$i = 1
$totalwords = 0
$totaldocs = 0
Foreach ($doc in $docs)
{
Write-Progress -Activity "Processing files" -status "Processing $($doc.FullName)" -PercentComplete ($i /$docs.Count * 100)
$document = $application.documents.open($doc.FullName)
$range = $document.content
$null = $range.movestart()
$wordFound = $range.find.execute($findText,$matchCase,
$matchWholeWord,$matchWildCards,$matchSoundsLike,
$matchAllWordForms,$forward,$wrap)
if($wordFound)
{
$doc.fullname
$document.Words.count
$totaldocs ++
$totalwords += $document.Words.count
} #end if $wordFound
$document.close()
$i++
} #end foreach $doc
$application.quit()
"There are $totaldocs and $($totalwords.tostring('N')) words"
#clean up stuff
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($range) | Out-Null
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($document) | Out-Null
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($application) | Out-Null
Remove-Variable -Name application
[gc]::collect()
[gc]::WaitForPendingFinalizers()
EDIT 2: My colleague got the idea to call on the section header instead;
Foreach ($doc in $docs)
{
Write-Progress -Activity "Processing files" -status "Processing $($doc.FullName)" -PercentComplete ($i /$docs.Count * 100)
$document = $application.documents.open($doc.FullName)
# Load first section of the document
$section = $doc.sections.item(1);
# Load header
$header = $section.headers.Item(1);
# Set the range to be searched to only Header
$range = $header.content
$null = $range.movestart()
$wordFound = $range.find.execute($findText,$matchCase,
$matchWholeWord,$matchWildCards,$matchSoundsLike,
$matchAllWordForms,$forward,$wrap,$Format)
if($wordFound) [script continues as above]
But this is met with the following errors:
You cannot call a method on a null-valued expression.
At C:\Users\user\Desktop\count_mod.ps1:27 char:31
+ $section = $doc.sections.item <<<< (1);
+ CategoryInfo : InvalidOperation: (item:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At C:\Users\user\Desktop\count_mod.ps1:29 char:33
+ $header = $section.headers.Item <<<< (1);
+ CategoryInfo : InvalidOperation: (Item:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At C:\Users\user\Desktop\count_mod.ps1:33 char:26
+ $null = $range.movestart <<<< ()
+ CategoryInfo : InvalidOperation: (movestart:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At C:\Users\user\Desktop\count_mod.ps1:35 char:34
+ $wordFound = $range.find.execute <<<< ($findText,$matchCase,
+ CategoryInfo : InvalidOperation: (execute:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Is this the right way to go or is it a dead end?
if you want the header text, you can try the following:
$document.content.Sections.First.Headers.Item(1).range.text
For anyone looking at this question in the future: Something isn't quite working with my code above. It seems to return a false positive and puts $wordFound = 1 regardless of the content of the document thus listing all documents found under $path.
Editing the variables within Find.Execute doesn't seem to change the outcome of $wordFound. I believe the problem might be found in my $range, as it is the only place I get errors in while going through the code step by step.
Errors listed;
You cannot call a method on a null-valued expression.
At C:\Users\user\Desktop\Powershell\count.ps1:24 char:58
+ $range = $document.content.Structures.First.Headers.Item <<<< (1).range.Text
+ CategoryInfo : InvalidOperation: (Item:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Exception calling "MoveStart" with "0" argument(s): "The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)"
At C:\Users\user\Desktop\Powershell\count.ps1:25 char:26
+ $null = $range.MoveStart <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodCOMException
You cannot call a method on a null-valued expression.
At C:\Users\user\Desktop\Powershell\count.ps1:26 char:34
+ $wordFound = $range.Find.Execute <<<< ($findText,$matchCase,
+ CategoryInfo : InvalidOperation: (Execute:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Remove a PowerShell session variable?

I'm attempting to set and clear a session variable in a PowerShell module:
function Set-Variable
{
[CmdletBinding()]
param(
[string]$Value = $PSCmdlet.SessionState.PSVariable.Get('Value').Value
)
# if `Value` not supplied on the command line and not available in the session, prompt for it
if (!$PSCmdlet.SessionState.PSVariable.Get('Value') -And !$Value) {
$Value = Read-Host "Value"
}
# if `Value` has been supplied on the command line, save it in the session
if ($Value) {
$PSCmdlet.SessionState.PSVariable.Set('Value',$Value)
}
}
Export-ModuleMember Set-Variable
function Remove-Variable {
$PSCmdlet.SessionState.PSVariable.Remove('Value')
# also throws an exeception
# $PSCmdlet.SessionState.PSVariable.Set('Value',$null)
}
Export-ModuleMember Remove-Variable
Setting the value works as expect, however, removing the variable or setting its value to null produces an error:
PS> Remove-Variable
You cannot call a method on a null-valued expression.At
C:\Users\XXXX\Documents\WindowsPowerShell\Modules\foo\foo.psm1:39 char:5
+ $PSCmdlet.SessionState.PSVariable.Remove('Value')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
and:
You cannot call a method on a null-valued expression.At
C:\Users\XXXX\Documents\WindowsPowerShell\Modules\foo\foo.psm1:37 char:5
+ $PSCmdlet.SessionState.PSVariable.Set('Value',$null)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Is there a way to do this?

Powershell for SharePoint >> getting we!rd error

I keep getting following error on this code:
#Setup default variables
$webUrl = Get-SPWeb -Identity "http://CiscoIntranet/sites/VOIP"
$list = $webUrl.GetList("http://CiscoIntranet/sites/VOIP/ForwardTech")
[System.Reflection.Assembly]::LoadWithPartialName(”Microsoft.SharePoint”)
function ProcessMove {
param($folderUrl)
$folder = $web.GetFolder($folderUrl)
foreach ($file in $folder.Files)
{
[Microsoft.SharePoint.SPFile]$spFile = $file;
$docset=$($file.Counterparty2);
$destinationFolderUrl = "http://CiscoIntranet/sites/VOIP/ForwardTech/" + $docset;
$spFile.MoveTo($destinationFolderUrl + $file.Name, $true);
$webUrl.Update();
}
}
#Move root Files
ProcessMove($list.RootFolder.Url)
You cannot call a method on a null-valued expression.
At C:\PS\MoveFiles.ps1:8 char:28
+ $folder = $web.GetFolder <<<< ($folderUrl)
+ CategoryInfo : InvalidOperation: (GetFolder:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Method invocation failed because [Microsoft.SharePoint.SPListItemCollection] doesn't contain a method named 'MoveTo'.
At C:\PS\MoveFiles.ps1:13 char:23
+ $list.Items.MoveTo <<<< ($destinationFolderUrl + $file.Name, $true);
+ CategoryInfo : InvalidOperation: (MoveTo:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
This is the working code...
$siteURL="http://CiscoIntranet/sites/VOIP"
$docLib = "ForwardTech"
$site=Get-SPSite $siteURL
$web=$site.RootWeb
$collFiles=$web.GetFolder($docLib).Files
$count=$collFiles.Count
while($count -ne 0)
{
$item = $collFiles[$count-1].Item
$DocSet = $item["Region"]
Write-Host "$DocSet is the doc set. $collFiles[$count-1].Name is name"
$collFiles[$count-1].MoveTo($siteURL + "/" + $docLib + "/" + $DocSet + "/" + $collFiles[$count-1].Name, $true)
$count--
}