I downloaded the npm package for merge junit reports - https://www.npmjs.com/package/junit-merge.
The problem is that I have multiple files to merge and I am trying to use string variable to hold file names to merge.
When I write the script myslef like:
junit-merge a.xml b.xml c.xml
This works, the merged file is being created, but when I do it like
$command = "a.xml b.xml c.xml"
junit-merge $command
This does not work. The error is
Error: File not found
Has anyone faced similar issues?
results in command line
junit-merge "a.xml b.xml c.xml"
[1], i.e. it passes a string with verbatim valuea.xml b.xml c.xml
as a single argument tojunit-merge
, which is not the intent.PowerShell does not act like POSIX-like shells such as
bash
do in this regard: Inbash
, the value of variable$command
- due to being referenced unquoted - would be subject to word splitting (one of the so-called shell expansions) and would indeed result in 3 distinct arguments (though even there an array-based invocation would be preferable).PowerShell supports no
bash
-like shell expansions[2]; it has different, generally more flexible constructs, such as the splatting technique discussed below.Instead, define your arguments as individual elements of an array, as justnotme advises:
This is an application of a PowerShell technique called splatting, where you specify arguments to pass to a command via a variable:
Either (typically only used for external programs, as in your case):
Or (more typically when calling PowerShell commands):
As a hashtable to pass named parameter values, in which case you must replace the
$
sigil in the variable reference with@
; e.g., in your case@command
; e.g., the following is the equivalent of callingGet-ChildItem C:\ -Directory
:$paramVals = @{ LiteralPath = 'C:\'; Directory = $true }; Get-ChildItem @paramVals
Caveat re array-based splatting:
Due to a bug detailed in GitHub issue #6280, PowerShell doesn't pass empty arguments through to external programs (applies to all Windows PowerShell versions / up to PowerShell (Core) 7.2.x. This has been fixed in 7.3, with selective exceptions on Windows, in conjunction with fixing how arguments with embedded
"
are passed - see the$PSNativeCommandArgumentPassing
preference variable.E.g., up to PowerShell v7.2.x,
foo.exe ""
unexpectedly results in justfoo.exe
being called.This problem equally affects array-based splatting, so that
$cmdArgs = "", "other"; foo.exe $cmdArgs
results infoo.exe other
rather than the expectedfoo.exe "" other
.The workaround (which also applies in v7.3+ if
$PSNativeCommandArgumentPassing = 'Legacy'
is set) is to use'""'
(sic).Optional use of
@
in array-based splatting with external programs:As noted, with external programs use of
@
in lieu of$
isn't necessary, because passing an array implicitly results in splatting. (By contrast, when calling a PowerShell command to which you want to pass the elements of an array as individual, positional arguments, you must use@
)However, you may choose to use
@
with external programs too, and arguably it conveys the splatting intent more clearly:There is a subtle behavioral distinction, however - though it will probably rarely if ever surface in practice:
The safer choice is to use
$
, because it guards against (the however hypothetical) accidental misinterpretation of a array element containing--%
that you intend to be passed on as-is.Only the
@
syntax recognizes an array element with verbatim value--%
as the special stop-parsing token,--%
Said token tells PowerShell not to parse the remaining arguments as it normally would and instead pass them through as-is - unexpanded, except for expanding
cmd.exe
-style variable references such as%USERNAME%
.This is normally only useful when not using splatting, typically in the context of being able to use command lines that were written for
cmd.exe
from PowerShell as-is, without having to account for PowerShell's syntactical differences.In the context of splatting, however, the behavior resulting from
--%
is non-obvious and best avoided:As in direct argument passing, the
--%
is removed from the resulting command line.Argument boundaries are lost, so that a single array element
foo bar
, which normally gets placed as"foo bar"
on the command line, is placed asfoo bar
, i.e. effectively as 2 arguments.[1] Your call implies the intent to pass the value of variable
$command
as a single argument, so when PowerShell builds the command line behind the scenes, it double-quotes the verbatima.xml b.xml c.xml
string contained in$command
to ensure that. Note that these double quotes are unrelated to how you originally assigned a value to$command
. Unfortunately, this automatic quoting is broken for values with embedded"
chars. - see this answer, for instance.[2] As a nod to POSIX-like shells, PowerShell does perform one kind of shell expansion, but (a) only on Unix-like platforms (macOS, Linux) and (b) only when calling external programs: Unquoted wildcard patterns such as
*.txt
are indeed expanded to their matching filenames when you call an external program (e.g.,/bin/echo *.txt
), which is feature that PowerShell calls native globbing.