I'm struggling to understand how PowerShell handles recursion and Copy-Item command.
$date=Get-Date -Format yyyyMMdd
$oldfolder="c:\certs\old\$date"
New-PSDrive -Name "B" -PSProvider FileSystem -Root "\\(server)\adconfig"
$lastwrite = (get-item b:\lcerts\domain\wc\cert.pfx).LastWriteTime
$timespan = new-timespan -days 1 -hours 1
Write-Host "testing variables..."
Write-Host " date = $date" `n "folder path to create = $oldfolder" `n
"timespan = $timespan"
if (((get-date) - $lastwrite) -gt $timespan) {
#older
Write-Host "nothing to update."
}
else {
#newer
Write-Host "newer certs available, moving certs to $oldfolder"
copy-item -path "c:\certs\wc" -recurse -destination $oldfolder
copy-item b:\lcerts\domain\wc\ c:\certs\ -recurse -force
}
Existing files exist at c:\certs\wc\cert.pfx
I have the "test" comparing the time between the cert.pfx in the b:\lcerts\domain\wc\ folder and the current time . If the cert has been modified in the past 1 day and 1 hour, then the script should continue:
Copy cert.pfx from c:\certs\wc\ to c:\certs\old\$date\cert.pfx
Copy cert.pfx from b:\lcerts\domain\wc to c:\certs\wc\cert.pfx
I obviously don't understand PowerShell nomenclature for this because the first time I run this script, it works fine. The second time it creates another folder inside c:\certs\wc\$date\wc\cert.pfx.
How do I get it to fail with "c:\certs\wc\$date\cert.pfx already exists?"
I don't want to restrict this to just the cert.pfx file by specifying the actual file name, I want all files in the folder as eventually there will be more than one file.
The behavior of Copy-Item when a directory is specified in the -Path parameter depends on whether the directory specified in the -Destination parameter exists.
Copy-Item -Path "c:\certs\wc" -Recurse -Destination "c:\certs\old\$date"
If the c:\certs\old\$date directory does not exist, then the wc directory is copied and named c:\certs\old\$date.
If the c:\certs\old\$date directory exists, the wc directory is copied under the c:\certs\old\$date directory. Therefore, it becomes c:\certs\old\$date\wc.
So you are sure to check in advance if the directory exists.
if(Test-Path $oldfolder) { throw "'$oldfolder' is already exists." }
Copy-Item -Path "c:\certs\wc" -Destination $oldfolder -Recurse
You are not testing if the destination folder exists. Seeing you are creating its name using a current date, it is more than likely this folder does not yet exist, so you need to create it first.
Also, There should be no need to use the New-PSDrive cmdlet, because Copy-Item is perfectly able to use UNC paths.
Something like this perhaps:
$server = '<NAME OF THE SERVER>'
$serverPath = "\\$server\adconfig\lcerts\domain\wc"
$testFile = Join-Path -Path $serverPath -ChildPath 'cert.pfx'
$localPath = 'c:\certs\wc'
$date = Get-Date -Format yyyyMMdd
$timespan = New-TimeSpan -Hours 1 -Minutes 1
$oldfolder = "c:\certs\old\$date"
# check if this output path exists. If not, create it
if (!(Test-Path -Path $oldfolder -PathType Container)) {
Write-Host "Creating folder '$oldfolder'"
New-Item -ItemType Directory -Path $oldfolder | Out-Null
}
Write-Host "testing variables..."
Write-Host "date = $date`r`nfolder path to create = $oldfolder`r`ntimespan = $timespan"
# test the LastWriteTime property from the cert.pfx file on the server
$lastwrite = (Get-Item $testFile).LastWriteTime
if (((Get-Date) - $lastwrite) -gt $timespan) {
#older
Write-Host "Nothing to update."
}
else {
#newer
Write-Host "Newer cert(s) available; copying all from '$localPath' to '$oldfolder'"
Copy-Item -Path $localPath -Filter '*.pfx' -Destination $oldfolder
Copy-Item -Path $serverPath -Filter '*.pfx' -Destination $localPath -Force
}
Related
I built a script that copies files from the local C drive to a Network Drive.
I have a folder in C:\www\root called "images" or "C:\www\root\images"
The script creates a folder called "images" in the "F:\powershell_wwwroot\root_" + $TimeStamp" however the "images" directory is empty in the destination folder. The script copies everything from the C:\www\root\images\ folder to base destination folder "F:\powershell_wwwroot\root_" + $TimeStamp".
So I see everything from C:\www\root\images\ in F:\powershell_wwwroot\root_02_12_2021
however the F:\powershell_wwwroot\root_02_12_2021\images is empty. I would like to see everytying from "C:\www\root\images" in "F:\powershell_wwwroot \root_02_12_2021\images"
$TimeStamp = get-date -f dd_MM_yyyy
$Destination = "F:\powershell_wwwroot\root_" + $TimeStamp
New-Item -ItemType directory -Path $Destination -Force
gci "C:\www\root\" -recurse | %{
$file = $_.Fullname
Write-Host $file
Copy-Item -Path $file -Destination $Destination -Force
}
A ForEach-Loop for this shouldn't be needed, let Copy-Item handle it. The following should leave the Folder / File hierarchy intact.
$TimeStamp = Get-Date -Format dd_MM_yyyy
$Destination = "F:\powershell_wwwroot\root_" + $TimeStamp
New-Item -ItemType directory -Path $Destination -Force
$Source = "C:\www\root\"
Copy-Item -LiteralPath $source -Destination $destination -Recurse -Force
you can use either copy-item or bit-transfer. I wrote a script that uses the bits transfer cmdlet. If you update my script, to create the destination folder your home dry. If you have any issues regarding the script holler at me :o)
https://superuser.com/questions/1689980/start-bitstransfer-failing-to-send-files-to-remote-destination/1690801#1690801
Import-Module BitsTransfer
#Get Script Location Path
$ScriptPath = Get-Location
$ScriptPath = $ScriptPath.ToString()
$dirName=[System.IO.Path]::GetDirectoryName($ScriptPath)
#Build Source Server Location Path
#Source Server
$Sourceserver = $env:COMPUTERNAME
#Source Server Folder Path
$Sourcefolderpath = "C:\Temp"
#Switch Colon for Dollar Sign
$Sourcepath = $Sourcefolderpath.Replace(':','$')
#Build Target Folder Path
$ServerSourceFolderpathTest = "\\" + $SourceServer + "\" + $Sourcepath
#Print to screen to view path
#Write-Host $ServerSourceFolderpathTest
#Test Path
$testsourcefolderpath = Test-Path -LiteralPath $ServerSourceFolderpathTest
IF ($testsourcefolderpath -eq $False) {
Write-Host -Fore Red "Source Folder Path Cannot Be Established.
Path is - $testsourcefolderpath"
Write-Host "Script Will Exit"
Exit
} ELSE {
Write-Host -Fore Green "Source Folder Path Established.
Path Is - $testsourcefolderpath"
}
#Build Target Server Location Path
#Target Server
$Targetserver = $env:COMPUTERNAME
#Target Server Folder Path
$Targetfolderpath = "C:\Test"
#Switch Colon for Dollar Sign
$Targetpath = $Targetfolderpath.Replace(':','$')
#Build Target Folder Path
$ServerTargetFolderpathTest = "\\" + $TargetServer + "\" + $Targetpath
#Print to screen to view path
#Write-Host $ServerTargetFolderpathTest
#Test Path
$testTargetfolderpath = Test-Path -LiteralPath $ServerTargetFolderpathTest
IF ($testTargetfolderpath -eq $False) {
Write-Host -Fore Red "Target Folder Path Cannot Be Established.
Path is - $testTargetfolderpath"
Write-Host "Script Will Exit"
Exit
} ELSE {
Write-Host -Fore Green "Target Folder Path Established.
Path Is - $testTargetfolderpath"
}
#Build Source Target Paths for Bit Transfer
$SourceFolder = $ServerSourceFolderpathTest
$PreTargetFolder = $ServerTargetFolderpathTest
$Slash = "\"
$TargetFolder = $PreTargetFolder + $Slash
#Test Target Folder Path
#$TargetFolder
#Get All Text Files From Source Folder
$GetFiles = (gci $SourceFolder -Filter *.txt)
$GetFilesFileFullName = $GetFiles.FullName
#Test View All Full File Names
#$GetFilesFileFullName
ForEach($File in $GetFilesFileFullName) {
$Discript = "Backing Up $File from $Sourceserver to $Targetserver -- " + (Get-Date)
$Discript
Start-BitsTransfer -Source $File -Destination $TargetFolder -Display $Discript -Priority 'Low' -Asynchronous -WhatIf
}
Hello Stack Overflow Community,
at the moment I'm struggling with this code (it's not that beautiful):
$filepath = "C:\inetpub\logs\LogFiles"
$filearchivepath = "C:\inetpub\logs"
$daystoarchive = 1
$_ = "";
function create-7zip([String] $aDirectory, [String] $aZipfile){
#change the path where you downloaded the 7z exe
[string]$pathToZipExe = "C:\Users\kschweiger\Downloads\7za.exe";
[Array]$arguments = "a", "-tzip", "$aZipfile", "$aDirectory";
& $pathToZipExe $arguments;
}
#Create a new folder with the specific date
$ArchiveFolder = (Get-Date -Format dd.MM.yyyy) + " - Logs-Archive"
if(Test-Path "$filearchivepath\$ArchiveFolder"){
Write-Host "Folder already exists!"
}else{
New-Item -Path $filearchivepath -Name $ArchiveFolder -ItemType directory
}
#Save alle files older than X days into $Files
$Files = Get-ChildItem -Path $filepath -Recurse | where {$_.LastWriteTime -lt (Get-Date).AddDays(-$daystoarchive)}
#Copy/Move files and keep folder structure
foreach ($File in $Files){
$NewPath = $File.DirectoryName.Replace($filepath,"")
if (!(Test-Path "$filearchivepath\$ArchiveFolder\$NewPath"))
{
New-Item -Path "$filearchivepath\$ArchiveFolder\$NewPath" -ItemType Directory
}
$File | Copy-Item -Destination "$filearchivepath\$ArchiveFolder\$NewPath"
}
#Compress folder
if(Test-Path "$filearchivepath\$ArchiveFolder.zip"){
Write-Host "Archive-File already exists!"
}else{
#[IO.Compression.ZipFile]::CreateFromDirectory("$filearchivepath\$ArchiveFolder","$filearchivepath\$ArchiveFolder.zip")
create-7zip "$filearchivepath\$ArchiveFolder" "$filearchivepath\$ArchiveFolder.zip"
#Delete Folder
Remove-Item -Path "$filearchivepath\$ArchiveFolder" -Recurse -Force
}
The code works. but I also get a error message called:
You cannot call a null-valued expression
How can I resolve this?
Get-ChildItem by default returns files and folders. If you need only files, you should use -File. Otherwise, your $Files will contain folders too (as they have LastWriteTime property).
If you try to run .DirectoryName.Replace($filepath,"") on a folder, it'll return such error as you cannot run replacing on $null.
Update: for PowerShell 2.0 you can use | where { ! $_.PSIsContainer } (source)
How can I troubleshoot it by myself?
In your error you can see which line is broken:
$NewPath = $File.DirectoryName.Replace($filepath,"")
All you have to do to troubleshoot such situations is to list all the involved variables and check their values. You could do it like this:
$File
$File.DirectoryName
Pause
$NewPath = $File.DirectoryName.Replace($filepath,"")
Using Pause can be useful as it'll wait for you to press Enter before continuing.
I have created a script to copy the folder/sub folders/files from a specific location to a list of servers that I specified on a notepad.
It checks that if the folder has not been created, it will create it and copy over the files, but if the folder has already been created - then it stops.
However, I would like to still copy over newer files even if that folder has already been created, albeit with no subfolders or files in there.
My current code
[String] $KfxComputers = "C:\temp\Kofax Apps\servers.txt"
# This file contains the list of servers you want to copy files/folders to
$computers = get-content -Path $KfxComputers
# the folder you want to copy to the servers in the $computer variable
$sourceRoot = #("\\wdevkofx110\Kofax Software\Oracle Clients",
"\\wdevkofx110\Kofax Software\Kofax Capture 11")
# the destination location you want the file/folder(s) to be copied to
$destinationRoot = "C$\temp"
foreach ($computer in $computers) {
$testpath = Test-Path -Path \\$computer\$destinationRoot
if (!$testpath)
{
Write-Host "creating folder and copying files..." -ForegroundColor green
New-Item -ItemType Directory -Force -Path "\\$computer\$destinationRoot"
copy-item -Path $sourceRoot -Recurse -Destination
"\\$computer\$destinationRoot" -Container
} else {
Write-Host "$computer\$destinationRoot folder already exists"
}
}`
You can use Else IF
[String] $KfxComputers = "C:\temp\Kofax Apps\servers.txt"
# This file contains the list of servers you want to copy files/folders to
$computers = get-content -Path $KfxComputers
# the folder you want to copy to the servers in the $computer variable
$sourceRoot = #("\\wdevkofx110\Kofax Software\Oracle Clients",
"\\wdevkofx110\Kofax Software\Kofax Capture 11")
# the destination location you want the file/folder(s) to be copied to
$destinationRoot = "C$\temp"
foreach ($computer in $computers) {
$testpath = Test-Path -Path \\$computer\$destinationRoot
if (!$testpath)
{
Write-Host "creating folder and copying files..." -ForegroundColor green
New-Item -ItemType Directory -Force -Path "\\$computer\$destinationRoot"
copy-item -Path $sourceRoot -Destination
"\\$computer\$destinationRoot" -Container -Recurse -force
}
ElseIF ($testpath) {
Write-Host "folder already exists, copying files..." -ForegroundColor green
copy-item -Path $sourceRoot -Destination
"\\$computer\$destinationRoot" -Container -Recurse -force
}
else {
Write-Host "$computer\$destinationRoot folder already exists"
}
}`
I'm trying to copy files from a source folder to a destination folder, and rename the files in the process.
$Source = "C:\Source"
$File01 = Get-ChildItem $Source | Where-Object {$_.name -like "File*"}
$Destination = "\\Server01\Destination"
Copy-Item "$Source\$File01" "$Destination\File01.test" -Force -
Confirm:$False -ErrorAction silentlyContinue
if(-not $?) {write-warning "Copy Failed"}
else {write-host "Successfully moved $Source\$File01 to
$Destination\File01.test"}
The problem is that since Get-ChildItem doesn't throw an error message if the file is not found, but rather just gives you a blank, I end up with a folder called File01.test in destination if no file named File* exists in $Source.
If it does exist, the copy operation carries out just fine. But I don't want a folder to be created if no matching files exist in $Source, rather I just want an error message logged in a log file, and no file operation to occur.
This shouldn't matter what the file name is, but it won't account for files that already exist in the destination. So if there is already File01.txt and you're trying to copy File01.txt again you'll have problems.
param
(
$Source = "C:\Source",
$Destination = "\\Server01\Destination",
$Filter = "File*"
)
$Files = `
Get-ChildItem -Path $Source `
| Where-Object -Property Name -Like -Value $Filter
for ($i=0;$i -lt $Files.Count;$i++ )
{
$NewName = '{0}{1:D2}{3}' -f $Files[$i].BaseName,$i,$Files[$i].Extension
$NewPath = Join-Path -Path $Destination -ChildPath $NewName
try
{
Write-Host "Moving file from '$($Files[$i].FullName)' to '$NewPath'"
Copy-Item -Path $Files[$i] -Destination
}
catch
{
throw "Error moving file from '$($Files[$i].FullName)' to '$NewPath'"
}
}
You can add an "if" statement to ensure that the code to copy the files only runs when the file exists.
$Source = "C:\Source"
$Destination = "\\Server01\Destination"
$File01 = Get-ChildItem $Source | Where-Object {$_.name -like "File*"}
if ($File01) {
Copy-Item "$Source\$File01" "$Destination\File01.test" -Force -Confirm:$False -ErrorAction silentlyContinue
if(-not $?) {write-warning "Copy Failed"}
else {write-host "Successfully moved $Source\$File01 to
$Destination\File01.test"}
} else {
Write-Output "File did not exist in $source" | Out-File log.log
}
In the "if" block, it will check to see if $File01 has anything in it, and if so, then it'll run the subsequent code. In the "else" block, if the previous code did not run, it'll send the output to the log file "log.log".
The basic idea:
Mirror the directory structure of the target path
Search the target path for files and folders of a certain age and create a text file with all the paths.
Read the previous text file and copy the contents of the different paths to a new drive location.
Script 1 - Mirror the directory structure of the target path from drive to the next. This I feel like I have working pretty decently.
$target = Read-Host "What is the target path?"
$destination = "Z:\This\Is\The\Destination"
Copy-Item $target -Filter {PSIsContainter} -Recurse -Destination $destination -Force
Script 2 - Is used to look through the $target path and looks for files and folders that are -ge -le -eq a lastwritetime, then outputs the locations in a text file.
$path = read-host "What path would you like to search?"
$daysOLD = read-host "How many days old should the files I'm looking for be?"
$outfile = "C:\locationOFtheTEXTfile\ReadThis.txt"
$today = Get-Date
$targetdate = $today.AddDays(-$daysOLD).ToString('MM-dd-yyyy')
$files = Get-ChildItem $path -Recurse| Where-Object {$_.lastwritetime.ToString('MM-dd-yyyy') -le $targetdate} | ForEach-Object {$_.fullname}| out-file $outfile
Script 3 - Is where I am having an issue. I know that I can use Get-Content and point to the location of the text file. What I have a hard time with is having the script read the file, test the path, and not create duplicates in the $destination.
Since I mirrored the directory structure with the first path, I just need the third script to move files to their appropriate folders on the new drive.
$dest = "Z:\This\Is\The\Destination"
$safe = Get-Content "C:\locationOFtheTEXTfile\ReadThis.txt"
$safe | ForEach-Object{
#find drive-delimeter
$first=$_.IndexOf(":\");
if($first -eq 1){
#stripe it
$newdes=Join-Path -Path $dest -ChildPath #($_.Substring(0,1)+$_.Substring(2))[0]
} else {
$newdes=Join-Path -Path $des -ChildPath $_
}
$folder = Split-Path -Path $newdes -Parent
$err=0
#check if folder exists"
$void=Get-Item $folder -ErrorVariable err -ErrorAction SilentlyContinue
if($err.Count -ne 0){
#create when it doesn't
$void=New-Item -Path $folder -ItemType Directory -Force -Verbose
}
$void=Copy-Item -Path $_ -destination $newdes -Recurse -Container -Force -Verbose
}
write-host "Doomsday =("
This script was taken from the web and I haven't had a chance to figure it's parts and pieces for myself. I know that sound pathetic but I don't feel like trying to reinvent the wheel is necessary.