2

The below code works perfect but If i try to get user input for $source:

$Source= Read-Host -Prompt "Enter the source directory (example c:\dir1)"

The Replace does not work. It keeps it as $source instead of making it Dir2 Can anyone help me figure this out? I have been messing with it for a couple hours now

$Source = "C:\Dir1" 
$Destination = "C:\Dir2" 
$LogfilePath = $Destination + "\Log.txt"
    
Get-ChildItem -Path $Source -File -Recurse | ForEach-Object {
    $NewDir = $_.DirectoryName.Replace($Source, $Destination) 
    #see if destination directory exists
    if (-not(Test-Path -Path $NewDir)) {
        New-Item -Path $NewDir -ItemType Directory | Out-Null   
    }
    Copy-Item -Path $_.FullName -Destination $NewDir
    Write-Host '.' -NoNewline
    #added to validate time stamp in log file is working
    Start-Sleep -Seconds 1
    "$([DateTime]::Now) copied - '$($_.name)' from '$($_.DirectoryName)' to '$NewDir' Size: $([System.Math]::Round(($($_.Length)/1KB),2))KB"  | Add-Content $LogfilePath
}
0

1 Answer 1

1

There are two conceivable scenarios (possibly in combination) in which $_.DirectoryName.Replace($Source, $Destination) wouldn't work as intended, and they're not related to Read-Host per se:

  • $Source has a trailing \ (e.g., C:\Dir1\ instead of C:\Dir1)

    • In this case, the .Replace() call malfunctions at least for files immediately located in the target path, because .DirectoryName values do not have a trailing \ (except for files in a root path such as C:\).
  • Perhaps more likely: $Source differs in case from the actual target path; e.g., c:\dir1 vs. C:\Dir1

    • In this case, the .Replace() call fails, because this method is case-sensitive, invariably in Windows PowerShell, by default in PowerShell (Core) 7+.

You could fix those problems by using a substring approach instead of trying to replace the start of the path, which bypasses the case-sensitivity problem:

# Remove the input path from the start and prepend the destination path.
Join-Path $Destination $_.DirectoryName.Substring($Source.Length)

Note that, due to how Join-Path works, the path will be synthesized correctly whether or not $Source has a trailing \, so there is no need to trim one manually first. E.g.,
Join-Path c:\dir2 sub, Join-Path c:\dir2 \sub, and Join-Path c:\dir2\ \sub
all yield the same result: c:\dir2\sub.


Alternative approach: Get-ChildItem has a -Name switch that reports matching files as relative path strings, namely relative to the input directory. This allows for a solution that uses PowerShell cmdlets only, without needing to resort to .NET method calls:

# Note the use of -Name
Get-ChildItem -Name -Path $Source -File -Recurse | ForEach-Object {
    # Prepend the destination path to the parent path of the relative source path.
    $NewDir = Join-Path $Destination (Split-Path -Parent $_)
    # Ensure that the destination dir. exists - note the use of -Force
    $null = New-Item -Force -Path $NewDir -ItemType Directory
    Copy-Item -LiteralPath $_.FullName -Destination $NewDir
    Write-Host '.' -NoNewline
    #added to validate time stamp in log file is working
    Start-Sleep -Seconds 1
    "$([DateTime]::Now) copied - '$($_.name)' from '$($_.DirectoryName)' to '$NewDir' Size: $([System.Math]::Round(($($_.Length)/1KB),2))KB"  | Add-Content $LogfilePath
}

Note that, as in your own attempt, both solutions above rely on $Source and $Destination to contain full paths.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.