+ Post New Thread
Results 1 to 8 of 8
Scripts Thread, Provide function parameters via user input in a script? (Powershell) in Coding and Web Development; So, I found out about function parameters and how they can include validating, mandatory or not, etc. Very useful stuff. ...
  1. #1

    Garacesh's Avatar
    Join Date
    Jan 2012
    Posts
    3,223
    Thank Post
    1,206
    Thanked 476 Times in 347 Posts
    Rep Power
    235

    Provide function parameters via user input in a script? (Powershell)

    So, I found out about function parameters and how they can include validating, mandatory or not, etc. Very useful stuff. So I'm revisiting an old script that contains a lot of if/elseif/else commands to the same purpose.
    Code:
    function TestFunc {
    	Param (
    		[Parameter(Mandatory=$true,
    			ValueFromPipeline=$true,
    			Position=0)]
    		[ValidateScript({
    You see where this is going.. Problem is, I've defined 5 parameters for this function (two mandatory, 3 not) and if I copy-and-paste my script into a powershell window, it's working fine..
    PS M:\> TestFunc Param0 Param1 Param2 Param3 Param4

    But this is part of a script, so if I just have the function (with the intention of letting it load and then manually calling it) the script hits the end of file and then exits. If I make a shortcut to powershell that pulls the script with the -noexit flag, the powershell window stays open but it doesn't "remember" the function (presumably the script exits once complete still)

    So, I've tried $Command = Read-Host " " and then $TestFunc $Command, but that isn't working either. It plays out as expected, but whereas a manual execution of a function splits at spaces, instead this method makes Param0 "Param0 Param1 Param2 Param3 Param4", leaving Param1 through 4 as $null. Try as I might with [regex]::split, ForEach-Object {$_ -split, I just can't get it to pass the parameters as individual bits.

    Anybody encountered anything similar before?
    Last edited by Garacesh; 19th June 2014 at 12:18 PM.

  2. #2
    ascott2's Avatar
    Join Date
    Nov 2007
    Posts
    184
    Thank Post
    18
    Thanked 38 Times in 30 Posts
    Rep Power
    21
    I have had something similar in the past passing arguments to an exe where it just wouldn't accept anything from a Read-Host unless it was part of an array. So something like this might work?

    Code:
    $commandargs = @()
    for ($i=1; $i -le 5; $i++){
    $indarg = Read-Host "Enter paramenter $i"
    $commandargs += $inarg
    }
    
    $TestFunc $commandargs[0] $commandargs[1] $commandargs[2] $commandargs[3] $commandargs[4]
    Rather than loop through Read Hosts for each one you could use split and add to the array that way. Not sure if it is the same issue but worth a try.

  3. #3

    Garacesh's Avatar
    Join Date
    Jan 2012
    Posts
    3,223
    Thank Post
    1,206
    Thanked 476 Times in 347 Posts
    Rep Power
    235
    That doesn't seem to be working.. If I write a function:
    Code:
    function test {
    	Write-Host $args[0]
    	Write-Host $args[2]
    	Write-Host $args[1]
    }
    and then define $teststr..
    Code:
    $Teststr = "one", "two", "three"
    When I run it through the function I ought to get a return of
    Code:
    one
    three
    two
    right? But I don't When I run "test $teststr", instead I get
    Code:
    one two three
    (one two three and then two blank lines)
    So it's still passing it all as one argument.
    Last edited by Garacesh; 19th June 2014 at 02:15 PM.

  4. #4

    Garacesh's Avatar
    Join Date
    Jan 2012
    Posts
    3,223
    Thank Post
    1,206
    Thanked 476 Times in 347 Posts
    Rep Power
    235
    Some progress:

    Okay, if I take the time to actually define parameters (like I would do in my live script) things change slightly..

    Code:
    function func {
    	Param (
    		[Parameter(Mandatory=$true,
    			ValueFromPipeline=$true,
    			Position=0)]
    		[ValidateScript({
    			Write-Host ("Value0: " + $_)
    			$true
    		})]
    		[String]$Value0
    	, 
    		[Parameter(Mandatory=$false,
    			ValueFromPipeline=$true,
    			Position=1)]
    		[ValidateScript({
    			Write-Host ("Value1: " + $_)
    			$true
    		})]
    		[String]$Value1
    	,
    		[Parameter(Mandatory=$false,
    			ValueFromPipeline=$true,
    			Position=2)]
    		[ValidateScript({
    			Write-Host ("Value2: " + $_)
    			$true
    		})]
    		[String]$Value2
    	)
    	begin {
    	} process {
    	} end {
    		Write-Host "End"
    	}
    }
    If I run through that 'normally' with "func 1 2 3" I get the expected result.. "Value0: 1", "Value1: 2" and "Value2: 3", however using $args[0], [1] and [2] no longer works like it did previously.

    If I split a string (such as the string Read-Host would generate) at spaces ($teststring.split()) which makes an array of "one", "two" and "three" (rather than just one string of "one two three", but if I run that through func (with $teststring.split() | func) I get a response of "Value0: one", "Value1: one", "Value2: one", "Value0: two", "Value1: two", "Value2: two", "Value0: three", "Value1: three" and "Value2: three"

    So it appears to be passing each member of a variable through to func, but applying it as every parameter.
    Maybe I can make something of this.. maybe not.

    pwrshl.png
    Last edited by Garacesh; 20th June 2014 at 02:51 PM.

  5. #5

    Garacesh's Avatar
    Join Date
    Jan 2012
    Posts
    3,223
    Thank Post
    1,206
    Thanked 476 Times in 347 Posts
    Rep Power
    235
    Bump! I found a solution!

    Code:
    $teststr = ($null, $null, $null, $null, $null)
    $teststr[0], [int]$teststr[1], $teststr[2], $teststr[3], $teststr[4] = ((Read-Host " ").split(' '))
    TestFunc $($teststr[0]) ([int]$($teststr[1])) $($teststr[2]) $($teststr[3]) $($teststr[4])
    It feels a tad kludgy, but it works.

  6. #6


    Join Date
    Feb 2007
    Location
    51.403651, -0.515458
    Posts
    9,608
    Thank Post
    250
    Thanked 2,898 Times in 2,131 Posts
    Rep Power
    828
    Quote Originally Posted by Garacesh View Post
    It feels a tad kludgy.
    It sure is!!! You're making it way more complicated than it needs to be.

    This is what you should be doing...

    Code:
    function Test-Me {
    
    	Param (
    		[Parameter(Mandatory=$true,
    			ValueFromPipeline=$true,
    			Position=0)]
    		[String] $Value0,
    
    		[Parameter(Mandatory=$false,
    			ValueFromPipeline=$true,
    			Position=1)]
    		[String] $Value1,
    
    		[Parameter(Mandatory=$false,
    			ValueFromPipeline=$true,
    			Position=2)]
    		[String] $Value2
    	)
    
        Begin { }
    
        Process {
    
            $Value0
            $Value1
            $Value2
    
        }
    
        End { }
    }
    
    Test-Me -Value0 One -Value1 Two -Value2 Three
    Test-Me One Two Three
    ... and here's the output:



    Out of interest, what sort of things are you trying to validate? For some of the parameters you could use ValidateSet, ValidateRange, ValidatePattern (and so on) for example.

    Btw, you may find Volume 7 of the PowerShell Monthly Tips useful.
    Last edited by Arthur; 23rd June 2014 at 09:49 PM.

  7. 2 Thanks to Arthur:

    Garacesh (24th June 2014), mac_shinobi (23rd June 2014)

  8. #7

    Garacesh's Avatar
    Join Date
    Jan 2012
    Posts
    3,223
    Thank Post
    1,206
    Thanked 476 Times in 347 Posts
    Rep Power
    235
    Well, since I learnt about Function Parameters (and their subsequent ValidateScript section) I wanted to revisit one of my earlier scripts because the current error checking is a huge sequence of
    Code:
    if (...) {
    
    # blah blah blah
    } elseif (...) {
    # blah blah blah
    } elseif (...) {
    ad infinatum.

    The code is for my "Search for Login events of a remote machine and pull back X amount. Email results if required." script, primarily used for when laptops are vandalised. Here's my full Param( block right now as it stands (ErrorActionPreference will be changed to Stop/Inquire rather than Inquire/Inquire once everything is fully working and tested)

    Code:
    function TestFunc {
    	Param (
    		[Parameter(Mandatory=$true,
    			HelpMessage="The name of the target machine. Must be powered and networked.",
    			Position=0)]
    		[ValidateScript({
    			$ErrorActionPreference = "Inquire"
    			Write-Host "$_"
    			try {
    				Test-Connection ($_ + ".school.local") -Count 2
    			} catch {
    				Write-Warning ("No response from target host. Ensure power and connectivity.")
    			}
    			$ErrorActionPreference = "Inquire"
    		})]
    		[String]$ComputerName
    	, 
    		[Parameter(Mandatory=$true,
    			HelpMessage="Number of events to return. INT only.",
    			Position=1)]
    		[ValidateScript({
    			$ErrorActionPreference = "Inquire"
    			if (($_ -isnot [int]) -or ($_ -lt 1)) {
    				Write-Warning "MaxEvents value must be a positive integer"
    			} else {
    			$true
    			}
    			$ErrorActionPreference = "Inquire"
    		})]
    		$MaxEvents
    	, 
    		[Parameter(Mandatory=$false,
    			Position=2)]
    		[AllowNull()]
    		[AllowEmptyString()]
    		[AllowEmptyCollection()]
    		[String]$Part
    	, 
    		[Parameter(Mandatory=$false,
    			Position=3)]
    		[AllowNull()]
    		[AllowEmptyString()]
    		[AllowEmptyCollection()]
    		[ValidateSet("y","n")]
    		[ValidateScript({
    			if ($_ -inotmatch "^(y|n)$") {
    				Write-Warning "RepairedStatus must be Y or N."
    			} else {
    				$true
    			}
    		})]
    		$RepairStatus
    	,
    		[Parameter(Mandatory=$false,
    			Position=4)]
    		[AllowNull()]
    		[AllowEmptyString()]
    		[AllowEmptyCollection()]
    		[ValidateScript({
    			$ErrorActionPreference = "Inquire"
    			try {
    				(Get-ADUser -Identity $_)
    			} catch { 
    				Write-Warning ("Cannot find user \'" + $_ + "\'")
    			}
    			$ErrorActionPreference = "Inquire"
    		})]
    		$Email
    ComputerName is quite literally the name of the target machine, used for Get-WinEvent (thus, mandatory) and for crafting the email if needed. The same goes for MaxEvents, it's required for Get-WinEvent, so that's a mandatory parameter. Part is an open string with no requirements used for the e-mail, for example "keyboard", "screen", etc. The e-mail just fills in the blanks. RepairStatus alters the e-mail body by a simple if (...) statement and $EmailBody +=, adding fixed/not fixed. The $Email checks a given string against AD (as our email addresses are all ADUserName@School....). If any of the non-mandatory parameters are not specified, it simply writes the output to the screen (Write-Host), and if they are all specified (and checked as correct) it sends an e-mail (Send-MailMessage)

    Just to clarify, this function will run as part of a script. The function relies on parameters it's given, but there's no way to manually call the function if it's a .ps1, therefore I'm using a Read-Host to prompt for the parameters it needs, which will then run them through the function (and as part of the function, validation)

    Edit: Oh, poop
    Get-ADUser doesn't want to work in Powershell 2 (It has to be ran as 2 as Get-WinEvent doesn't work in PS3 unless you're in the USA).. Funny, though. In a test environment running PS2 I can use Import-Module ActiveDirectory and then Get-ADUser works.. But if I have Import-Module ActiveDirectory in my script, it still says Get-ADUser isn't a recognised cmdlet, so I don't think it's importing..
    Last edited by Garacesh; 24th June 2014 at 11:46 AM.

  9. #8

    Garacesh's Avatar
    Join Date
    Jan 2012
    Posts
    3,223
    Thank Post
    1,206
    Thanked 476 Times in 347 Posts
    Rep Power
    235
    Double-post because I can't edit.

    I've found a pseudo-solution that fixes my "Get-WinEvent only working in Powershell 2" issue.. If I prepare it as two separate scripts and call the second from the first I can use "powershell.exe -version 2 -command (". {C:\Scripting\Location\GetLogins.ps1}")" and that's actually working correctly now. The issue is that the parameters for the function (ComputerName, MaxEvents, etc) aren't being passed over to the child script.

    Every attempt I make to clean-up this script and make it more efficient seems to end in just kludging it a different way



SHARE:
+ Post New Thread

Similar Threads

  1. Replies: 18
    Last Post: 13th September 2013, 05:31 PM
  2. updating user information in AD via script.
    By Hedghog in forum How do you do....it?
    Replies: 2
    Last Post: 4th April 2008, 04:39 PM
  3. updating user information in AD via script.
    By Hedghog in forum How do you do....it?
    Replies: 0
    Last Post: 4th April 2008, 03:31 PM
  4. View user password in AD
    By timbo343 in forum Windows
    Replies: 10
    Last Post: 20th March 2007, 06:04 PM
  5. Script to change user email in AD (or Exchange)
    By SpuffMonkey in forum Scripts
    Replies: 16
    Last Post: 8th November 2005, 11:31 PM

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •