How to call a function within & in powershell? - powershell

Suppose I have a function in test.ps1, its name is show the code is like below:
. "some_path\some_other_script.ps1"
function show {
write-host "Hello World"
#some other function call from some_other_script.ps1
...
}
How can I call show in follow format (in a & - call operator)
powershell.exe "& '%test_script_path%\test.ps1'\show"
I think I need to dot source test.ps1 first in order to get dependencies in show function from some_other_script.ps1
I know I can create a new script and put the code in show in the new script instead of in a function. In that way, when do &... the script will be invoked. But I don't want to create a new script just for a very simple function
Thanks for any suggestion

You could dot source it and then call it like this.
powershell -Command ". '%test_script_path%\toolsql.ps1'; show"
The ; is used as a separator between commands.

I am really appreciated for the help and time from #Patrick Meinecke, the following command works.
powershell -command ". .\test.ps1;show"
Like he mentioned in the chat,
"So to explain that
the .\ just indicates it's in the current directory
it still needed the other . to tell powershell to load the script into the current context"

Related

Is it possible to dot source a string variable in PowerShell?

I know I can dot source a file:
. .\MyFunctions.ps1
But, I would like to dot source the commands in a string variable:
. $myFuctions
I see that this is possible:
.{$x=2}
And $x equals 2 after the script block is sourced.
But... .{$myFunctions} does not work.
I tried $myFunctions | Invoke-Expression, but it doesn't keep the source function in the current scope. The closest I have been able to come up with is to write the variable to a temporary file, dot source the file, and then remove the file.
Inevitably, someone will ask: "What are you trying to do?" So here is my use case:
I want to obfuscate some functions I intend to call from another script. I don't want to obfuscate the master script, just my additional functions. I have a user base that will need to adjust the master script to their network, directory structure and other local factors, but I don't want certain functions modified. I would also like to protect the source code. So, an alternate question would be: What are some good ways to protect PowerShell script code?
I started with the idea that PowerShell will execute a Base64-encoded string, but only when passed on the command line with -EncodedCommand.
I first wanted to dot source an encoded command, but I couldn't figure that out. I then decided that it would be "obfuscated" enough for my purposes if I converted by Base64 file into a decode string and dot sourced the value of the string variable. However, without writing the decoded source to a file, I cannot figure out how to dot source it.
It would satisfy my needs if I could Import-Module -EncodedCommand .\MyEncodedFile.dat
Actually, there is a way to achieve that and you were almost there.
First, as you already stated, the source or dot operator works either by providing a path (as string) or a script block. See also: . (source or dot operator).
So, when trying to dot-source a string variable, PowerShell thinks it is a path. But, thanks to the possibility of dot-sourcing script blocks, you could do the following:
# Make sure everything is properly escaped.
$MyFunctions = "function Test-DotSourcing { Write-Host `"Worked`" }"
. { Invoke-Expression $MyFunctions }
Test-DotSourcing
And you successfully dot-sourced your functions from a string variable!
Explanation:
With Invoke-Expression the string is evaluated and run in the child scope (script block).
Then with . the evaluated expressions are added to the current scope.
See also:
Invoke-Expression
About scopes
While #dwettstein's answer is a viable approach using Invoke-Expression to handle the fact that the function is stored as a string, there are other approaches that seem to achieve the same result below.
One thing I'm not crystal clear on is the scoping itself, Invoke-Expression doesn't create a new scope so there isn't exactly a need to dot source at that point...
#Define your function as a string
PS> $MyUselessFunction = "function Test-WriteSomething { 'It works!' }"
#Invoke-Expression would let you use the function
PS> Invoke-Expression $MyUselessFunction
PS> Test-WriteSomething
It works!
#Dot sourcing works fine if you use a script block
PS> $ScriptBlock = [ScriptBlock]::Create($MyUselessFunction)
PS> . $ScriptBlock
PS> Test-WriteSomething
It works!
#Or just create the function as a script block initially
PS> $MyUselessFunction = {function Test-WriteSomething { 'It works!' }}
PS> . $MyUselessFunction
PS> Test-WriteSomething
It works!
In other words, there are probably a myriad of ways to get something similar to what you want - some of them documented, and some of them divined from the existing documentation. If your functions are defined as strings, then Invoke-Expression might be needed, or you can convert them into script blocks and dot source them.
At this time it is not possible to dot source a string variable.
I stand corrected! . { Invoke-Expression $MyFunctions } definitely works!

How can I pass unbound arguments from one script as parameters to another?

I have little experience with PowerShell in particular.
I'm trying to refactor some very commonly re-used code into a single script that can be sourced where it's needed, instead of copying and pasting this same code into n different scripts.
The scenario I'm trying to get looks (I think) like this:
#common.ps1:
param(
# Sure'd be great if clients didn't need to know about these
$some_params_here
...
)
function Common-Func-Uses-Params {
...
}
⋮
# foo/bar/bat.ps1:
# sure would love not to have to redefine all the common params() here...
. common.ps1 <pass-the-arguments>
Common-Func-Uses-Params $specific_Foo/Bar/Bat_Data
As the pseudo-comments above indicate, I've only been able to do this so far by capturing the params in the calling script as well.
I want to be in a situation where I can update the common code (say with a -Debug or -DryRun or -Url or whatever parameter) and not have to worry about updating all of the client code to match.
Is this possible?
You're missing two key things:
args - which captures all of (and only) the unbound arguments to the script
splatting (#) - which is used to pass arrays or hashtables to a command rather than flattening them like you'd get with $
When you combine these, you can easily pass all arguments onto another script, like so:
# foo.ps1
. common.ps1 #args
With a sourced file like this:
#common.ps1
param ([string]$foo = "foo")
echo "`$foo is $foo"
You get these output:
> foo.ps1 returns $foo is foo
> foo.ps1 -Foo bar returns $foo is bar
Note that, if you're trying to use the PowerShell ISE it might take you a while to figure this out or debug any of it. When you're in the debugger, both $args nor $MyInvocation.UnboundArguments will do their best to hide that information from you. They'll appear to be completely empty.
You can print the args with >> echo "$(#args)", but that also provides the very weird side effect of telling the Debugger to continue. I think the splatting is adding an extra newline and that's ending up in the Command Window.
The best workaround I have for that is to add $theargs = $args at the top of your script and remember to use $theargs in the debugger.

