How does PowerShell split unquoted command-line arguments? - powershell

PowerShell does not split command arguments in the same way as cmd.exe or sh. The behaviour I'm stumbling over is that a . character sometimes, but not always, starts a new argument.
To illustrate:
PS D:\temp> gc .\list-args.py
import sys
print(*sys.argv, sep='\n')
PS D:\temp> py .\list-args.py a.txt .\b -c=d -e=.\f -g.h -.i=j
.\list-args.py
a.txt
.\b
-c=d
-e=
.\f
-g
.h
-.i=j
PS D:\temp>
What rules does Powershell use to separate one command-line argument from the next?
Where are these documented?

Here's echoargs output (http://ss64.com/ps/EchoArgs.exe).
.\echoargs .\list-args.py a.txt .\b -c=d -e=.\f -g.h -.i=j
Arg 0 is <.\list-args.py>
Arg 1 is <a.txt>
Arg 2 is <.\b>
Arg 3 is <-c=d>
Arg 4 is <-e=>
Arg 5 is <.\f>
Arg 6 is <-g>
Arg 7 is <.h>
Arg 8 is <-.i=j>
One quoting workaround:
.\echoargs .\list-args.py a.txt .\b -c=d -e='.\f' '-g.h' -.i=j
Arg 0 is <.\list-args.py>
Arg 1 is <a.txt>
Arg 2 is <.\b>
Arg 3 is <-c=d>
Arg 4 is <-e=.\f>
Arg 5 is <-g.h>
Arg 6 is <-.i=j>
. might be used for property references.
$a = [pscustomobject]#{name='joe'}
write-output $a.name
joe
The command line parses arrays too.
$b = 1,2,3
write-output $b[0]
1

Related

Why does an argument $args after = sign not get expanded? ($args as parameter to jvm typical -D switch)

I have the following definition in my *profile.ps1 file:
if(Test-Path $env:M2_HOME){
function mvn{
$cmd = "$env:M2_HOME\bin\mvn.bat"
& $cmd $args
}
}
When I define a function using this function in powershell like:
function d { mvn help:describe $args }
using like:
d -Dplugin=jar
everything is fine as opposed to defining the latter as:
function d { mvn help:describe -Dplugin=$args }
using like:
d jar
Is there some builtin to handle this corner case?
It looks like you just need to make sure you're passing the arguments as strings and ensure they're evaluated first:
function mvn{
$cmd = "$env:M2_HOME\bin\mvn.bat"
& $cmd $args
}
function d { mvn "help:describe" "-Dplugin=$($args)" }
for get argument through call function you should use like this
function test {
write-host $args[0]
write-host $args[1]
}
test stackoverflow powershell
output
stackoverflow
powershell
stackoverflow is first argument passed to function and powershell is second argument passed to function
for every argument passed to function
function test {
foreach ($a in $args){
write-host "output:$args"
}
}
test 1 2 3 4 5 6 7 8
output:
test 1 2 3 4 5 6 7 8
output:1 2 3 4 5 6 7 8
output:1 2 3 4 5 6 7 8
output:1 2 3 4 5 6 7 8
output:1 2 3 4 5 6 7 8
output:1 2 3 4 5 6 7 8
output:1 2 3 4 5 6 7 8
output:1 2 3 4 5 6 7 8
output:1 2 3 4 5 6 7 8
for your function
if(Test-Path $env:M2_HOME){
function mvn{
$cmd = "$env:M2_HOME\bin\mvn.bat"
foreach ($arg in $args) {
& $cmd $args}
}
}
Someone seems to have made a similar observation:
The reason seems -D being a special character in powershell more or less marking where exactly each particluar special option string determined by -D ends, at least this modification to my calling function then works for me:
function x {mvn help:describe `-Dplugin=$args}
Sure it would be nice to handle such occasions in the hosting function somehow (mvn definition in my *profile.ps1), but that solutions rather seems out of scope of my question.

Re-Use of a line fails as "`r" doesn't move the 'line-pointer' ("`b" fails as well)

As C# does not has the options of powershell's write-progress I start to try to 're-use' the same line:
function ReUseFails {
$x=0
$nDec = 10
while ($true) {
$x++
$a = $x.ToString().PadLeft($nDec)
$z = $x.ToString().PadLeft($nDec)
Write-Host "`r$a $z" -noNewLine
Start-Sleep -s 1
}
}
ReUseFails
I expected because of `r (carriage return) to see in the same line (the next line overrides the prev.):
1 1 # and after 1 Second in that line:
2 2 # after the 2nd second
3 3 # (and so on)
but what I get is
1 1 2 2 3 3 4 4 ...
So the carriage return r has no effect?<br>
Is there a way to 'enable' thisr??
Even when I try to use `b I see additional spots but not a back-space.
Can it be that the DOS-console can do that (to show a spinning wheel) but bot the PS-console?
Regards

Power shell Pass argument

