Passing around command line $args in powershell , from function to function - command-line

This is a nasty issue I am facing. Wont be surprised if it has a simple solution, just that its eluding me.
I have 2 batch files which I have to convert to powershell scripts.
file1.bat
---------
echo %1
echo %2
echo %3
file2.bat %*
file2.bat
--------
echo %1
echo %2
echo %3
On command line, I invoke this as
C:> file1.bat one two three
The output I see is as expected
one
two
three
one
two
three
(This is a crude code sample)
When I convert to Powershell, I have
file1.ps1
---------
Write-Host "args[0] " $args[0]
Write-Host "args[1] " $args[1]
Write-Host "args[2] " $args[2]
. ./file2.ps1 $args
file2.ps1
---------
Write-Host "args[0] " $args[0]
Write-Host "args[1] " $args[1]
Write-Host "args[2] " $args[2]
When I invoke this on powershell command line, I get
$> & file1.ps1 one two three
args[0] one
args[1] two
args[2] three
args[0] one two three
args[1]
args[2]
I understand this is because $args used in file1.ps is a System.Object[] instead of 3 strings.
I need a way to pass the $args received by file1.ps1 to file2.ps1, much the same way that is achieved by %* in .bat files.
I am afraid, the existing manner will break even if its a cross-function call, just the way its a cross-file call in my example.
Have tried a few combinations, but nothing works.
Kindly help. Would much appreciate it.

