Search code examples
powershellreturnoutputreturn-valuemultiple-return-values

Powershell Join-Path showing 2 dirs in result instead of 1 - accidental script/function output


I am constructing incremental directory structures, and for some reason Join-Path is showing 2 dirs. When I later join that with a file I'm sending to copy-item, it causes an error, as shown below. I have shown in the comment for the $to_loc_finalDT1 line, where I first see these two dirs:

Copy-Item : Cannot find path '\\T2\DisasterBackup\Loc_2019-03-08\Privileges\Privileges_HH_Bak.csv \\T2\DisasterBackup\Loc_2019-03-08\Privileges\Privileges_HH_Bak.csv' because it does not exist

So this is the pertinent powershell script:

$T2 = "\\T2\DisasterBackup\Loc" 
$toLocParentDT2 = CreateDatedFolder $parentDirBaseNameDT2 
$to_loc_finalDT2 = Join-Path -Path $toLocParentDT2 -ChildPath "Privileges" 
#create sub-folder location 
if(-Not (Test-Path $to_loc_finalDT2 )) 
{
   write-output  " Creating folder $to_loc_finalDT2 because it does not exist " 
   New-Item -ItemType directory -Path $to_loc_finalDT2 -force 
}


#second dir save files to
$parentDirBaseNameDT1 = "\\T1\DisasterBackup\Loc" 
$toLocParentDT1 = CreateDatedFolder $parentDirBaseNameDT1 
$to_loc_finalDT1 = Join-Path -Path $toLocParentDT1 -ChildPath "Privileges" #shows 2 dirs here in debugger: \\T2\DisasterBackup\Loc_2019-03-08\Privileges \\T2\DisasterBackup\Loc_2019-03-08\Privileges
#create sub-folder location 
if(-Not (Test-Path $to_loc_finalDT1 )) 
{
   write-output  " Creating folder $to_loc_finalDT1 because it does not exist " 
   New-Item -ItemType directory -Path $to_loc_finalDT1 -force 
}
   

I'm not sure how to get Join-Path to just have the one dir, as it should. Right now, I think it's being treated as an array, which is not correct.

I tried searching for related issues, but didn't see anything similar.

Update

Here's the code for CreateDatedFolder:

#create dated folder to put backup files in 
function CreateDatedFolder([string]$name){
   $datedDir = ""
   $datedDir = "$name" + "_" + "$((Get-Date).ToString('yyyy-MM-dd'))"
   New-Item -ItemType Directory -Path $datedDir -force
   return $datedDir
}

The output for that looks fine when it's returned. It appends the date onto the \T2\DisasterBackup\Loc, but the debugger only shows one dir there, not an array or 2 dirs that are separate strings.


Solution

  • As T-Me correctly inferred before you posted the CreateDatedFolder source, the problem is that the function inadvertently outputs 2 objects, and Join-Path accepts an array of parent paths to each join with the child path.

    Specifically, it is the New-Item call that accidentally creates an additional output object, just before your return $datedDir call.

    New-Item outputs a [System.IO.DirectoryInfo] instance representing the newly created directory and, due to PowerShell's implicit output behavior, that instance becomes part of what the function outputs too - any command or expression inside a script / function that returns a value that isn't captured or redirected becomes part of the output.

    To prevent that, suppress the output:

    $null = New-Item -ItemType Directory -Path $datedDir -force
    

    Other ways to suppress output are discussed in this answer, which also discusses the design rationale for PowerShell's implicit output behavior.

    Note that you never need return in PowerShell in order to output a result - but you may need it for flow control, to exit a function prematurely:

    return $datedDir 
    

    is syntactic sugar for:

    $datedDir # Implicitly output the value of $datedDir.
              # While you could also use `Write-Output $datedDir`,
              # that is rarely needed and actually slows things down.
    return    # return from the function - flow control only
    

    For more information about PowerShell's implicit output behavior, see this answer.