none
Process Block Usage RRS feed

  • Question

  • I inherited a script that makes heavy use of process blocks.  I'm struggling to understand the usage though.

    If a function is designed to receive a single elemental input parameter such as a directory path, is there a reason to wrap the subsequent code in a process block?  The script isn't iterating multiple input values, its taking a single pathname and iterating items contained in the path.

    Monday, July 30, 2018 3:56 PM

Answers

  • I would say that it looks to me like whoever wrote that function didn't understand very well how PowerShell function blocks work. (For example, there is no point in repeatedly calling the LoadWithPartialName method for every iteration of the pipeline.)

    -- Bill Stewart [Bill_Stewart]

    • Marked as answer by EVC Monday, July 30, 2018 6:46 PM
    Monday, July 30, 2018 6:19 PM
    Moderator
  • Sorry Bill.  I was trying to stay implementation agnostic as not to confuse the general concepts with code.  After reviewing what I wrote, an example would have been better.  I've included something below.  This function is called to get a username from the database.  The result is then used by the calling function perform an action using the User ID.  Does the process block make sense here? 



    Your example does not use the pipeline.  It is a one-shot "Process" is optional and should be removed.  Just run the default.

    Please use the code posting tool for code and not other mechanisms which are unreadable in many browser and cannot be copied correctly.

    Here is how to use the pipeline.

    function GetRecordID {
        param (
            [parameter(
                Mandatory,
                ValueFromPipeLine
            )][string] $UserName,
            [Parameter(Mandatory)]$SqlCommand
        )
    
        process {
            Write-Verbose "Getting record id for -  $UserName... "
            $sqlcommand.CommandText = "SELECT RecordID FROM [QA_INT_Service_Profile_DB].[dbo].[UserProfile_Full] WHERE NTName = 'BWXT\$UserName'"
            $locationID = $sqlCommand.ExecuteScalar()
            Write-Verbose "Located ID = $locationID"
            [pscustomobject]@{
                UserName = $username
                LocationID = $locationID
            }
        }
    } 
    
    $user1,$user2,$user3 | GetRecordID -SqlCommand $cmd

    Here is no pipeline.

    function GetRecordID {
        param (
            [parameter(
                Mandatory
            )][string] $UserName,
            [Parameter(Mandatory)]$SqlCommand
        )
    
        Write-Verbose "Getting record id for -  $UserName... "
        $sqlcommand.CommandText = "SELECT RecordID FROM [QA_INT_Service_Profile_DB].[dbo].[UserProfile_Full] WHERE NTName = 'BWXT\$UserName'"
        $locationID = $sqlCommand.ExecuteScalar()
        Write-Verbose "Located ID = $locationID"
        [pscustomobject]@{
            UserName = $username
            LocationID = $locationID
        }
    } 
    
     GetRecordID -UserName $user1 -SqlCommand $cmd  -Verbose
    


    \_(ツ)_/



    • Edited by jrv Monday, July 30, 2018 6:28 PM
    • Marked as answer by EVC Monday, July 30, 2018 6:46 PM
    Monday, July 30, 2018 6:27 PM

