Home > Enterprise >  Powershell: How to repeat a condition until a valid value is passed?
Powershell: How to repeat a condition until a valid value is passed?

Time:10-20

I am working on a script that clones a directory structure (excluding files) to a new directory. Here's my code so-far:

Function Copy-DirectoryStructure{
    [CmdletBinding()]

    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [string]$Path
    )

    # Test whether the path is exactly a drive letter
    if($Path -match '^[a-zA-Z]:\\$' -or $Path -match '^[a-zA-Z]:$'){
        throw "Path cannot be at the root of a drive. Aborting."
    }

    # Check if the path is valid
    if(Test-Path -Path $Path -PathType Container){
        # Create the destination path format
        $DestinationPath = (get-item $Path).FullName   ' Folder Copy'
        # Test if our destination already exists.
        # If so, prompt for a new name
        if(Test-Path -Path $DestinationPath -PathType Container){
            Write-Warning "The destination path already exists."
            $NewFolderName = Read-Host -Prompt "Input the name of the new folder to be created."
            $DestinationPath = (get-item $Path).parent.FullName   '\'   $NewFolderName
        }
        # Begin copy
        robocopy $Path $DestinationPath /e /xf *.* | Out-Null
    } else {
        throw "Invalid directory was passed. Aborting."
    }
}

Copy-DirectoryStructure -Path "C:\Users\[username]\Desktop\Test"

The relevant part is here:

# Create the destination path format
        $DestinationPath = (get-item $Path).FullName   ' Folder Copy'
        # Test if our destination already exists.
        # If so, prompt for a new name
        if(Test-Path -Path $DestinationPath -PathType Container){
            Write-Warning "The destination path already exists."
            $NewFolderName = Read-Host -Prompt "Input the name of the new folder to be created."
            $DestinationPath = (get-item $Path).parent.FullName   '\'   $NewFolderName
        }

Right now it prompts the user to input a new name if $DestinationPath already exists and then proceeds to the robocopy. But what if the user inputs a folder name that ALSO already evaluates to an existing path?

I want to handle this scenario gracefully and re-prompt for a new path until the user enters a destination path that doesn't already exist.

I have no idea how to do this.

I know this is an extreme edge-case, but I want to make my code as safe as possible.

Any help is greatly appreciated.

CodePudding user response:

Remove the interactive bits from the function completely - it should simply fail on destination path existing - and instead give the user a way to explicitly pass an alternative destination path:

Function Copy-DirectoryStructure{
    [CmdletBinding()]

    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [string]$Path,

        [Parameter(Mandatory=$false)]
        [string]$DestinationPath
    )

    $ErrorActionPreference = 'Stop'

    # Test whether the path is exactly a drive letter
    if($Path -match '^[a-zA-Z]:\\$' -or $Path -match '^[a-zA-Z]:$'){
        throw "Path cannot be at the root of a drive. Aborting."
    }

    # Check if the path is valid
    if(-not(Test-Path -Path $Path -PathType Container)){
        throw "Path must describe an existing directory"
    }
    
    # Create the destination path format
    if(-not $PSBoundParameters.ContainsKey('DestinationPath'))
    {
        $DestinationPath = (Get-Item $Path).FullName   ' Folder Copy'
    }

    # Test that the destination isn't an existing file system item
    if(Test-Path -Path $DestinationPath){
        throw "The destination path already exists."
    }

    # If we've reached this point all validation checks passed, begin copy
    robocopy $Path $DestinationPath /e /xf *.* | Out-Null
}

Now that the function predictably fails, we can use error handling to handle the retry-logic outside the function:

while($true){
  $path = Read-Host "Give a target path!"
  try {
    Copy-DirectoryStructure -Path $path
    break
  }
  catch {
    if($_ -match 'The destination path already exists.'){
      $destPath = Read-Host "Wanna try an alternative destination path (input NO to start over)"
      if($destPath -ceq 'NO'){
        continue
      }
      Copy-DirectoryStructure -Path $path
      break
    }
  }
}
  • Related