0

I'm using arguments for a PowerShell script, but I found that if the second one isn't set, it's getting garbage, although it's possible I'm doing something wrong, since I'm having trouble finding examples with two passed in arguments. How can I verify that both are set?

This script is called from a very old perl script as shown:

#`"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -NoProfile -noninteractive -ExecutionPolicy bypass -Command "\\\\wserverp01\\kiosksupport\\Retailer_DeviceSN\\MoveSNToSprdsht_1.0.ps1 $service_report_xls"`;

In my script, MoveSNToSprdsht_1.0.ps1:

$out_pth = ""
$outFilePathExcel_pl = ""
$outFilePathExcel_pl = $Args[0]
$out_pth = $Args[1]  #this is blob scan output dir that we look for correct date file in
Write-Host "new tab will go to:$($outFilePathExcel_pl)" 
Write-Host "Will get files from:$($out_pth)"  #this had garbage in it, like terminal output from something I ran yesterday that's unrelated

I was thinking maybe I'm not supposed to use $Args1, and found this example, but it doesn't say what to do in the script with -arg1 and -arg2. I'm using VisualStudioCode to run the PowerShell to test it for now so it's possible I'm executing it from the VSC command line incorrectly as well. When I used the pasted perl line from the old perl script, it worked ok with the first parameter but I've added the second parameter now.

I haven't found in a search how to verify the script arguments are actually set. I tried checking against empty string and that didn't work. Something had filled Arg1 with the garbage so it failed the check for empty string.

Update:

