I am trying to get a value from an input box from a Powershell script back to a UI Path Sequence. I have created a simple sequence as an example. Yes, I know I could do this all in UI Path, I am just using an easy example as a way to try and test this for future use cases. Here is my sequence:
My text file from "Read text file" is:
$test = "Desktop/PassingArgs2of2.ps1 -Message foo"
Invoke-Expression -Command $test
The activity in UiPath looks like so:
The psCmd that I am running in the Invoke power shell activity looks like this:
Param(
[parameter(Mandatory=$true)]
[string]
$Message)
try{
$Global:fooVar = $null
function Test-InputBox(){
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
$msg = "fooMsg"
$title = "fooTitle"
$localtest = [Microsoft.VisualBasic.Interaction]::InputBox($msg, $title)
$Global:fooVar = $localtest.ToString()
}
Test-InputBox
}
catch{}
I tried setting fooVar equal to testLocal in the PowerShellVariables within Invoke power shell and then writing it, but that did not work.
Basically I want to get fooVar back into UI Path. Any help would be greatly appreciated. Thank you!
You're almost there. First, your Powershell script has to return a value. Take this for example:
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
$title = 'Your title goes here'
$msg = 'Your favorite color:'
$text = [Microsoft.VisualBasic.Interaction]::InputBox($msg, $title)
return $text
Here's the script in action (note that I called it twice and provided "red" the first time:
Then, just use this script directly in the Invoke Powershell activity. Note that the most important part here is the Output property - here, I decided to go for an array of strings. Naturally, as we only return a single value, you can just access the text provided by the user by accessing output(0).ToString().
I can write the following code in javascript:
function sum(num1, num2) {
return num1 + num2;
}
and then get a value
var someNum = sum(2,5);
I would like to do the same thing in Powershell, but I read the following guide:
PowerShell also knows the return keyword; however, it follows a
different logic. In general, the purpose of return is to end the
execution of a code section and to give the control back to the parent
block.
If you add a parameter to the return statement, the value will indeed
be returned to the calling subroutine. However, this also applies for
all other statements with an output. This means that any output
produced in the function will be stored in the variable together with
the return parameter.
I want to do this for the sake of having pure functions. However, it seems doing
var someNum = sum(2,5);
is entirely redundant, when I can just call the function above, define someNum inside of it, and it will be available in the global scope.
Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?
A bit tangential, but here is my actual code:
function GetPreviousKeyMD5Hashes() {
$query = "SELECT Name, MD5, executed FROM [AMagicDb].[dbo].cr_Scripts";
$command = New-Object System.Data.SQLClient.SQLCommand;
$command.Connection = $connection;
$command.CommandText = $query;
try {
$reader = $command.ExecuteReader();
while ($reader.Read()) {
$key = $reader.GetString(1)
$previousScripts.Add($key) | Out-Null
}
$reader.Close();
Write-Output "$(Get-Date) Finished querying previous scripts"
}
catch {
$exceptionMessage = $_.Exception.Message;
Write-Output "$(Get-Date) Error running SQL at with exception $exceptionMessage"
}
}
and then:
$previousScripts = New-Object Collections.Generic.HashSet[string];
GetPreviousKeyMD5Hashes;
This code isn't clear to me at all - running GetPreviousKeyMD5Hashes does set $previousScripts, but this is entirely unclear to whoever modifies this after me. My only other alternative (afaik) is to have all this in line, which also isn't readable.
is entirely redundant, when I can just call the function above, define someNum inside of it, and it will be available in the global scope.
No: functions execute in a child scope (unless you dot-source them with .), so variables created or assigned to inside a function are local to it.
Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?
Yes: The implicit output behavior only applies to statements whose output is neither captured - $var = ... - nor redirected - ... > foo.txt
If there are statements that happen to produce output that you'd like to discard, use $null = ... or ... > $null
Note: ... | Out-Null works in principle too, but will generally perform worse, especially in earlier PowerShell versions - thanks, TheIncorrigible1.
If there are status messages that you'd like to write without their becoming part of the output, use Write-Host or, preferably Write-Verbose or, in PSv5+, Write-Information, though note that the latter two require opt-in for their output to be visible in the console.
Do NOT use Write-Output to write status messages, as it writes to the success output stream, whose purpose is to output data ("return values").
See this answer of mine for more information about PowerShell's output streams.
The equivalent of your JavaScript code is therefore:
function sum($num1, $num2) {
Write-Host "Adding $num1 and $num2..." # print status message to host (console)
$num1 + $num2 # perform the addition and implicitly output result
}
PS> $someNum = sum 1 2 # NOTE: arguments are whitespace-separated, without (...)
Adding 1 and 2... # Write-Host output was passed through to console
PS> $someNum # $someNum captured the success output stream of sum()
3
Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?
You can't have your cake and eat it too...
If you have no out put in your function, then it is "pure" like you desire. If you have output, that also becomes part of the return.
You can use [ref] params. See below for example.
function DoStuff([ref]$refObj)
{
Write-Output "DoStuff: Enter"
$refObj.Value += $(1 + 2)
$refObj.Value += "more strings"
Write-Output "DoStuff: Exit"
}
$refRet = #()
$allRet = DoStuff([ref]$refRet)
"allRet"
$allRet
"refRet"
$refRet
"`n`nagain"
$allRet = DoStuff([ref]$refRet)
"allRet"
$allRet
"refRet"
$refRet
Note: Powershell doesn't need semicolons at the end of each statement; only for separating multiple statements on the same line.
Whenever possible, it's a good idea to avoid changing global state within a function. Pass input as parameters, and return the output, so you aren't tied to using the function in only one way. Your sample could look like this:
function sum
{
param($num1,$num2)
return $num1+$num2
}
$somenum=sum 2 5
Now, with Powershell, the return statement isn't needed. The result of every statement that isn't otherwise assigned, captured, redirected, or otherwise used, is just thrown in with the return value. So we could replace the return statement above with simply
$num1+$num2
You're already making use of this in your code with:
$previousScripts.Add($key) | Out-Null
where you are discarding the result of .Add(). Otherwise it would be included in the return value.
Personally, I find using return to explicitly mark the return value makes it easier to read. Powershell's way of putting all if the output in the return caused a lot of trouble for me as I was learning.
So, the only fixes to your code I would make are:
Move $previousScripts = New-Object Collections.Generic.HashSet[string] to inside the function, making it local.
Add return $previousScripts to the end of the function.
The powershell ise sometimes prints out my source code, if I have:
function f
{
$a=2
}
$a
It prints:
C:\Users\vics> function f
{
$a=2
}
$a
Why so weired?
If you are not saving your files, the code is written down into the console window. If your file is saved, it is just executed. You will then see the execution path like
C:\Users\vics> C:\Users\vics\Documents\test.ps1
...
Need your help in solving this following issue.
We have a powershell script like:
invoke-command -ScriptBlock { [cmdletbinding()]
param(
[parameter(mandatory=$True)]
[string] $ticktfilepath
)
$ticketdetails=get-content $ticktfilepath |%{if ( $_ -like '"AB*' ) {$_}}|%{echo "$($_.Split(',')[7].Split('"')[1])=$($_.Split(',')[3].Split(':')[0].Split(' ')[$_.Split(',')[3].Split(':')[0].Split(' ').length-1])=$($_.Split(',')[0].Split('"')[1]);"}
write-output $ticketdetails } -ArgumentList 'D:\file.csv'
This script reads a csv file and for those lines in the csv with “AB…” at the start, does some string parsing. The csv file passed has rows with the “AB…” and hence result are returned. This runs perfectly when executing through powershell console or ISE.
But as per our requirement, where we trying to execute the same script through:
1. System.Diagnostics.Process with process start info having file name as powershell.exe and the argument at the above script. It fails for –like. i.e at |%{if ( $_ -like '"AB*' ). It is always false for the condition even though it is expected to be true.
N.B. other powershell script works perfectly with this approach
2. Exactly Similar issue when executed through System.Management.Automation.Runspaces
So looks like some constraint using the “–like” operator.
We even with the System.Diagnostics.Process approach tried writing the script line by line leveraging the Process.StandardInput.WriteLine(line) but then powershell hangs.
Any pointer to address this will be highly appreciated.
While using System.Diagnostics.Process, I used something like:
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.FileName = "powershell.exe";
processStartInfo.Arguments = <ScriptContent>;
Process powerShellProc = new Process();
powerShellProc.StartInfo = processStartInfo;
powerShellProc.Start();
string successMessage = powerShellProc.StandardOutput.ReadToEndAsync().Result;
string errorMessage = powerShellProc.StandardError.ReadToEndAsync().Result;
powerShellProc.WaitForExit();
where,
ScriptContent- is the above powershell script.
Instead of like, even tried with startswith but then also the same result. But with powershell console or iSE, it works perfectly.
It's probably too late to answer :)
You shouldn't call powerShellProc.StandardOutput.ReadToEndAsync().Result before process actually finished. You can use Invoke-Executable function from How to capture process output asynchronously in powershell?
The following order is working
$outTask = $oProcess.StandardOutput.ReadToEndAsync();
$errTask = $oProcess.StandardError.ReadToEndAsync();
$bRet=$oProcess.WaitForExit($TimeoutMilliseconds)
$outText = $outTask.Result;
$errText = $errTask.Result;
I try to start a function of an script out of another script.
I want to save the return into a variable but this doesn't work.
script1.ps1:
function test
{
return "hallo"
}
script2.ps1:
./script1.ps1; $p=test
or
$p = ./script1.ps1; test
It seems that $p is null, but I don't know what's wrong.
Can anybody please help me?
thx
Try this:
. ./script1.ps1; $p=test
Why: you have to load the function into current scope (that's the period at the beginning – the dot source operator).
If you use ';', then completely new statement begins. So from you example $p = ./script.ps1; test, you assign output from script.ps1 to $p and then run the function.