All replies

  • Your question isn't clear.

    The usual meaning of a process block is "execute it for each element in a collection" (typically, from the pipeline).

    Perhaps you should provide a short example of what isn't working?


    -- Bill Stewart [Bill_Stewart]

    Monday, July 30, 2018 4:08 PM
    Moderator
  • The default block when not specified is the "End{}"  block.  In non-advanced functions "Process{}" is optional.  In  a function that needs to use the pipeline the "Process" block is required for correct functioning.

    Function with no "Process" block will not execute once for each element of a collection.  It will execute once and the current object will be the whole collection.  Adding "Process" will execute the function once for each element of a collection.


    \_(ツ)_/

    Monday, July 30, 2018 5:31 PM
  • The default block when not specified is the "End{}"  block.  In non-advanced functions "Process{}" is optional.  In  a function that needs to use the pipeline the "Process" block is required for correct functioning.

    Function with no "Process" block will not execute once for each element of a collection.  It will execute once and the current object will be the whole collection.  Adding "Process" will execute the function once for each element of a collection.


    \_(ツ)_/

    Thank You!

    I was afraid I had misunderstood the definition. 

    Basically, within a function that is designed to receive one-dimensional parameters, meaning no collections or arrays, the use of a process block is redundant and unnecessary since there is nothing to iterate.   Am I correct in my understanding that this is your conclusion as well?

    Monday, July 30, 2018 5:44 PM
  • Your question isn't clear.

    The usual meaning of a process block is "execute it for each element in a collection" (typically, from the pipeline).

    Perhaps you should provide a short example of what isn't working?


    -- Bill Stewart [Bill_Stewart]

    Sorry Bill.  I was trying to stay implementation agnostic as not to confuse the general concepts with code.  After reviewing what I wrote, an example would have been better.  I've included something below.  This function is called to get a username from the database.  The result is then used by the calling function perform an action using the User ID.  Does the process block make sense here? 

    function GetRecordID {
    [CmdletBinding()]
    
    param (
          [parameter(Mandatory=$true)]
          [string] $UserName
         )
    
     process {
       #[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.ConnectionInfo')
       # Build Connection String
    
        Write-Host "Getting record id for -  $UserName... " -NoNewline;
    
        $sqlcommand.CommandText = "SELECT RecordID FROM [QA_INT_Service_Profile_DB].[dbo].[UserProfile_Full] WHERE NTName = 'BWXT\$UserName'"
       $Return = $sqlCommand.ExecuteScalar()
    
       Write-Host "Located ID = " + $Return;
       return $Return
      }
    } 





    • Edited by EVC Monday, July 30, 2018 8:17 PM format
    Monday, July 30, 2018 5:55 PM
  • The default block when not specified is the "End{}"  block.  In non-advanced functions "Process{}" is optional.  In  a function that needs to use the pipeline the "Process" block is required for correct functioning.

    Function with no "Process" block will not execute once for each element of a collection.  It will execute once and the current object will be the whole collection.  Adding "Process" will execute the function once for each element of a collection.


    \_(ツ)_/

    Thank You!

    I was afraid I had misunderstood the definition. 

    Basically, within a function that is designed to receive one-dimensional parameters, meaning no collections or arrays, the use of a process block is redundant and unnecessary since there is nothing to iterate.   Am I correct in my understanding that this is your conclusion as well?

    This only applies to the pipeline and not to parameters passed on the command line.


    \_(ツ)_/

    Monday, July 30, 2018 6:14 PM
  • I would say that it looks to me like whoever wrote that function didn't understand very well how PowerShell function blocks work. (For example, there is no point in repeatedly calling the LoadWithPartialName method for every iteration of the pipeline.)

    -- Bill Stewart [Bill_Stewart]

    • Marked as answer by EVC Monday, July 30, 2018 6:46 PM
    Monday, July 30, 2018 6:19 PM
    Moderator
  • Sorry Bill.  I was trying to stay implementation agnostic as not to confuse the general concepts with code.  After reviewing what I wrote, an example would have been better.  I've included something below.  This function is called to get a username from the database.  The result is then used by the calling function perform an action using the User ID.  Does the process block make sense here? 



    Your example does not use the pipeline.  It is a one-shot "Process" is optional and should be removed.  Just run the default.

    Please use the code posting tool for code and not other mechanisms which are unreadable in many browser and cannot be copied correctly.

    Here is how to use the pipeline.

    function GetRecordID {
        param (
            [parameter(
                Mandatory,
                ValueFromPipeLine
            )][string] $UserName,
            [Parameter(Mandatory)]$SqlCommand
        )
    
        process {
            Write-Verbose "Getting record id for -  $UserName... "
            $sqlcommand.CommandText = "SELECT RecordID FROM [QA_INT_Service_Profile_DB].[dbo].[UserProfile_Full] WHERE NTName = 'BWXT\$UserName'"
            $locationID = $sqlCommand.ExecuteScalar()
            Write-Verbose "Located ID = $locationID"
            [pscustomobject]@{
                UserName = $username
                LocationID = $locationID
            }
        }
    } 
    
    $user1,$user2,$user3 | GetRecordID -SqlCommand $cmd

    Here is no pipeline.

    function GetRecordID {
        param (
            [parameter(
                Mandatory
            )][string] $UserName,
            [Parameter(Mandatory)]$SqlCommand
        )
    
        Write-Verbose "Getting record id for -  $UserName... "
        $sqlcommand.CommandText = "SELECT RecordID FROM [QA_INT_Service_Profile_DB].[dbo].[UserProfile_Full] WHERE NTName = 'BWXT\$UserName'"
        $locationID = $sqlCommand.ExecuteScalar()
        Write-Verbose "Located ID = $locationID"
        [pscustomobject]@{
            UserName = $username
            LocationID = $locationID
        }
    } 
    
     GetRecordID -UserName $user1 -SqlCommand $cmd  -Verbose
    


    \_(ツ)_/



    • Edited by jrv Monday, July 30, 2018 6:28 PM
    • Marked as answer by EVC Monday, July 30, 2018 6:46 PM
    Monday, July 30, 2018 6:27 PM
  • I would say that it looks to me like whoever wrote that function didn't understand very well how PowerShell function blocks work. (For example, there is no point in repeatedly calling the LoadWithPartialName method for every iteration of the pipeline.)

    -- Bill Stewart [Bill_Stewart]

    I would say that the commented items are residue for copying another function and trying to guess at how to alter it.  For the code in the function I see no reason to write a function.  Just run the two lines inline in the regular code.

    Most people with no formal programming experience tend to try to wrap everything in a function.  This is unnecessary and counter productive.

    Build the command and parameters easily in the script then call it.

    $cmd.Parameter['NTName] =  $username
    $locationID = $cmd.ExecuteScaler()

    Call this for each user you want.  No need for a.

    function.


    \_(ツ)_/


    • Edited by jrv Monday, July 30, 2018 6:35 PM
    Monday, July 30, 2018 6:34 PM
  • This code may be a result of cargo cult programming.

    -- Bill Stewart [Bill_Stewart]

    Monday, July 30, 2018 7:18 PM
    Moderator
  • This code may be a result of cargo cult programming.

    -- Bill Stewart [Bill_Stewart]

    Yes.  That happens ritually in scripting when people with no programming training try to write complex solutions.  It is not "evil" but it is quite normal and tends to spread bad coding practices through forums and blog posts. 

    This is one reason why I try to push back on bad coding and insist that those new to coding should start with some formal tutorial or book.  Unfortunately todays techs do not read and have a very short attention span. 

    Its the old RTFM that seems to get new users mad.  Perhaps we should just abandon books and documentation.  After all, the 13th century was much simpler...wasn't it? 


    \_(ツ)_/

    Monday, July 30, 2018 7:27 PM
  • It did seem needlessly overcomplicated for sure.  I have to admit, I'm guilty of putting everything into functions though.  That's how we did it in school, it was all about abstraction and reusability.  Sometimes, I find myself rolling stuff back into the main function just because I've created such a mess! lol.
    Monday, July 30, 2018 8:21 PM
  • Abstraction is a formalism.  It is not about putting things in functions.  It is about designing code.  Unfortunately there are a lot of people teaching programming who are not themselves formal programmers.

    Reusability means designing code that an serve multiple purposes because it generalizes a problem solution.  Placing code in a function does not generalize the code. 

    The code you posted was not abstracted or reusable.  It was totally dependent on its environment.  The code I posted assisted in generalizing the code.  I did this on purpose to make you aware of how the CMD object was not accounted for making the function mostly unnecessary.

    If we want a general function to execute a sql statement this would be better.

    function Get-SomeScalarValue {
        Param(
            [Parameter(Mandatory)]$SqlConnection,
            [Parameter(Mandatory)]$SqlQuery
        )
    
        $cmd = $SqlConnection.CreateCommand()
        $sqlcommand.CommandText = $SqlQuery
        # return the scalar value
        $sqlCommand.ExecuteScalar()
    } 
    
    Get-SomeScalarValue -SqlConnection $conn -SqlQuery $sql
    

    Even that is of minimal use and only serves to hide the functionality of the code.  It is only useful if you plan on using this from many places in your code.

    KISS.  That is the controlling rule in all programming.  Code abstraction is usually done at design time or using code folding techniques at the end.


    \_(ツ)_/

    Monday, July 30, 2018 8:50 PM
  • Totally agree about the reusability comment!!  Not my code BTW, I swear.. lol
    Tuesday, July 31, 2018 2:54 PM