tag:blogger.com,1999:blog-17332816.post7379535887493386667..comments2023-04-05T22:38:04.960+08:00Comments on Cup(Of T): Splatting Hellpiers7http://www.blogger.com/profile/11186470645521299750noreply@blogger.comBlogger7125tag:blogger.com,1999:blog-17332816.post-28677744961493280322017-01-07T07:14:54.826+08:002017-01-07T07:14:54.826+08:00I found this which looks pretty awesome to make Po...I found this which looks pretty awesome to make PowerShell handle extra arguments not required by the cmdlet you happen to be calling.<br /><br />http://pelebyte.net/blog/2011/07/14/better-powershell-splatting/Deikensentsuhttps://www.blogger.com/profile/15538586481060914020noreply@blogger.comtag:blogger.com,1999:blog-17332816.post-90088298902686667672011-05-26T21:45:16.491+08:002011-05-26T21:45:16.491+08:00I think Gotcha #5 can be overcome by adding the fo...I think Gotcha #5 can be overcome by adding the following as a final parameter to any function (sure, not ideal, but it works):<br /><br /> #Required for Splatting non-positionally and/or capturing remaining arguments...<br /> [Parameter(ValueFromRemainingArguments = $true)]$remainingArgs<br /><br />as found at <a href="http://stackoverflow.com/questions/2795582/any-way-to-specify-a-non-positional-parameter-in-a-powershell-script" rel="nofollow">http://stackoverflow.com/questions/2795582/any-way-to-specify-a-non-positional-parameter-in-a-powershell-script</a><br /><br />This will mop up any additional parameters in the splatted hashtable which otherwise would be complained about as the error message you refer to:<br />"A parameter cannot be found that matches parameter name ..."<br /><br />( Interestingly, this error/complaint only comes about as soon as you add the <br />[Parameter( ... attribute to any one parameter. With just a plain param definition, the splatting works without error, even with a splat hashtable containing extra parameters not in the function being called. )<br /><br />Hope that helps someone out there...<br /><br />Regards<br />VernonAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-17332816.post-36236236892343770042010-07-26T23:23:04.330+08:002010-07-26T23:23:04.330+08:00Hello,
I faced a similar issue in porting an exec...Hello,<br /><br />I faced a similar issue in porting an execution control and logging routine to PowerShell.<br /><br />Usage:<br />Exec echo "Hello world"<br />Exec Configure-MySystem -Verbose<br /><br />The solution was to let Invoke-<br />Expression itself decide if a -Xxxx argument is a named parameter, or a "normal" string.<br /><br />Function Exec([string]$cmd) {<br /> $txtLine = "$cmd"<br /> $cmdLine = "$cmd"<br /> $a = $args # Make a copy (Necessary because Invoke-Expression overwrites $args)<br /> $n = 0<br /> foreach ($arg in $args) { # Parse all optional arguments following the command name.<br /> $txtLine += " $(Quote $arg)"<br /> if (($arg -ne $null) -and ($arg.GetType().FullName -eq "System.String") -and ($arg -match '^-\w+:?$')) {<br /> $cmdLine += " $arg" # Let Invoke-Expression decide whether it's a param name or an actual string.<br /> } else {<br /> $cmdLine += " `$a[$n]" # Preserve the type through Invoke-Expression.<br /> }<br /> $n += 1<br /> }<br /> if ($script:Verbose -or $script:NoExec) {<br /> Put-Line $txtLine<br /> }<br /> if (!$script:NoExec) {<br /> if (!$script:Verbose) {Put-Line $txtLine}<br /> Invoke-Expression $cmdLine<br /> }<br />}<br /><br />Jean-Francois LarvoireAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-17332816.post-36689041980337851652010-07-03T22:14:41.689+08:002010-07-03T22:14:41.689+08:00OK. here's the script that can be used as a pa...OK. here's the script that can be used as a pass-through, and brings in %~dp0 for extra points :-) Fell free to add more :-))<br /><br />Run it from cmd.exe like : pass.cmd foo.ps1 <br /><br />and then inside foo.ps1 call the next one like:<br /><br />& "${Here}pass.cmd ${Here}next.ps1 ${NextArgs}"<br /><br />prepare ${NextArgs} like:<br /><br />$ParsedArgs = $myInvocation.BoundParameters # survies . sourcing btw<br /><br />$ParsedArgs.Remove("MyName") > $Null<br />$NextArgs = @();<br />$ParsedArgs.Keys |% { $NextArgs += '-'+$_; $NextArgs += $ParsedArgs[$_]; }<br />$NextArgs += $args;<br /><br />Again, you'll probably be able to do a bit better, but this provides basic mechanism and opens path to run interference from normal .cmd for anything else you may need.<br /><br />-------- pass.cmd ----------<br />@echo off<br />setlocal<br /><br />set pshPath="%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe"<br /><br />set here=%~dp0<br />set script=%~dp0%1<br /><br />%pshPath% %script% -Here %here% -MyName %*<br /><br />endlocalAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-17332816.post-61685614226699686032010-07-03T20:03:36.499+08:002010-07-03T20:03:36.499+08:00PowerShell is simply broken regarding arg passing ...PowerShell is simply broken regarding arg passing and parsing for any practical purposes. Probably beyond repair (if no one wanted to fix that by 2.0 ...). <br /><br />One core issue is that scripts are made to act like "functions", and that means expecting pre-parsed args as "objects" and no facility to parse them on the spot.<br /><br />Untill someone makes explicit, declarative distinction between actual function (well defined params and return value, straight invocation, no dumping into "pipe"), procedure (well defined params but undefined output - dumps into "pipe" at will) and script (loosely defined params, always parses on invocation as if it's a command line and a raw string of chars, undefined output) this is always going to be in limbo.<br /><br />If you have several levels of scripts you'd have to capture original $psBoundParameters and $args separately, carry them around as special args to lower level scripts, plus do splatting.<br /><br />Then you'd have to merge local and original $psBoundParameters at some point (so that you don't have to replicate huge top level signature everywhere) and then either parse the rest of $args manually (if your lower level script needs a lot of extras) or recondition merged $psBoundParameters to pass them passing them to a non-ps1 executable, even a cmd with sole purpose of breaking the vicious cycle and forcing next .ps1 script to actually parse command line. Beware of .cmd rules though (got to protect your args with quotes and do escapes for special chars). <br /><br />Now, if some intermediary scripts are used as stand alone as well, they have to go checking if they received arg captures or not and process their args differently based on that.<br /><br />Perhaps it's high time that someone just publishes a function that does normal arg parsing. It would be a long function but at least then we can simply keep and pass everything as a cmd string, forget the whole mess and live happily ever after - more or less :-)<br /><br />Come to think of it, injecting a .cmd before every further .ps1 script invocation might still be less frustrating at the end of the day than dealing with the whole mess - haven't tried though.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-17332816.post-45277128376413994792010-06-09T20:45:01.473+08:002010-06-09T20:45:01.473+08:00Sure, and that's pretty much where I ended up,...Sure, and that's pretty much where I ended up, but it was exactly that kind of pre-defined pattern I was attempting to avoid.<br /><br />Actually neither scenario were plugins. One was a powershell based test runner running a tree structure of tests, the other was a build script executing child scripts which represent build steps.<br /><br />In both case the child scripts could have parameters that the parent need not know about (they will default) but it would be useful to have a generic 'pass though' mechanism in case the caller of the parent needed to set them.<br /><br />I'm still surprised that splatting an array doesn't appear to *ever* do any named argument matching.piers7https://www.blogger.com/profile/11186470645521299750noreply@blogger.comtag:blogger.com,1999:blog-17332816.post-71442158597602457302010-06-08T00:18:08.807+08:002010-06-08T00:18:08.807+08:00It sounds like you're trying to do the equival...It sounds like you're trying to do the equivalent of a plugin / extension system. You drop a plugin (implemented as a PowerShell script) into some directory, and then the controller script runs it.<br /><br />The problem that you're running into is that this model doesn't enforce any strictness in the system. You're trying to support an arbitrary definition of the “control” script, and arbitrary definitions of the child scripts. Ultimately, something has to have structure. Since there is no structure defined, there really are no features to come to the rescue :)<br /><br />To be successful, this is normally done with the child scripts following a pre-defined pattern:<br /><br />foreach($script in (Get-ChildItem *.ps1))<br />{<br /> & $script –Argument1 -Argument2 -Argument3 <br />}<br /><br />Or, via splatting:<br /><br />foreach($script in (Get-ChildItem *.ps1))<br />{<br /> $arguments = @{ Argument1 = value; Argument2 = value; Argument3 = value }<br /> & $script @arguments<br />}<br /><br />Lee Holmes [MSFT]<br />Windows PowerShell Development<br />Microsoft CorporationAnonymousnoreply@blogger.com