In PowerShell V2, it's trivial with splatting. bar just becomes:
function bar { foo #args }
Splatting will treat the array members as individual arguments instead of passing it as a single array argument.
In PowerShell V1 it is complicated, there's a way to do it for positional arguments. Given a function foo:
function foo { write-host args0 $args[0] args1 $args[1] args2 $args[2] }
Now call it from bar using the Invoke() method on the scriptblock of the foo function
function bar { $OFS=','; "bar args: $args"; $function:foo.Invoke($args) }
Which looks like
PS (STA) (16) > bar 1 2 3
bar args: 1,2,3
args0 1 args1 2 args2 3
when you use it.

# use the pipe, Luke!
file1.ps1
---------
$args | write-host
$args | .\file2.ps1
file2.ps1
---------
process { write-host $_ }

Related

Param with more values

Early days with CMD and Batch I use the shift command.
How I do that with PowerShell?
Here my sample:
# test.ps1
# start ps script with parameters
param(
[string]$varmon)
if ($varmon) {
foreach ($var in $varmon) {
Write-Host $var
}
}
PS CLI: .\test.ps1 one two three
I get only "one". How can I start the script with more parameters than one?
You could use the automatic variable $args:
# test.ps1
# start ps script with parameters
foreach ($var in $args){
write-host $var
}
Arrays allow you to enter a variable number of arguments - at the end of the day $args is just an automatic array that's created for unassigned variables.
Array parameters are comma delimited, not space - example below.
test.ps1
# test.ps1
# start ps script with parameters
param(
[int[]]$numbers,
[string[]]$names
)
if($numbers){
Write-host "`nYou have entered the following numbers:"
foreach ($num in $numbers){
write-host "Number : $num"
Write-host "Square root: $([system.math]::sqrt($num))"
}
}
if($names){
Write-host "`nYou have entered the following names:"
foreach($name in $names){
Write-host $name
}
}
Example 1: Without using the parameter names, you will need to keep the arrays in order. So $numbers first, and $names second
PS CLI: .\test.ps1 4,9,16 john,jim,jane
Example 1: With parameter names, you can change the order.
PS CLI: .\test.ps1 -names john,jim,jane -numbers 4,9,16

Passing in $args variable to function

I have a script tester.ps1; the first thing it does is call a function (defined within the script itself) called main.
I need to pass in the automatic variable $args that was passed into it from the commandline.
How do I do this?
The following doesn't seem to work:
#Requires -Version 5.0
#scriptname: tester.ps1
function main($args) {
Write-Host $args
}
# Entry point
main $args
When I save this tester.ps1 and call it, the function doesn't see the passed-in parameter?
PS> . .\tester.ps1 hello world
From entry point: hello world
From Function:
In your example, simply removing $args from the main function declaration, would be enough to have the output you want.
Though, be aware that if you want to pass parameters by name, you need to call main with the splatting operator #, for example:
#Requires -Version 5.0
#scriptname: tester.ps1
function main($myString, $otherVar) {
Write-Host $myString
}
# Entry point
Write-Host "Not using splatting: " -NoNewline
main $args
Write-Host "Using splatting: " -NoNewline
main #args
Output:
PS> . .\test.ps1 -myString "Hi World" -otherVar foobar
Not using splatting: -myString Hi World -otherVar foobar
Using splatting: Hi World
Find more about the splatting operator # here
Based on Jeroen Mostert's comment*; the solution is below.
Basically I was incorrectly trying to 'overload' or 'shadow' the built-in $arg variable.
I just need to have a param with a different name like this:
#Requires -Version 5.0
function main($my_args) {
write-host "From Function:" $my_args
}
# Entry point
write-host "From entry point:" $args
main $args
> . .\tester.ps1 hello world
From entry point: hello world
From Function: hello world

Powershell command built dynamically -- Parameter Passing Issue

I am unable to understand Parameter passing behavior in Powershell.
Say, I have a script callScript.ps1
param($a="DefaultA",$b="DefaultB")
echo $a, $b
Say, another script calls callScript.ps1.
.\callScript.ps1
# outputs DefaultA followed by DefaultB as expected
.\callScript.ps1 -a 2 -b 3
# outputs 2 followed by 3 as expected
$arguments='-a 2 -b 3'
callScript.ps1 $arguments
# I expected output as previous statement but it is as follows.
# -a 2 -b 3
# DefaultB
How can I run a powershell script by constructing command dynamically as above?
Can you please explain why the script the $arguments is interpreted as $a variable in callScript.ps1?
what's happening here is that you're passing a string:
'-a 2 -b 3' as the parameter for $a
you need to specify the values within the param, if you really needed to do it as you have above (there's definitely a better way though) you could do this using Invoke-Expression (short iex)
function doprint {
param( $a,$b )
$a ; $b
}
$arg = '-a "yes" -b "no"'
"doprint $arg" | iex
you could also change your function to take in an array of values like this:
function doprint {
param( [string[]]$a )
$a[0] ; $a[1]
}
$arg = #('yes','no')
doprint $arg
As has already been hinted at, you can't pass a single string as your script is expecting two params - the string is taken as input for param $a, whilst param $b takes the default value.
You can however build a simple hash table containing your arguments, and then use splatting to pass them to the script.
The changes to your code are minimal:
$arguments=#{a="2";b="3"}
callScript.ps1 #arguments

Passing parameters to a PowerShell job [duplicate]

This question already has an answer here:
Parenthesis Powershell functions
(1 answer)
Closed 7 years ago.
I've been toying around with this dang parameter passing to powershell jobs.
I need to get two variables in the script calling the job, into the job. First I tried using -ArgumentList, and then using $args[0] and $args[1] in the -ScriptBlock that I provided.
function Job-Test([string]$foo, [string]$bar){
Start-Job -ScriptBlock {#need to use the two args in here
} -Name "Test" -ArgumentList $foo, $bar
}
However I realized that -ArgumentList gives these as parameters to -FilePath, so I moved the code in the scriptblock into its own script that required two parameters, and then pointed -FilePath at this script.
function Job-Test([string]$foo, [string]$bar){
$myArray = #($foo,$bar)
Start-Job -FilePath .\Prog\august\jobScript.ps1 -Name 'Test' -ArgumentList $myArray
}
#\Prog\august\jobScript.ps1 :
Param(
[array]$foo
)
#use $foo[0] and $foo[1] here
Still not working. I tried putting the info into an array and then passing only one parameter but still to know avail.
When I say no avail, I am getting the data that I need however it all seems to be compressed into the first element.
For example say I passed in the name of a file as $foo and it's path as $bar, for each method I tried, I would get args[0] as "filename path" and args[1] would be empty.
ie:
function Job-Test([string]$foo, [string]$bar){
$myArray = #($foo,$bar)
Start-Job -FilePath .\Prog\august\jobScript.ps1 -Name 'Test' -ArgumentList $myArray
}
Then I called:
$foo = "hello.txt"
$bar = "c:\users\world"
Job-Test($foo,$bar)
I had jobScript.ps1 simply Out-File the two variables to a log on separate lines and it looked like this:
log.txt:
hello.txt c:\users\world
#(empty line)
where it should have been:
hello.txt
c:\users\world
you don't need to call the function like you would in java. just append the two variables to the end of the function call Job-Test $foo $bar

Strange order of variables written from Write-Host within function

With my following code seems Write-Host puts out variables in a strange way (for me coming from C# at least).
Code is here
function Run(
[string] $command,
[string] $args
)
{
Write-Host 'from function - command is:' $command '.args is: ' $args
}
$cmd = "ping"
$args = "208.67.222.222"
Write-Host 'from main - command is:' $cmd '.args is: ' $args
Run("ping","208.67.222.222")
Output is here
from main - command is: ping .args is: 208.67.222.222
from function - command is: ping 208.67.222.222 .args is:
How come Write-Host from main works as I expect, but within the function it outputs all variables at the same time? How can I correct this behaviour?
$args in the function is an automatic variable. It is an array that contains all the arguments passed to the function.
Use something besides $args for your IP address variable.