Changes between Initial Version and Version 1 of Programming/PowerShell/HowToPassCommandLineArgumentsBetweenScripts


Ignore:
Timestamp:
Feb 19, 2016, 3:06:15 PM (8 years ago)
Author:
Vijay Varadan
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Programming/PowerShell/HowToPassCommandLineArgumentsBetweenScripts

    v1 v1  
     1= HOWTO: Powershell - Pass Command Line Arguments Between Scripts =
     2Jan 22 2016
     3
     4[[Image(htdocs:images/powershell/PowerShell_5.0_icon.png, alt="PowerShell_5.0_icon", align=center)]]
     5
     6=== Scenario ===
     7
     8I have to invoke different sets of scripts conditionally for setting up the build environment based on which project(s) I want to build. This is related to porting my build environment scripts from bash to Powershell.
     9
     10Let's say, `push0.ps1` is the main script and there are three other scripts, viz. `quake.push0.ps1`, `doom.push0.ps1` and `wolf.push0.ps1`. I want to invoke `push0.ps1` with some arguments from the command line and have `push0.ps1` invoke one of the `*.push0.ps1` scripts while passing all the command line arguments that `push0.ps1` was invoked with.
     11
     12A bit of code would help make the example concrete:
     13{{{#!html
     14<pre><strong>push0.ps1</strong>
     15<span style="color: #008000;"># $args is the array of input arguments to any script
     16# Do some work here common to all projects</span>
     17<span style="color: #008000;"># like pushing code to the master repo for developer scripts, etc.</span>
     18
     19<span style="color: #008000;"># invoke project specific scripts here
     20# $Env:Project will be set to quake, doom or wolf</span>
     21<span style="color: #008000;"># call project specific push0 script if it exists </span>
     22<span style="color: #008000;"># and pass all arguments to it</span>
     23$scriptName = $Env:Project + ".push0.ps1"
     24if (Test-Path $scriptName) {
     25    &amp; $scriptName $args
     26}</pre>
     27}}}
     28
     29----
     30
     31{{{#!html
     32<pre><strong>quake.push0.ps1</strong>
     33<span style="color: #008000;"># do quake specific work here
     34</span>$pushFolder = Join-Path $Env:QuakeBaseDir "win32"
     35cd $pushFolder
     36hg push
     37
     38$winSpecificFolder = Join-Path $pushFolder $Env:QuakeWinSubDir
     39cd $winSpecificFolder
     40hg push
     41
     42<span style="color: #008000;"># now push any external sub-repos
     43</span>$quakeExtBaseDir = Join-Path $Env:ExtBaseDir $QuakeExtWin32SubDir
     44$args | % {
     45    $extFolder = Join-Path $quakeExtBaseDir $_
     46    cd $extFolder
     47    hg push
     48}
     49</pre>
     50}}}
     51
     52----
     53
     54{{{#!html
     55<pre><strong>doom.push0.ps1</strong>
     56<span style="color: #008000;"># do doom specific work here
     57</span>$pushFolder = Join-Path $Env:DoomBaseDir "win16"
     58cd $pushFolder
     59hg push
     60
     61$win16SpecificFolder = Join-Path $pushFolder $Env:DoomWin16SubDir
     62cd $win16SpecificFolder
     63hg push
     64
     65<span style="color: #008000;"># now push assembly language library repos
     66</span>$doomAsmBaseDir = Join-Path $Env:AsmLibBaseDir $doomAsmSubDir
     67$args | % {
     68    $asmFolder = Join-Path $doomAsmBaseDir $_
     69    cd $asmFolder
     70    hg push
     71}</pre>
     72}}}
     73
     74----
     75
     76{{{#!html
     77<pre><strong>wolf.push0.ps1</strong>
     78<span style="color: #008000;"># do wolf specific work here
     79</span>$pushFolder = Join-Path $Env:WolfBaseDir "dos"
     80cd $pushFolder
     81hg push
     82
     83$dosSpecificFolder = Join-Path $pushFolder $env:WolfDosSubDir
     84cd $dosSpecificFolder
     85hg push
     86
     87<span style="color: #008000;"># now push dos assembly language library repos
     88</span>$wolfAsmBaseDir = Join-Path $Env:AsmLibBaseDir $wolfAsmSubDir
     89$args | % {
     90    $asmFolder = Join-Path $wolfAsmBaseDir $_
     91    cd $asmFolder
     92    hg push
     93}
     94</pre>
     95}}}
     96
     97As you can see from the example above, the three project specific scripts push different sub-repos that are passed in as parameters.
     98
     99=== Problem ===
     100
     101The three project specific scripts `{quake|doom|wolf}.push0.ps1`, work fine as they are now, if they are individually invoked from Powershell. But if they are  invoked from another Powershell script, then they fail processing the arguments passed in via `$args`.
     102
     103The issue is that when `$args` is passed to `push0.ps1`, it's an array of parameters, but when you invoke another script from within `push0.ps1`, the `$args` array (i.e. all the parameters) is treated as a single parameter passed to the callee script. If I add a line to print out the arguments in `quake.push0.ps1` like this:
     104
     105{{{#!html
     106<pre><strong>quake.push0.ps1</strong>
     107...
     108...
     109Write-Host "Quake:- args[=" $args.Count "]: " $args
     110...
     111...</pre>
     112}}}
     113
     114And invoke `push0.ps1` passing in some arguments, then the output looks like this:
     115
     116{{{#!html
     117<pre>$&gt; .\push0.ps1 boost libssl lua
     118Quake:- args[=<span style="color: #ff0000;"><strong>1</strong></span>]: boost libssl lua</pre>
     119  }}}
     120
     121As you can see, even though all 3 parameters are passed in, they are seen as <em>a single parameter</em>.
     122
     123Essentially it would appear, that when the callee script is invoked, all 3 parameters as passed in as `$args[0]`. Now, Powershell is an object scripting language and it seems to pass an <em>array of strings</em> from the caller script as the first and only parameter to the callee script.
     124
     125Knowing that since Powershell is built on top of .NET and deals with objects, we can verify this by checking the type of $args[0] like this:
     126
     127{{{#!html
     128<pre><strong>quake.push0.ps1</strong>
     129...
     130...
     131Write-Host $args[0].GetType().IsArray
     132...
     133...
     134
     135<strong>OUTPUT</strong>
     136<span style="color: #0000ff;">True</span></pre>
     137}}}
     138
     139And we're in business! :-)
     140
     141=== Solution ===
     142
     143What we need to do in the callees, is check if the incoming $args has only 1 parameter and if it's an array. If so, then simply use $args[0] as the actual list of arguments and process it below.
     144
     145Since this bit of the code is common to multiple scripts, I put it in a function and stick it in the $PROFILE file or some other file that can be dot-sourced. Here's the function:
     146{{{#!html
     147<pre><strong><span style="color: #0000ff;">function UnwrapArguments($params) {</span></strong>
     148<strong><span style="color: #0000ff;">    $unwrapped = @()</span></strong>
     149<strong><span style="color: #0000ff;">    if ($params.Count -gt 0) {</span></strong>
     150<strong><span style="color: #0000ff;">        if ($params.Count -eq 1 -and $params[0].GetType().IsArray) {</span></strong>
     151<strong><span style="color: #0000ff;">            $unwrapped = $params[0]</span></strong>
     152<strong><span style="color: #0000ff;">        } else {</span></strong>
     153<strong><span style="color: #0000ff;">            $unwrapped = $params</span></strong>
     154<strong><span style="color: #0000ff;">        }</span></strong>
     155<strong><span style="color: #0000ff;">    }</span></strong>
     156<strong><span style="color: #0000ff;">    return $unwrapped</span></strong>
     157<strong><span style="color: #0000ff;">}</span></strong></pre>
     158}}}
     159
     160So our scripts end up looking like this (changes in blue):
     161
     162{{{#!html
     163<pre><strong>push0.ps1
     164<span style="color: #0000ff;"># NOTE: No changes to the caller.</span></strong>
     165<span style="color: #008000;"># $args is the array of input arguments to any script
     166# Do some work here common to all projects</span>
     167<span style="color: #008000;"># like pushing code to the master repo for developer scripts, etc.</span>
     168
     169<span style="color: #008000;"># invoke project specific scripts here
     170# $Env:Project will be set to quake, doom or wolf</span>
     171<span style="color: #008000;"># call project specific push0 script if it exists </span>
     172<span style="color: #008000;"># and pass all arguments to it</span>
     173$scriptName = $Env:Project + ".push0.ps1"
     174if (Test-Path $scriptName) {
     175    &amp; $scriptName $args
     176}</pre>
     177}}}
     178
     179----
     180
     181{{{#!html
     182<pre><strong>quake.push0.ps1
     183<span style="color: #0000ff;">$myargs = UnwrapArguments($args)
     184</span></strong>
     185<span style="color: #008000;"># do quake specific work here
     186</span>$pushFolder = Join-Path $Env:QuakeBaseDir "win32"
     187cd $pushFolder
     188hg push
     189
     190$winSpecificFolder = Join-Path $pushFolder $Env:QuakeWinSubDir
     191cd $winSpecificFolder
     192hg push
     193
     194<span style="color: #008000;"># now push any external sub-repos
     195</span>$quakeExtBaseDir = Join-Path $Env:ExtBaseDir $QuakeExtWin32SubDir
     196<span style="color: #0000ff;"><strong>$myargs</strong></span> | % {
     197    $extFolder = Join-Path $quakeExtBaseDir $_
     198    cd $extFolder
     199    hg push
     200}
     201</pre>
     202}}}
     203
     204----
     205
     206{{{#!html
     207<pre><strong>doom.push0.ps1
     208<span style="color: #0000ff;">$myargs = UnwrapArguments($args)
     209</span></strong>
     210<span style="color: #008000;"># do doom specific work here
     211</span>$pushFolder = Join-Path $Env:DoomBaseDir "win16"
     212cd $pushFolder
     213hg push
     214
     215$win16SpecificFolder = Join-Path $pushFolder $Env:DoomWin16SubDir
     216cd $win16SpecificFolder
     217hg push
     218
     219<span style="color: #008000;"># now push assembly language library repos
     220</span>$doomAsmBaseDir = Join-Path $Env:AsmLibBaseDir $doomAsmSubDir
     221<strong><span style="color: #0000ff;">$myargs</span></strong> | % {
     222    $asmFolder = Join-Path $doomAsmBaseDir $_
     223    cd $asmFolder
     224    hg push
     225}</pre>
     226}}}
     227
     228----
     229
     230{{{#!html
     231<pre><strong>wolf.push0.ps1
     232<span style="color: #0000ff;">$myargs = UnwrapArguments($args)
     233</span></strong>
     234<span style="color: #008000;"># do wolf specific work here
     235</span>$pushFolder = Join-Path $Env:WolfBaseDir "dos"
     236cd $pushFolder
     237hg push
     238
     239$dosSpecificFolder = Join-Path $pushFolder $env:WolfDosSubDir
     240cd $dosSpecificFolder
     241hg push
     242
     243<span style="color: #008000;"># now push dos assembly language library repos
     244</span>$wolfAsmBaseDir = Join-Path $Env:AsmLibBaseDir $wolfAsmSubDir
     245<strong><span style="color: #0000ff;">$myargs</span></strong> | % {
     246    $asmFolder = Join-Path $wolfAsmBaseDir $_
     247    cd $asmFolder
     248    hg push
     249}
     250</pre>
     251}}}
     252
     253That's it. We're golden.