I'm trying to create a script that will copy over specific folders and files in C:\Users\ from three terminal servers, onto a file server.
The problem is, that I create the folders on the file server based on the name of the folder i'm copying. And the script fails when it trys to create a folder on the file server, based on a file instead of a folder.
(Sorry that the text on the image is in Danish! - Hope it still might help.)
I hope the script makes more sense, thanks! :)
Image: http://imgur.com/kxbzXXK
$ServerList = "\\ServerA", "\\ServerB" #Angiv hvilke servere der skal kopires fra, f.eks "\\serverA", "\\serverB".
$FromDir = "\C$\Users\" #Angiv hvilken sti der skal kopires fra, f.eks "\c$\TEST"
$ToDir = "C:\DavidTest_DataMappe\"
foreach ($Server in $ServerList)
{
$RemotePath = $Server + $FromDir
$RemoteDirs = Get-ChildItem $RemotePath |? {$_.mode -match "d"}
foreach($Username in $RemoteDirs | where-object {$_.Name.Length -le 4})
{
$FileList = "\Desktop",
#"\Documents",
#"\Music",
#"\Pictures",
#"\Videos",
#"\Favorites",
#"\Links"#,
"\AppData\Local\Google\Chrome\User Data\Default\Bookmarks",
"\AppData\Roaming\Mozilla\Firefox\Profiles\*.default\places.sqlite"
foreach($File in $FileList)
{
ECHO "Copying folder"
$ToDirPlusUser = $ToDir + $Username + $File
$CopyFile = $RemotePath + $Username + $File
Copy-Item $CopyFile $ToDirPlusUser -Recurse
}
}
}
ECHO "***********"
ECHO "***********"
ECHO "Script Done"
ECHO "***********"
ECHO "***********"
Also add this line above copy-item
Write-Host "Copy-Item $CopyFile $ToDirPlusUser -Recurse"
It will help you zone in on which file/directory is creating the error.
Related
This question already has answers here:
How can I make PowerShell handle [ or ] in file name well?
(2 answers)
Error "Could not find a part of the path" while setting attributes on an existing file
(2 answers)
Closed 4 months ago.
This post was edited and submitted for review 4 months ago and failed to reopen the post:
Original close reason(s) were not resolved
I have regular Jobs to clean up old User Windows-Profile directories on a central NAS.
Last time I had Directories containing double square Brackets in the path (Import from Macromedia or so). This looks like:
\server.ad.local\HOME1\Username\System\Anwendungsdaten\Macromedia\FlashPlayer\SharedObjects\G5EUZBX2\www.daserste.de\[[IMPORT]]\players.edgesuite.net\flash\plugins\osmf\advanced-streaming-plugin\v3.4\osmf2.0\AkamaiAdvancedStreamingPlugin.swf\HDCore.sol
As this Job should run automatically with Powershell I tired various things:
Tried to replace the brackets an rename the folder - no success
Tried LiteralPath for Remove-Item - no success
Tried to Deleted with Scripting.FileSystemObject - no success
I always get the following error Message:
The Element ... cannot be removed: A part of the Path "HDCore.sol" cannot be found.
Are there any ideas?
Tried to rename the Folder, tried remove-item with -LiteralPath, tried to use FileSystemObject.
All of the actions gave the same result: Error
Just to complete: here are the functions I used last:
Function RemoveChar
{
Param ($Path)
$Pattern = '#|&|%|\[{1,}|\]{1,}|\^|\s|\.{2,}'
if ($Path -imatch $Pattern){
Rename-Item -Path $Path -NewName ($Path -replace $Pattern,'') -ErrorAction SilentlyContinue
return ($Path -replace $Pattern,'')
} else {
return $Path
}
}
Function Truncate
{
Param ($Path)
$total_path_length_threshold = 248 # maximal erlaubte Zeichenzahl (248 für Verzeichnisse, 260 für Dateien)
$characters_to_truncate = 60 # Anzahl der zeichen, um die der name zu kürzen ist. Ein eindeutiger Index wird angehangen
$virtual_drive = "v:" # Für temp. Prozessing muss das Format "v:" haben
$collection = cmd /c dir $Path /s /b |? {$_.length -gt $total_path_length_threshold }
$count_paths = ($collection | measure).count - 1
foreach ($pathlong in $collection) {
$parent_path = Split-path -path $pathlong
subst $virtual_drive $parent_path
$leaf = split-path -leaf $pathlong
$short_virtual_path = join-path $virtual_drive $leaf
$item = Get-Item -LiteralPath $short_virtual_path
if (Test-Path -LiteralPath $item) {
$filename = $item.name
$filename_extension = $item.Extension
$basename = $item.BaseName
$basename_length = $basename.length
$new_index = "X" + $counter + "X"
$adjusted_characters_to_truncate = $characters_to_truncate + $new_index.length
if ( $basename_length -gt $adjusted_characters_to_truncate ) {
$length_to_use = $basename_length - $adjusted_characters_to_truncate
$new_filename = $basename.substring(0, $length_to_use ) + $new_index + $filename_extension
$new_path = $parent_path + $new_filename
$Path = $new_path
Rename-Item -LiteralPath $short_virtual_path -NewName $new_filename
}
}
subst v: /d
}
return $Path
}
Function removeRecursive
{
param([String] $Path)
$fso = new-object -com "Scripting.FileSystemObject"
function proc {
param($folder)
$folder.Files | foreach-object {
RemoveChar $_
}
$folder.Files | foreach-object {
$short = $fso.GetFile($_.FullName).ShortPath
LogWrite "$FullDate : Processing: $short"
$fso.DeleteFile($short,$true)
}
$folder.SubFolders | foreach-object {
proc $_
}
}
proc $fso.GetFolder($Path)
}
The function I call from main code is removeRecursive. And yes I tried Remove-Item -LiteralPath SomePath -Recursive -Force too but no success as well.
(I have tried to rewrite this to be more clear)
I have a problem with having hundreds of folders and files (each start with ABC and then numbers 001-936) that need to be moved to a single folder with the same name. For example:
C:\folders\ABC001
C:\folders\random\ABC001\
C:\folders\files\ABC001.pdf
C:\folders\ABC002.pdf
C:\folders\ABC002\
C:\folders\needs sorting\ABC002\
That I would like to move all files and folders to a folder with the same name:
C:\folders\ABC\ABC001\
C:\folders\ABC\ABC002\
and so on...
However, sometimes the orginal folder and/or file will have a duplicate in the destination folder(or may already have since folders already exist), so I want it to add to the name (1), (2), etc.
C:\folders\ABC\ABC001\ABC001\
C:\folders\ABC\ABC001\ABC001 (1)\
C:\folders\ABC\ABC001\ABC001.pdf
C:\folders\ABC\ABC002\ABC002.pdf
C:\folders\ABC\ABC002\ABC002\
C:\folders\ABC\ABC002\ABC002 (1)\
Any help would be greatly appreciated, I have been trying to solve this for weeks (everytime I needed the files) but am new to scripting/code. I started with:
for /R "C:\folders" %x in (*abc123*.*) do move "%x" "D:\folders\abc\abc123"
Closest attempt (minor edits to another code):
function MoveFileToFolder ([string]$source,[string]$destination){
# get a list of source files
$files = Get-ChildItem -Path $source -Recurse | ?{($_.PSIsContainer)}
# verify if the list of source files is empty
if ($files -ne $null) {
foreach ($file in $files) {
$filename = $file.Name
$filebase = $file.BaseName
$fileext = $file.Extension
# verify if destination file exists
$filenameNU = $filename
if (Test-Path (Join-Path $destination $filename)) {
$n = 0
while ((Test-Path (Join-Path $destination $filenameNU)) -eq $true)
{
$filenameNU = $filebase + "(" + ++$n + ")" + $fileext
}
}
Move-Item $file.FullName (Join-Path $destination $filenameNU)
}
}
}
MoveFileToFolder C:\folders\ C:\folders\abc\abc123
(I would try and run this in Powershell for each subfolder, but it became very hard to keep up with)
Thank you for reading.
I know I could use System.IO.FileSystemWatcher in a Powershell script to detect when a file is created in Azure files (in my storage account). Is there a better way to detect a file being created than this method ?
I can't seem to find any logs that I could perhaps parse ?
Thanks
This could do you if you really didn't want to use the FileSystemWatch
$Files = Get-ChildItem -file -Recurse "C:\Temp"
$IfOlderThan = 60
$NewlyCreated = #()
$OldCreated = #()
$Count = 0
Foreach ($file in $files){
if($file.CreationTime -gt (Get-Date).AddMinutes(-$IfOlderThan)){
$NewlyCreated += $file
} Else {
$OldCreated += $file
}
}
Write-Host "Found " -NoNewline;Write-host $($NewlyCreated.Count) -NoNewline -ForegroundColor Green; Write-Host " file\s that have been created in the last $IfOlderThan minutes"
$NewlyCreated | select Name, Fullname, CreationTime
I have a script that scans for a specific folder in users AppData folder. If it finds the folder, it then returns the path to a txt file. So we can see the computer name and username where it was found.
I would like to be able to format the what is actually written to the text file, so it removes everything from the path except the Computer and User names.
Script:
foreach($computer in $computers){
$BetterNet = "\\$computer\c$\users\*\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm"
Get-ChildItem $BetterNet | ForEach-Object {
$count++
$betternetCount++
write-host BetterNet found on: $computer
Add-Content "\\SERVERNAME\PowershellScans\$date\$time\BetterNet.txt" $_`n
write-host
}
}
The text files contain information like this
\\computer-11-1004S10\c$\users\turtle\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm
\\computer-1004-24S\c$\users\camel\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm
\\computer-1004-23S\c$\users\rabbit\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm
If you have each line in a form of the string $string_containing_path then it is easy to split using split method and then add index(1) and (4) that you need:
$afterSplit = $string_containing_path.Split('\')
$stringThatYouNeed = $afterSplit[1] + " " + $afterSplit[4]
You can also use simple script that will fix your current logs:
$path_in = "C:\temp\list.txt"
$path_out= "C:\temp\output.txt"
$reader = [System.IO.File]::OpenText($path_in)
try {
while($true){
$line = $reader.ReadLine()
if ($line -eq $null) { break }
$line_after_split_method = $line.Split('\')
$stringToOutput = $line_after_split_method[1] + " " + $line_after_split_method[4] + "`r`n"
add-content $path_out $stringToOutput
}
add-content $path_out "End"
}
finally {
$reader.Close()
}
If you split your loop into two foreach loops, one for computer and user directory it would be easier to output the name of the user directory.
$output = foreach($computer in $computers){
$UserDirectories = Get-ChildItem "\\$computer\c$\users\" -Directory
foreach ($Directory in $UserDirectories) {
$BetterNet = Get-ChildItem (Join-Path $Directory.fullname "\AppData\Local\Google\Chrome\User Data\Default\Extensions\gjknjjomckknofjidppipffbpoekiipm")
Add-Content "\\SERVERNAME\PowershellScans\$date\$time\BetterNet.txt" "$computer $($Directory.name)`r`n"
write-host BetterNet found on: $computer
$BetterNet
}
}
$output.count
I try to compare 2 Folders. A and B.
B is a Backup Folder of A. Firstly I want to check if any Files have been deleted in Folder A, if so, I want to take that corresponding File from B and move it. Then I can go ahead and RoboCopy /mir A again which will create the Backup.
My issue is how to compare the two directories (A and B have subfolders etc), and then move the Deleted File. Here is the Code which is supposed to do this task.
#region | Global Variables
$backupFolder = "Sandbox_Backup"
$UsbDisk = "C:\Users\pullrich\Desktop"
#endregion
$date = Get-Date -Format G
# Create Log that Script has been started
MyLog "--- Script has been started at $date ---" 0
$testfolder = Test-Path "$UsbDisk\$backupFolder"
# If a Backup folder already exist then Compare files and see if any changes have been made
if ( $testfolder -eq $true ) { #IF_2
# Copy deleted files to BackUp Folder
MyLog "Check for Deleted Files on Data_01:\" 0
$source = $testfolder + "\Data_01"
$sourcelist = Get-ChildItem $source -Recurse
$destination = "$UsbDisk\$backupFolder\Data_01\_DeletedFiles"
$testDestination = Test-Path $destination
if ( $testDestination -eq $false ){
mkdir $destination
}
foreach ($file in $sourcelist){
$result = test-path -path "C:\Users\pullrich\Desktop\Sandbox\Data_01*" -include $file.Name
if ( -not $result ){
$CopyFile = $file.DirectoryName + "\" + $file.Name
Copy-Item $CopyFile -Destination $destination
Remove-Item $CopyFile
}
}
# Start Synchronizing Data_01:\
MyLog "Start Synchronizing Data_01:\" 0
Robocopy "C:\Users\pullrich\Desktop\HP-Sandbox\Data_01" "$UsbDisk\$backupFolder\Data_01" /mir /r:2 /w:3 /M /XD VM_*
MyLog "Data_01:\ is up to Date" 0
} #IF_2
UPDATE 1
I kept working on the Code and figured out that the issue seems to be here:
$result = test-path -path "C:\Users\pullrich\Desktop\Sandbox\Data_01*" -include $file.Name
It will always return false, because it is not really doing what I intended it to do I guess. It's supposed to go through every File in the B folder and check if that File exists in A.
Answer
I was able to get the Script done with latkins' answer.
For reference, here is the Full script.
#region | Global Variables
$backupFolder = "Sandbox_Backup"
$UsbDisk = "C:\Users\pullrich\Desktop"
#endregion
$date = Get-Date -Format G
# Create Log that Script has been started
MyLog "--- Script has been started at $date ---" 0
$testfolder = Test-Path "$UsbDisk\$backupFolder"
# If a Backup folder already exist then Compare files and see if any changes have been made
if ( $testfolder -eq $true ) { #IF_2
# Copy deleted files to BackUp Folder
MyLog "Check for Deleted Files on Data_01:\" 0
# Set Data_01 as Source
$source = "$UsbDisk\$backupFolder\Data_01"
# Get all Items
$sourcelist = Get-ChildItem $source -Recurse
# Choose as Destination the DeletedFiles Folder
$destination = "C:\Users\pullrich\Desktop\Sandbox\Data_01\_DeletedFiles"
mkdir $destination
# Check if a File or Folder exists in the BackUp folder but not on the Server
foreach ($file in $sourcelist){
# First we have to check if the File we have in the Backup still exists on the Server; however, we do not care about the DeletedFiles Folder
if ( -not ($file.Name -eq "_DeletedFiles" )){
$fullPath = "C:\Users\pullrich\Desktop\Sandbox\Data_01" + $file.FullName.Substring($source.Length)
$result = test-path $fullPath
# If it doesn't exist then we go ahead and Copy the File to the DeletedFiles Folder on the Server,
# which will later be copied to the BackUp and then deleted of the Server
if(-not $result ){
$CopyFile = $source + $file.FullName.Substring($source.Length)
Copy-Item $CopyFile -Destination $destination -force
}
}
}
# Start Synchronizing Data_01:\
MyLog "Start Synchronizing Data_01:\" 0
Robocopy "C:\Users\pullrich\Desktop\Sandbox\Data_01" "$UsbDisk\$backupFolder\Data_01" /mir /r:2 /w:3 /M /XD VM_*
#Delete the DeletedFiles Folder from Server and keep it only on the BackUp
Remove-Item $destination
MyLog "Data_01:\ is up to Date" 0
} #IF_2
else { #Else_2
mkdir "$UsbDisk\$backupFolder"
# Start Copying Data
MyLog "Start Backing up Data_01:\" 0
Robocopy "C:\Users\pullrich\Desktop\Sandbox\Data_01" "$UsbDisk\$backupFolder\Data_01" /mir /r:2 /w:3 /XD VM_*
}
# Delete All Files in _DeleteFiles Folder which are Older than 60 Days
You could construct the full path for each potential file, rather than relying on a potentially costly recursive wildcard search.
Each $file comes from some subdirectory of $source, so the full path (given by $file.FullName) can be shortened to the "relative" path by dropping the first $source.Length chars from $file.FullName. Take your new root path and add it to this relative path, and you've got the new full path.
$fullPath = "C:\Users\pullrich\Desktop\Sandbox\Data_01" + $file.FullName.Substring($source.Length)
$result = Test-Path $fullPath