I am passing $nextday and $currrentyear to the .exe which I am executing within my powershell script. It seems that the arguments do not pass, why?
$nextday = (Get-Date).AddDays(-1).ToString("M/dd/yyyy")
$currentgetyear = (Get-Date).ToString("yyyy")
& "C:\tmt.exe" YEAR= $currentgetyear DATE= $nextday
Why do you use space between the words? See how it's parsed by the software:
[21:50:46] > & echoargs YEAR= $currentgetyear DATE= $nextday
Arg 0 is <YEAR=>
Arg 1 is <2013>
Arg 2 is <DATE=>
Arg 3 is <2.26.2013>
This however is parsed how I guess it should be.
[21:50:58] > & echoargs YEAR=$currentgetyear DATE=$nextday
Arg 0 is <YEAR=2013>
Arg 1 is <DATE=2.26.2013>
So my solution: remove the spaces, like this:
& "C:\tmt.exe" YEAR=$currentgetyear DATE=$nextday
Try
& "C:\tmt.exe" ("YEAR={0}" -f (get-date).Year) ("DATE={0:M/dd/yyyy}" -f (get-date).AddDays(-1))
Bill

Powershell - Load Arguments from a file

This is my 2nd question is fairly quick succession! Essentially at the moment, I'm running a powershell script which I execute manually and pass arguments to on the CMD line like:
PostBackupCheck.Ps1 C 1 Hello Test Roger
Those are placed into variables and used in the script.
Is there a way I could add these line by line to a text file such like:
C 1 Hello Test Roger 0
C 2 Hello Test Roger 1
C 3 Hello Test Roger 2
And then get the Powershell script to use the first line, do the script, then loop back and use the next line, do the script, loop back and so on.
So in context - I need to mount images in the following naming context
SERVERNAME_DRIVELETTER_b00x_ixxx.spi
Where,
SERVERNAME = Some string
DRIVELETTER = Some Char
b00X - where X is some abritrary number
ixxx - where xxx is some abritrary number
So in my text file:
MSSRV01 C 3 018
MSSRV02 D 9 119
And so on. It uses this information to mount a specific backup image (via ShadowProtect's
mount.exe SERVERNAME_DRIVELETTER_b00x_ixxx.spi
Thanks!
You can try do something like this:
content of p.txt:
C 1 Hello Test Roger 0
C 2 Hello Test Roger 1
C 3 Hello Test Roger 2
the content of the script p.ps1
param($a,$b,$c,$d,$e,$f)
"param 1 is $a"
"param 2 is $b"
"param 3 is $c"
"param 4 is $d"
"param 5 is $e"
"param 6 is $f"
"End Script"
the call to script:
(Get-Content .\p.txt ) | % { invoke-expression ".\p.ps1 $_" }
the result:
param 1 is C
param 2 is 1
param 3 is Hello
param 4 is Test
param 5 is Roger
param 6 is 0
End Script
param 1 is C
param 2 is 2
param 3 is Hello
param 4 is Test
param 5 is Roger
param 6 is 1
End Script
param 1 is C
param 2 is 3
param 3 is Hello
param 4 is Test
param 5 is Roger
param 6 is 2
End Script
Edit:
after your edit you can try something like this.
Get-Content .\p.txt |
% { $a = $_ -split ' ' ; mount.exe $($a[0])_$($a[1])_b00$($a[2])_i$($a[3]).spi }

Modifiers in Makefile rule's dependency list

The problem is fairly simple. I am trying to write a rule, that given the name of the required file will be able to tailor its dependencies.
Let's say I have two programs: calc_foo and calc_bar and they generate a file with output dependent on the parameter. My target would have a name 'target_*_*'; for example, 'target_foo_1' would be generated by running './calc_foo 1'.
The question is, how to write a makefile that would generate outputs of the two programs for a range of parameters?
If there are just a few programs, you can have a rule for each one:
target_foo_%:
./calc_foo $*
If you want to run a program with a list of parameters:
foo_parameter_list = 1 2 green Thursday 23 bismuth
foo_targets = $(addprefix target_foo_,$(foo_parameter_list))
all: $(foo_targets)
If you want a different set of parameters for each program, but with some in common, you can separate the common ones:
common_parameter_list = 1 2 green Thursday
foo_parameter_list = $(common_parameters) 23 bismuth
bar_parameter_list = $(common_parameters) 46 111
If it turns out you have more programs than you thought, but still want to use this method, you just want to automate it:
# add programs here
PROGRAMS = foo bar baz
# You still have to tailor the parameter lists by hand
foo_parameter_list = 1 2 green Thursday 23 bismuth
# everything from here on can be left alone
define PROGRAM_template
$(1)_targets = $(addprefix target_$(1)_,$($(1)_parameter_list))
target_$(1)_%:
./calc_$(1) $$*
all: $(1)_targets
endef
$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
This seems to do more or less what you are requesting - assuming you are using GNU Make.
Makefile
BAR_out = target_bar_
BAR_list = 1 2 3 4 5 6 7 8 9
BAR_targets = $(addprefix ${BAR_out},${BAR_list})
FOO_out = target_foo_
FOO_list = 11 12 13 14 15 16 17 18 19
FOO_targets = $(addprefix ${FOO_out},${FOO_list})
all: ${BAR_targets} ${FOO_targets}
${BAR_targets}:
calc_bar $(subst ${BAR_out},,$#)
${FOO_targets}:
calc_foo $(subst ${FOO_out},,$#)
It can probably be cleaned up, but I tested it with the commands:
calc_bar
echo "BAR $#" | tee target_bar_$#
sleep $#
calc_foo
echo "FOO $#" | tee target_foo_$#
sleep $#
Clearly, if you want a different list, you can specify that on the command line:
make -j4 FOO_LIST="1 2 3 4 5 6 33"