Using the Begin-process-end for Powershell. What are the benefits? - powershell

i'm currently trying to do a small script to retrieve data from a server using REST endpoint. As it's the first time i work with powershell, i'm a bit disappointed by the 'lack' of structure and 'typing' in it.
Then i structured my code like this with comments :
# declare ------------------------------------------------------------------------------------------
// All the var i'll need in my process
# do -----------------------------------------------------------------------------------------------
// Ask for user to enter URL and credentials
// Check if credentials are correct
// Connect to the server
// Retrieves the data in a list (JSON formatted) --> List REST EndPoint
// Foreach document in my list
// retrieve the document's details --> single file REST EndPoint
// download the file into local directory
// End Foreach
# display results ----------------------------------------------------------------------------------
// display :
// Downloaded files
// Non-Downloaded files
During review, my colleague told me "Oh ! What you need is the 'begin-process-end' " and then just leave.
I then read somethings about this here but for what i see, the structure is the same as i did with my comments but i don't see the point where it's "What i need" as a functionalities.
Since i'm a real beginner, i maybe miss the point. Could you explain it to me ?
(btw, thx to the kind person who'll edit my ugly english mistakes)

Think of these blocks as a pre-processor (the begin block), actual processor (the process block), and a post-processor(the end block) to a function.
You don't need to define any of these blocks (although Begin and End block will always need a Process block), and can write code just fine without them, but the idea behind them is to divide the function into three separate areas of code. The following function should make it a little clearer:
function addition_by_one {
# Arguments to a powershell functions are defined inside the param block.
Param (
[int]$number
)
# In the below begin block, the variable $a was initialized to 1.
Begin {
$a = 1
}
# The Process block does the main work. In this case, $a is added to the argument supplied to the function.
Process {
$sum = $number + $a
}
# Finally, the End block will execute after the process block completes.
End {
"Sum is $sum"
}
}
# Call the function with any integer argument.
addition_by_one -number 3

Being/process/end is really for pipelines. You can't process from the pipe without a process block.
1..5 | & { process {$_ * 2} }
2
4
6
8
10

Related

Powershell script queues results of an if statement in a do while in a function

I'm using a function that I call from another script. It prompts a user for input until it gets back something that is not empty or null.
function GetUserInputValue($InputValue)
{
do{
$UserValue = Read-Host -Prompt $InputValue
if (!$UserValue) { $InputValue + ' cannot be empty' }
}while(!$UserValue)
$UserValue
return $UserValue
}
The issue is quite strange and likely a result of my lack of powershell experience. When I run the code and provide empty results, the messages from the if statement queue up and only display when I finally provide a valid input. See my console output below.
Console Results
test:
test:
test:
test:
test:
test:
test: 1
test cannot be empty
test cannot be empty
test cannot be empty
test cannot be empty
test cannot be empty
test cannot be empty
1
I can make this work however in the main file with hard coded values.
do{
$Server = Read-Host -Prompt 'Server'
if (!$Server) { 'Server cannot be empty' }
}while(!$Server)
I'm working Visual Studio Code. This is a function I have in another file I've named functions.ps1.
I call this from my main file like this,
$test = GetUserInputValue("test")
$test
When you put a naked value in a script like "here's a message" or 5 or even a variable by itself $PID what you're implicitly doing is calling Write-Output against that value.
That returns the object to the pipeline, and it gets added to the objects that that returns. So in a function, it's the return value of the function, in a ForEach-Object block it's the return value of the block, etc. This bubbles all the back up the stack / pipeline.
When it has nowhere higher to go, the host handles it.
The console host (powershell.exe) or ISE host (powershell_ise.exe) handle this by displaying the object on the console; this just happens to be the way they handle it. Another host (a custom C# application for example can host the powershell runtime) might handle it differently.
So what's happening here is that you are returning the message that you want to display, as part of the return value of your function, which is not what you want.
Instead, you should use Write-Host, as this writes directly to the host, skipping the pipeline. This is the correct command to use when you want to display a message to the user that must be shown (for other information you can use different commands like Write-Verbose, Write-Warning, Write-Error, etc.).
Doing this will give you the correct result, and prevent your informational message from being part of the return value of your function.
Speaking of which, you are returning the value twice. You don't need to do:
$UserValue
return $UserValue
The first one returns the value anyway (see the top of this answer); the second one does the same thing except that it returns immediately. Since it's at the end of the function anyway, you can use wither one, but only use one.
One more note: do not call PowerShell functions with parentheses:
$test = GetUserInputValue("test")
This works only because the function has a single parameter. If it had multiple params and you attempted to call it like a method (with parentheses and commas) it would not work correctly. You should separate arguments with spaces, and you should usually call parameters by name:
$test = GetUserInputValue "test"
# better:
$test = GetUserInputValue -InputValue "test"

Powershell returns wrong result

I came across this weird issue in Powershell (not in other languages). Could anyone please explain to me why this happened?
I tried to return a specified number (number 8), but the function keeps throwing everything at me. Is that a bug or by design?
Function GetNum() {
Return 10
}
Function Main() {
$Number10 = GetNum
$number10 #1 WHY NO OUTPUT HERE ??????? I don't want to use write host
$result = 8 # I WANT THIS NUMBER ONLY
PAUSE
return $result
}
do {
$again = Main
Write-Host "RESULT IS "$again # Weird Result, I only want Number 8
} While ($again -eq 10) # As the result is wrong, it loops forever
Is that a bug or by design?
By design. In PowerShell, cmdlets can return a stream of objects, much like using yield return in C# to return an IEnumerable collection.
The return keyword is not required for output values to be returned, it simply exits (or returns from) the current scope.
From Get-Help about_Return (emphasis added):
The Return keyword exits a function, script, or script block. It can be
used to exit a scope at a specific point, to return a value, or to indicate
that the end of the scope has been reached.
Users who are familiar with languages like C or C# might want to use the
Return keyword to make the logic of leaving a scope explicit.
In Windows PowerShell, the results of each statement are returned as
output, even without a statement that contains the Return keyword.
Languages like C or C# return only the value or values that are specified
by the Return keyword.
Mathias is spot on as usual.
I want to address this comment in your code:
$number10 #1 WHY NO OUTPUT HERE ??????? I don't want to use write host
Why don't you want to use Write-Host? Is it because you may have come across this very popular post from PowerShell's creator with the provocative title Write-Host Considered Harmful?
If so, I encourage you to read what I think is a great follow-up/companion piece by tby, titled Is Write-Host Really Harmful?
With this information, it should be clear that as Mathias said, you are returning objects to the pipeline, but you should also be armed with the information needed to choose an alternative, whether it's Write-Verbose, Write-Debug, or even Write-Host.
If I were going to be opinionated about it, I would go with Write-Verbose, altering your function definition slightly in order to support it:
function Main {
[CmdletBinding()]
param()
$Number10 = GetNum
Write-Verbose -Message $number10
$result = 8 # I WANT THIS NUMBER ONLY
PAUSE
$result
}
When you invoke it by just calling $again = Main you'll see nothing on the screen, and $again will have a value of 8. However if you call it this way:
$again = Main -Verbose
then $again will still have the value of 8, but on the screen you'll see:
VERBOSE: 10
likely in differently colored text.
What that gives is not only a way to show the value, but a way for the caller to control whether they see the value or not, without changing the return value of the function.
To drive some of the points in the articles home further, consider that it's not necessarily necessary to invoke your function with -Verbose to get that.
For example, let's say you stored that whole script in a file called FeelingNum.ps1.
If, in addition to the changes I made above, you also add the following to the very top of your file:
[CmdletBinding()]
param()
Then, you still invoked your function "normally" as $again = Main, you could still get the verbose output by invoking your script with -Verbose:
powershell.exe -File FeelingNum.ps1 -Verbose
What happens there is that using the -Verbose parameter sets a variable called $VerbosePreference, and that gets inherited on each function called down the stack (unless it's overridden). You can also set $VerbosePreference manually.
So what you get by using these built-in features is a lot of flexibility, both for you as the author and for anyone who uses your code, which is a good thing even if the only person using it is you.

Remote Job execution with non terminating Return

I have a script that I am running both remotely and locally in different situations. In the remote situations I would like to return some additional job information. However, the script also needs to complete with a Restart, LogOff, etc.
However, if I Return $Results, no code that follows is executed. Whereas if I Write-Host the return info, I see it as additional data for the job, and the script completion code that follows can still execute.
I can make this work, but it causes two issues.
I have two bits of data I need to return, which I had planned to do in an array. But with Write-Host I need to use the kludge of $value1|$value2 and then split the string on | to get at the two bits of data.
Write-Host somewhat pollutes my console so I need some extra code to manage that.
Is there any way to return intermediate info and continue execution? Or am I going to have no choice but to go the Write-Host route? Or is there perhaps another approach that is better than the Write-Host hack?
You seem to be under the impression that you have to use the return keyword to make a function(?) return something. PowerShell works a little differently, though. PowerShell functions return the entire non-captured output on the success output stream. The return keyword is just for making a function return at that particular point.
There is no difference at all between
function Foo {
return 'something' # return with value
}
and
function Foo {
'something'
return # echo first, then return
}
and
function Foo {
'something' # just echo, return implicitly
}
That way you can have a function generate the output, then do more stuff before actually returning:
function Foo {
'foo'
'bar'
Get-ChildItem . -Recurse | Out-Null # output not returned (suppressed)
'baz' > 'C:\some.txt' # output not returned (redirected to file)
}
The caller will receive the returned values (in this case 'foo', 'bar') only after the function call returns, though. If you need immediate feedback you'll need other means.
Don't use Return. Just output $Results, and let the rest of the script continue on.

Please give the solution to that

I am calling a function and passing 3 parameters to that. I am taking each parameter one be one and performing some operations on that. At the end i am traversing the loop so as to perform the same operations on the rest of the parameters.
Suppose the problem is like this:
print "Starting the operations";
# calling the function say NetworkMode
NetworkMode(SONET,SDH,SDH-J) #This will perform certain steps
print "Ending of the test case"
Output i want should come like this:
#Starting the operaions
#Whatever the output function will give using first parameter
#Ending the test case
#Starting the operaions
#Whatever the output function will give using second parameter
#Ending the test case
#Starting the operaions
#Whatever the output function will give using third parameter
#Ending the test case
Is there any way i can do that.
If you call one single function [once], then you get one chance to print out a result; if you want to print out once per parameter, then the function itself is going to have to do the printing out as it operates on each parameter. If, however, you're repeating the exact same steps on each parameter then perhaps your function should only take one parameter?
You mean that the NetworkMode function actually takes only one parameter? If so, then this code will probably do what you want:
foreach my $mode (SONET, SDH, SDH-J)
{
print "Starting the operations";
# calling the function say NetworkMode
NetworkMode($mode); #This will perform certain steps
print "Ending of the test case";
}

HOP::Lexer with overlapping tokens

I'm using HOP::Lexer to scan BlitzMax module source code to fetch some data from it. One particular piece of data I'm currently interested in is a module description.
Currently I'm searching for a description in the format of ModuleInfo "Description: foobar" or ModuleInfo "Desc: foobar". This works fine. But sadly, most modules I scan have their description defined elsewhere, inside a comment block. Which is actually the common way to do it in BlitzMax, as the documentation generator expects it.
This is how all modules have their description defined in the main source file.
Rem
bbdoc: my module description
End Rem
Module namespace.modulename
This also isn't really a problem. But the line after the End Rem also contains data I want (the module name). This is a problem, since now 2 definitions of tokens overlap each other and after the first one has been detected it will continue from where it left off (position of content that's being scanned). Meaning that the token for the module name won't detect anything.
Yes, I've made sure my order of tokens is correct. It just doesn't seem possible (somewhat understandable) to move the cursor back a line.
A small piece of code for fetching the description from within a Rem-End Rem block which is above a module definition (not worked out, but working for the current test case):
[ 'MODULEDESCRIPTION',
qr/[ \t]*\bRem\n(?:\n|.)*?\s*\bEnd[ \t]*Rem\nModule[\s\t]+/i,
sub {
my ($label, $value) = #_;
$value =~ /bbdoc: (.+)/;
[$label, $1];
}
],
So in my test case I first scan for a single comment, then the block above (MODULEDESCRIPTION), then a block comment (Rem-End Rem), module name, etc.
Currently the only solution I can think of is setup a second lexer only for the module description, though I wouldn't prefer that. Is what I want even possible at all with HOP::Lexer?
Source of my Lexer can be found at https://github.com/maximos/maximus-web/blob/develop/lib/Maximus/Class/Lexer.pm
I've solved it by adding (a slightly modified version of) the MODULEDESCRIPTION. Inside the subroutine I simply filter out the module name and return an arrayref with 4 elements, which I later on iterate over to create a nice usable array with tokens and their values.
Solution is again at https://github.com/maximos/maximus-web/blob/develop/lib/Maximus/Class/Lexer.pm
Edit: Or let me just paste the piece of code here
[ 'MODULEDESCRIPTION',
qr/[ \t]*\bRem\R(?:\R|.)*?\bEnd[ \t]*Rem\R\bModule[\s\t]\w+\.\w+/i,
sub {
my ($label, $value) = #_;
my ($desc) = ($value =~ /\bbbdoc: (.+)/i);
my ($name) = ($value =~ /\bModule (\w+\.\w+)/i);
[$label, $desc, 'MODULENAME', $name];
}
],