So if I used cmdletbinding, would this be good program structure, and would the program be run the normal way?

  [cmdletbinding()]
  Param ( [Parameter(Mandatory)][string]$outFilePathExcel_pl,  [Parameter(Mandatory)][string]$out_pth="\\wserverp01\support\Retailer_SN\OutputSprdsht\")

#and the rest of the program...
Get-ChildItem $out_pth | Foreach-Object {$lastupdatetime=$_.LastWriteTime;$nowtime = get-date; if (($nowtime - $lastupdatetime).totalhours -le 72) {write-host "here2";$excel_File_from = $_.Name;write-host $_.name}}
..

Update2: Hopefully I'm calling the powershell script correctly, below, per comments.

my $resultPS = `"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -NoProfile -noninteractive -ExecutionPolicy bypass -File "\\\\wserverp01\\support\\Retailer_SN\\MoveSNToSprdsht_1.0.ps1" "-outFilePathExcel_pl $service_report_xls" "-out_pth $cvs_blob_scan_dir"`;

Calling updated powershell script:

[cmdletbinding()]
param( [Parameter(Mandatory)][string]$outFilePathExcel_pl,  [Parameter()][string]$out_pth="\\wserverp01\support\Retailer_SN\OutputSprdsht\")

Start-Transcript -path '\\wserverp01\support\Retailer_SN\Logs\MoveSNToSprdsht.log' 
#get file location from parameter so put sn tab there

Write-Host "new tab will go to:$($outFilePathExcel_pl)" 
Write-Host "Will get scan files from:$($out_pth)"

#make sure params are valid paths; non-terminating error if invalid
if(Test-Path $outFilePathExcel_pl)
{
    Write-Host "path for $outFilePathExcel_pl looks good"
}
else 
{
    Write-Error "path for $outFilePathExcel_pl invalid" 
}
if(Test-Path $out_pth)
{
    Write-Host "path for $out_pth looks good"
}
else 
{
    Write-Error "path for $outFilePathExcel_pl invalid"
}

Get-ChildItem $out_pth | Foreach-Object {$lastupdatetime=$_.LastWriteTime;$nowtime = get-date; if (($nowtime - $lastupdatetime).totalhours -le 72) {write-host "here2";$excel_File_from = $_.Name;write-host $_.name}}

Update3:

For some reason when I use the named parameters from perl and use them from the powershell, I'm getting

Cannot bind argument to parameter 'Path' because it is an empty string.

when I try to use the variable. Is there a problem with my syntax?

from perl:

        my $resultPS = `"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -NoProfile -noninteractive -ExecutionPolicy bypass -File "\\\\wserverp01\\support\\Retailer_SN\\MoveSNToSprdsht_1.0.ps1" -outFilePathExcel_pl "$service_report_xls" -out_pth "$retailer_blob_scan_dir"`;

(where the variables are set from an ini file and passed to the script as the parameters, printout before the powershell call look good in perl)

Then in MoveSNToSprdsht_1.0.ps1:

[cmdletbinding()]
param( [Parameter(Mandatory)][string]$outFilePathExcel_pl,  [Parameter()][string]$out_pth="\\wserverp01\support\Retailer_SN\OutputSprdsht\")

Start-Transcript -path '\\wserverp01\support\Retailer_SN\Logs\MoveSNToSprdsht.log' 
#get file location from parameter so put sn tab there

Write-Host "new tab will go to:$($outFilePathExcel_pl)" #need to use below instead of $outFilePathExcel############################
Write-Host "Will get blob scan files from:$($out_pth)"

#make sure params are valid paths; non-terminating error if invalid
if(Test-Path $outFilePathExcel_pl)
{
    Write-Host "path for $outFilePathExcel_pl looks good"
}
else 
{
    Write-Error "path for $outFilePathExcel_pl invalid" 
}
if(Test-Path $out_pth)
{
    Write-Host "path for $out_pth looks good"
}
else 
{
    Write-Error "path for $out_pth invalid"
}

[System.String] $excel_File_from = ""

############## hours - 72 hours is ok for now so we don't cut it too close for file timestamp for scan..scan run on weekend, use Monday:
Get-ChildItem $out_pth | Foreach-Object {$lastupdatetime=$_.LastWriteTime;$nowtime = get-date; if (($nowtime - $lastupdatetime).totalhours -le 72) {write-host "here2";$excel_File_from = $_.Name;write-host $_.name}}

Maybe I need curly brackets below the cmdlet binding? It the error is complaining about using the empty parameter, not wrong formatting though.

Michele
  • 3,617
  • 12
  • 47
  • 81

1 Answers1

1
  • In your Perl command line, $service_report_xls is a Perl variable that gets string-expanded. To pass it robustly, enclose it in embedded "...", which requires \\"...\\" (sic):
`"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -NoProfile -noninteractive -ExecutionPolicy bypass -Command "\\\\wserverp01\\kiosksupport\\Retailer_DeviceSN\\MoveSNToSprdsht_1.0.ps1 \\"$service_report_xls\\""`;
  • However, even that can subject your arguments to unwanted interpretation, due to use of the -Command PowerShell CLI parameter. For execution of a script file (*.ps1) the better option is to use the -File parameter, which accepts separate arguments that are passed verbatim (for guidance on when to use -Command vs. -File, see this answer):
`"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "\\\\wserverp01\\kiosksupport\\Retailer_DeviceSN\\MoveSNToSprdsht_1.0.ps1" "$service_report_xls"`;
  • To ensure that no unexpected arguments are passed to your PowerShell script (*.ps1), make your script an advanced one, which requires formally declaring parameters and using the [CmdletBinding()] attribute; advanced scripts and functions only accept arguments that bind to formally declared parameters; e.g.:
[CmdletBinding()] # Make the script an advanced one.
param(
  # Mandatory, 1st positional parameter
  [Parameter(Mandatory)]  # Make sure an argument is always passed.
  [string] $outFilePathExcel_pl,   
  # Optional, 2nd positional parameter with default value.
  [string] $out_pth = '...'
)
# ...

Note: Since declared parameters can also be bound by name (preferably so, for conceptual clarity) - e.g. ...\MoveSNToSprdsht_1.0.ps1 -outFilePathExcel_pl c:\some\file.xlsx - it's better to use more descriptive parameter names.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    @Michele, Remove `[Parameter(Mandatory)]` from the 2nd parameter (or use `[Parameter()]`), because you want it to be _optional_ (and it doesn't make sense to assign a default value to a mandatory parameter). – mklement0 Mar 06 '23 at 15:35
  • 1
    @Michele, apart from that, it should work. The presence of `[CmdletBinding()]` then ensures that no extra, unexpected arguments can be passed. – mklement0 Mar 06 '23 at 15:36
  • Thanks!! I have Start-Transcript before cmdletbinding part. Is that ok? That's how I'm getting my logs. – Michele Mar 06 '23 at 15:43
  • 1
    @Michele, no you cannot have anything other than comments or `using` statements before a `param()` block, but you can place it _after_ it. – mklement0 Mar 06 '23 at 15:51
  • In the perl program that invokes the powershell script, am I using the double quotes and -outFilePathExcel_pl ok? See update2. – Michele Mar 06 '23 at 16:38
  • 1
    @Michele, no you need to pass all arguments _individually_ - the parameter _name_ and _its argument_ must syntactically be separate arguments: ``my $resultPS = `"C:\\Program Files\\PowerShell\\7\\pwsh.exe" -NoProfile -noninteractive -ExecutionPolicy bypass -File "\\\\wserverp01\\support\\Retailer_SN\\MoveSNToSprdsht_1.0.ps1" -outFilePathExcel_pl "$service_report_xls" -out_pth "$cvs_blob_scan_dir"`;`` – mklement0 Mar 06 '23 at 17:45
  • The parameter doesn't seem to be working. See update3. It complains about using the parameter in the Get-ChildItem line. In my log it's printing both if and else lines in looks good and invalid check for both variables. Very odd. – Michele Mar 07 '23 at 15:21
  • 1
    @Michele, we're getting pretty far afield here, given that we're now discussing the fundamentals of PowerShell's parameter parsing. If you pass `-out_pth "$retailer_blob_scan_dir"` and the `$retailer_blob_scan_dir` variable happens to be empty, you'll pass an empty spring explicitly, and the default value will _not_ kick in. You either have to omit the 2nd argument if that is the case, or implement the default-value logic in your script _body_: `if ('' -eq $out_pth) { $out_pth='\\wserverp01\support\Retailer_SN\OutputSprdsht\' }` – mklement0 Mar 07 '23 at 18:58
  • I think my problem is the double quotes around the $retailer_blob_scan_dir. I see double quotes when I print it out in the powershell script too. – Michele Mar 09 '23 at 14:44
  • 1
    @Michele, that would imply that `$retailer_blob_scan_dir` has _embedded_ `"`, i.e that they're part of the variable's _value_. – mklement0 Mar 09 '23 at 14:46
  • 1
    Yea I think both perl and powershell are not strongly typed, so they would both be considered a string already in both. Thanks for the help!! – Michele Mar 09 '23 at 16:24