r/PowerShell • • 11d ago

Question REPOST Issue with $PROFILE function

Hello everyone. I hope you are having a good time.

function gethash {

param([string]$string = "")

if ($string -eq [string]::Empty -or $string -isnot [string])

{

Write-Output "input not valid"

}

else

{

(Get-FileHash -algorithm sha256 $string).Hash | tee -Variable b

}

}  

I Have this function in my $Profile script. when i run gethash something it returns the hash string as expected, but when I try to print the variable containing the hash i just obtained from the piped command: tee, (alias of Tee-Object) with `$b

it prints nothing, the variable is not saved.

on the other hand, if i copy the exact same command from the $profile function directly through the terminal, $b prints what expected: the hash of the file.

I haven't used this function so much, but I'm pretty sure it worked before today.

Thanks in advance to any who will respond.

2 Upvotes

8 comments sorted by

6

u/BlackV 8d ago edited 7d ago

Why are you reposting? Your last post you were given the answers

Did they not work?
cause it does not look like you've updated your code based on those suggestions

Edit Oh.. wait this was from 3 days ago too (same as original)
Now I'm really confused as why

I guess top for op, scope issue, get hash already outputs object, out variable exists, parameter validation (cmdlet binding for good measure)

Those are where I'd look

1

u/skiThooly 7d ago

yeah I noticed that too, the code looks identical to the original post

1

u/BlackV 7d ago edited 7d ago

Yup, maybe it was automods the first time?

3

u/dodexahedron 7d ago

RE-ANSWER from the last time this was posted, when I said the following:

You are finding out about scope.

Variables in a script have script scope by default, no matter where they appear.

When you write it directly in the shell, script scope is your scope, so those variables stick around.

You would get the same effect if you dot-sourced the script file (which is actually how the profile is loaded at launch).

. SomeFile.ps1 is equivalent to pasting the contents of SomeFile.ps1 into the shell. That period makes all the difference (hence "dot-source").

When you run a script directly, it runs in a new child scope, which is its script scope.

Read up on the details at Get-Help about_Scopes

Note that dot-sourcing still runs the code you dot-sourced. But it will not be discarded after running. It is basically a means of appending a script to your shell session, which is itself one big script.

Regardless, your function should instead just write the value to the success stream, either with Write-Output $b or just $b at the end. Passing things by letting variables escape a script is a no-no. You can already tee it in the caller's pipeline if you need to tee it, or can use -PipelineVariable for what it looks like you're trying to do.

1

u/BetrayedMilk 7d ago

Scoping issue

1

u/Th3Sh4d0wKn0ws 7d ago

why repost what you were already given answers for?

1

u/jimb2 7d ago

If your input parameter is defined to be a string you will basically get a string in your function. If it is a different object it will convert to a string. There is no need to test if it is a string.

You do want to test that the file exists. This is the relevant test. You do need to handle an empty string as this will error on the path test.

You do need to clear any old result on error.

Use a global scope variable to save the result so it can be reused outside the function

function Get-Hash {
   param( [string] $FilePath )

   # check file exists
   if ( -not ( $FilePath -and ( Test-Path -LiteralPath $FilePath -PathType Leaf ) ) ) {
      Write-Host "ERROR: File not found [$FilePath]"
      $global:FileHash = ''  # clear any old result variable
      ''   # return an empty string (?)
      return
   }

   # Get the hash in a global variable
   $global:FileHash = (Get-FileHash -algorithm sha256 $FilePath).Hash
   Write-Host "File : $FilePath"
   # Write-Host "Hash : $FileHash" # either write or return the hash, not both
   $FileHash  # return the filehash
 }

 Set-Alias -Name hash -Value Get-hash  # optional abbreviated command

Depending on how you plan to use the function, you might vary the outputs and return value. It's normal to return the result but it might not be needed for a "command prompt" function if you save a global variable and write the output to screen.

Reusable functions would normally not write to the screen. You want to control output from your main script, not get random output from functions used the script.

-4

u/tommyfly 8d ago

This is from Gemini AI, so no guarantees, but I was not sure what the issue could be.

The Root Cause: PowerShell Scoping The reason $b is empty after calling gethash comes down to PowerShell variable scoping. When you run a function, PowerShell creates a temporary local scope for it. Inside your function, tee -Variable b (or Tee-Object -Variable b) creates and updates the variable $b only within that function's local scope. Once the function finishes executing, its local scope is destroyed, and $b disappears with it. When you ran the exact same command directly in your terminal, you were executing it in the global/session scope, which is why $b persisted there. Solutions Depending on how you want to use the hash, here are the best ways to fix or improve your function: Option 1: Save it to the Global Scope (If you want $b available everywhere) If you specifically want $b to be stored globally whenever you run the function, you can target the global scope explicitly:

function gethash { param([string]$string = "")

if ([string]::IsNullOrWhiteSpace($string)) {
    Write-Output "input not valid"
    return
}

# Assign and output using the global scope
$global:b = (Get-FileHash -Algorithm sha256 $string).Hash
$global:b

}

Option 2: Let standard pipeline assignment handle it (Recommended) Instead of forcing a global variable inside the function, it is usually cleaner to let the function return the value normally. This allows you to assign it to any variable name you want directly from the prompt:

function gethash { param([string]$string = "")

if ([string]::IsNullOrWhiteSpace($string)) {
    Write-Output "input not valid"
    return
}

(Get-FileHash -Algorithm sha256 $string).Hash

}

How to use it:

$b = gethash "path\to\file.txt"