Reputation: 21
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
}
Upvotes: 2
Views: 178
Reputation: 437608
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
)
.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
.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.
Upvotes: 1