1

When I pass parameters to a JAR, the Java application generates a PDF file which contains the values of the parameters passed, but the problem is when one these parameters is empty, one parameter takes the value of another one.

Example:

d:
cd D:\JavaApp\ITEquipmentPassV2
$Name=@"
$[Name]
"@
$Region=@"
$[Region]
"@
$Device=@"
$[Device]
"@
$ForHO=@"
$[ForHO]
"@
$Ex_Pas=@"
$[Ex_Pa]
"@
$Ex_Pass_Nr=@"
$[Ex_Pass_Nr]
"@
$Monitor=@"
$[Monitorl]
"@
java -jar JavaApp_v2.jar $Name $Region $Device $ForHO $Ex_Pas $Ex_Pass_Nr $Monitor

If $Ex_Pas is empty or contains a special character then $forHo takes the value of $Monitor.

Abra
  • 19,142
  • 7
  • 29
  • 41
ajra
  • 31
  • 4
  • 2
    What's with the `$[...]` syntax? Are you running your script through some sort of template engine to generate the correct arguments? – Mathias R. Jessen Aug 16 '23 at 13:38
  • Shouldn't you be quoting those parameters? – g00se Aug 16 '23 at 13:49
  • 1
    You need double quote the params. e.g. java -jar JavaApp_v2.jar "$Name" "$Region" "$Device" "$ForHO" "$Ex_Pas" "$Ex_Pass_Nr" "$Monitor" – Tristate Aug 16 '23 at 13:54
  • @Tristate: In PowerShell - unlike in `cmd.exe` and in POSIX-compatible shells such as Bash - variable references used as command arguments do NOT need double-quoting (even if they contain spaces or other shell metacharacters). – mklement0 Aug 16 '23 at 23:15
  • @ajra: A variable containing the _empty string_ indeed causes a problem _on the PowerShell side_, as described below. _Special characters_ are _not_ a problem on the PowerShell side, though can be for target programs with nonstandard argument parsing, such as batch files. – mklement0 Aug 17 '23 at 13:23

1 Answers1

1

Note:

  • The symptom is likely caused by variables containing the empty string (see below), even though the sample argument values in the question never result in such, given that no string interpolation is performed, because the syntax form $[var] isn't a valid variable reference.

  • You also mention special characters as a problem, though that is unlikely, given that PowerShell passes them through correctly, even with variable references not enclosed in "...", the latter generally not being necessary in PowerShell (e.g., some.exe $var is sufficient, no need for some.exe "$var").


but the problem is when if one these parameter is empty, one parameter takes the value of another one

You're likely seeing a long-standing bug present in Windows PowerShell and in PowerShell (Core) up to version 7.2.x - it has been fixed in v7.3+, with selective exceptions on Windows - see this answer for details (it focuses on arguments with embedded double-quotes, but the issue at hand is part of the problem).

  • Trying to pass an empty string ("", '') as an argument to an external program is quietly ignored; that is, no argument is passed at all.

  • Note that, by contrast, passing $null is ignored by design.

Examples:

The following uses python to simply echo the arguments it was passed.

# Pass an empty-string *literal*
python -c 'import sys; print(sys.argv[1:])' '' hi

# Pass an empty string via a *variable*
$someArg=''
python -c 'import sys; print(sys.argv[1:])' $someArg hi
  • The expected output is ['', 'hi']
  • With the bug present, the output is ['hi'], indicating that the empty string wasn't passed at all.

Workarounds:

  • Passing an empty string literally:

    • Use '""' (sic) (`"`" works too):

      # In WinPS and PS Core v7.2.x- only
      # (In v7.3+, use it only with $PSNativeCommandArgumentPassing = 'Legacy'.)
      python -c 'import sys; print(sys.argv[1:])' '""' hi
      
    • Alternatively, on Windows only, use --%, the stop-parsing token in combination with "" (not ''), but note its many limitations, detailed in the bottom section of this answer, notably the inability to embed PowerShell variables and expressions:

      # Windows only.
      python -c 'import sys; print(sys.argv[1:])' --% "" hi
      
  • Passing an empty string via variables that may or may not contain the empty string:

    • With an individual argument:

      $someArg = ''
      python -c 'import sys; print(sys.argv[1:])' ($someArg, '""')[$someArg -is [string] -and -not $someArg] hi
      
    • With an array of arguments:

      $someArgs = '', 'hi'
      python -c 'import sys; print(sys.argv[1:])' $someArgs.ForEach({
          ($_, '""')[$_ -is [string] -and -not $_]
        })
      
    • Explanation of the ($someArg, '""')[$someArg -is [string] -and -not $someArg] technique:

      • It is the concise equivalent of:

        if ($someArg -is [string] -and -not $someArg) { '""' } else { $someArg }
        
      • In PowerShell (Core) 7+, you can also use the ternary conditional operator

         ($someArg -is [string] -and -not $someArg ? '""' : $someArg)
        
      • It emulates a conditional using an array to represent the return values, and relies on a Boolean value getting implicitly converted to an integer in the context of an index expression ([...]), with $false becoming 0, and $true becoming 1, so that the appropriate array element is returned.[1]

      • If you know $someArg to always be a string, you can simplify to one of the following:

        ($someArg, '""')[-not $someArg]
        # PS v7+ only
        (-not $someArg ? '""' : $someArg)
        
      • All variants rely on PowerShell's implicit to-Boolean conversions, which treat an empty string as $false, and any nonempty one as $true. The complete rules are summarized in the bottom section of this answer.


Module-based polyfill:

If you need to write cross-edition, cross-version PowerShell code, the Native module (Install-Module Native; authored by me), has an ie function (short for: Invoke Executable), which is a polyfill that provides workaround-free, cross-edition (v3+), cross-platform, cross-version, forward-compatible behavior in the vast majority of cases, both for passing empty-string arguments and arguments with embedded " chars. correctly - simply prepend ie to your external-program calls:

# With the 'Native' module installed, simply prepending `ie` 
# to your external-program calls ensures that both empty-string
# arguments and arguments with embedded " chars. are passed correctly.
ie python -c 'import sys; print(sys.argv[1:])' '' hi

[1] Small caveat: unlike true conditionals, this shortcut performs no short-circuiting; that is, both array elements are evaluated up front, before the appropriate element is selected by the index expression.

mklement0
  • 382,024
  • 64
  • 607
  • 775