Friday, November 7, 2014

What to think about PowerShell Syntax?

When I think about the PowerShell syntax compared to JavaScript, Python or C#, I am puzzled to put it mildly.

The question is why choosing such a different syntax?

I understand that PowerShell is shell based language, I can read the PowerShell Grammar.

But ultimately, I am going to end up writing a lot of code and the question is?

Is my code as clear as if I was writing in Python or JavaScript. I do not think so.

Not to mention the following samples:
   
if ( f1 1 2 -and f2 3 4 ) {

}
First, if you are new to PowerShell, you will have difficulties understanding this code. Then once you think you know PowerShell, this code will not fail and will not do what you thought it would do.

What about the classical calling a function and not passing the arguments the PowerShell way. Once again it does not fail, it just behave as expected, except that is not what you are used to and for a moment you forgot to PowerShell grammar.
   
function f1($p1, $p2) {

}
f1 1,2
And now you have a bug or two in your code.

Unit tests: PowerShell is about automating and configuring Systems. There is not way you can write unit tests for that. Do I have to mock up the all Windows operating system.

Interesting remarks from John D. Cook from his blog post PowerShell gotchas, which summarize it well
         PowerShell is both a shell and a scripting language, but first of all it is a shell.


This post is about some of the weirdest stuff, I faced while working with PowerShell, and
some workarounds.


Function returning an array of one string

When a function returns an array containing only one string, PowerShell will convert the returned value into one string.
   

function CreateArray() {

  $myArray = @()      # Create an empty array of object
  $myArray += "Blue"  # Add a string to the array

  return $myArray
}
cls
$myArray = CreateArray

Write-Host "myArray is $($myArray.GetType().Name)"
Write-Host "myArray.len is $($myArray.Length)"
Write-Host "myArray: $myArray"
Output:
myArray is String
myArray.len is 4
myArray: Blue
To force the value to be returned as an array, add a coma just after the return.
(Sound like a typo to me)
   

function CreateArray() {

  $myArray = @()      
  $myArray += "Blue"  

  return , $myArray # Force to return an array
}
cls
$myArray = CreateArray

Write-Host "myArray is $($myArray.GetType().Name)"
Write-Host "myArray.len is $($myArray.Length)"
Write-Host "myArray: $myArray"


Calling one or more functions with parameters from an if

That should sound like a no brainer, but because in PowerShell what follow an if is not an expression that must evaluate to a bool , but is defined in the grammar as pipeLineRule then this can be dangerous.

Reminder: When calling a function in PowerShell
  • There is no need for (  ) 
  • Parameters are separated by a space
   

function f1($p1, $p2) {

   Write-Host "f1 `$p1: $p1, `$p2: $p2"
   return $true
}
function f2($p1, $p2) {

   Write-Host "f2 `$p1: $p1, `$p2: $p2"
   return $false
}
cls


if ( f1 1 2 ) {
    "  f1 returned true"
} 

# THIS SYNTAX WILL RUN, BUT WILL NOT CALL f2 3 4
if ( f1 1 2 -and f2 3 4 ) {
    "  f1 and f2 returned true"
}
else {
    "  f1 and f2 returned false"
}


# You must wrap each function call into a ()
if ( (f1 1 2) -and (f2 1 2)  ) { 
    "  f1 and f2 returned true"
}
else {
    "  f1 and f2 returned false"
}


# Negating the result of a function. You must wrap each function call into a ()
# At least here if you forget the (), it will not compile
if ( -not(f1 1 2)  ) { 
    "  f1 returned false"
}
else {    
    "  f1 returned true"
}

Output:
f1 $p1: 1, $p2: 2
  f1 returned true
f1 $p1: 1, $p2: 2
  f1 and f2 returned true
f1 $p1: 1, $p2: 2
f2 $p1: 1, $p2: 2
  f1 and f2 returned false
f1 $p1: 1, $p2: 2
  f1 returned true


Creating object on the fly

Sometime I missed the simplicity of JavaScript.
   

var o = {

   Name : "Rene",
   Age  : 48
};
The PowerShell way
   

# Out of the box
$o = New-Object -TypeName PSObject -Property @{ Name = "Rene"; Age = 48 }

# With a helper function
function CreateObject($properties) {

    return New-Object -TypeName PSObject -Property $properties
}

$o = CreateObject @{

    Name = "Rene"; 
    Age  = 48
}

Object literal

Powershell does not support the notion of Object Literal like in JavaScript, but support Dictionary and Array literal. It is close. Here is a code that convert a dictionary into an object though it does not supported nested object
   


var o = {
    Name   : "Rene",
    Age    : 32,
    Debug  : true,
    Unknown: null,

    Country : {
        Name : "Guatemala",
    },
    Numbers : [1, 2, 3, 4]
};
The PowerShell way
   
$o = @{
    Name    = "Rene"
    Age     = 32
    Debug   = $true
    Unknown = $null

    Country = @{
        Name = "Guatemala"
     }
     Numbers = @( 1, 2, 3, 4 )
}

No comments:

Post a Comment