Powershell script how to access variable

I have a master script master.ps1 which executes two child ps1 scripts like below:
& "child1.ps1"
& "child2.ps1"
The problem I have now is at the end of master.ps1 script, based on certain variable values set in child1.ps1 and child2.ps1, I need to determine if I need to send an email or not.
Is there anyway this can be acheived? I'm using powershell 2.0
Thanks,
What you want to do is execute the child1.ps1 and child2.ps1 scripts in the scope of the calling script.
You can do this easily by dot-sourcing the scripts (. <command>), rather than using the call operator (& <command>):
. child1.ps1
. child2.ps1
# $a is now available

Batch file to set a PowerShell variable

For some reason i simply can't understand most of the sites who explain this question. So i'll try to ask here, if i'm am in the wrong place, just tell me in the comments and i'll put this in another forum and delete this question.
Let's say that i have 2 files, Batch.bat and PowerShell.ps1.
Batch.bat:
set A="ThisIsSuchVar!"
PowerShell.ps1:
$B = "Well, i don't know what to do here"
What can i do to the B variable be the same as the A variable?
Remember: I want the Batch variable to go to the PowerShell file. It's an one-way script. I want to use the built-in windows sources. And please, consider that i am a complete newbie in programming and don't speak english very well, so be the simplest possible, please.
In your batch file run.bat, set the environment variable A and run the PowerShell script:
set A=8
PowerShell.exe -File .\script.ps1
pause
In script.ps1, get the environment variable A, and assign its value to B:
$B=$Env:A
echo $B
When you run run.bat you get:
C:\Temp\try>set A=8
C:\Temp\try>PowerShell.exe -File .\script.ps1
8
C:\Temp\try>pause
Press any key to continue . . .

Powershell remove quotes when start process

Let's look at the code below
$SBK="0x1682CCD8 0x8A1A43EA 0xA532EEB6 0xECFE1D98"
./windows/nvflash/nvflash.exe --sbk 0x1682CCD8 0x8A1A43EA 0xA532EEB6 0xECFE1D98
./windows/nvflash/nvflash.exe --sbk "0x1682CCD8 0x8A1A43EA 0xA532EEB6 0xECFE1D98"
./windows/nvflash/nvflash.exe --sbk $SBK
I have define a string var $SBK and then I'm going to pass it for some app. The first process call is working properly. The second one fails and therefore application doesn't accepts quotes. But the third call is failed too with the same error. It seems that powershell passes quotes, those are causing errors. But how to eliminate them? Thanks beforehand.
Try doing
iex "./windows/nvflash/nvflash.exe --sbk $SBK"
Also, get echoargs.exe from PowerShell Community Extensions to see how args are passed from Powershell to commands etc.
You should probably not use Invoke-Expression (see This Post from the PowerShell Team)
Instead, you can create an array of arguments, and then pass them using the call operator. See this post of mine on the subject for more details.
In your case, it would look something like this:
$SBKArgs="--sbk", "0x1682CCD8", "0x8A1A43EA", "0xA532EEB6", "0xECFE1D98"
$cmd = Get-Command ./windows/nvflash/nvflash.exe
& $cmd $sbkargs
Hope this Helps