Trying to move a folder(s) if they exist - powershell

I'm not getting the if/else state to work. I'm trying to move a folder or folders that starts with numbers 0-9. If the folders are there the folders will be moved. But im trying to make an else statement if they dont exists. Here is my script:
$Source = "\\Server\share"
$Destination = "\\Server\Archive"
$Dir = Get-ChildItem $Source | Where-Object {$_.Name -match "\d"}
if (!(Test-Path $Source | Where-Object {$_.Name -match "\d"}))
{
Move-Item $Dir -Destination $Destination
}
else
{
Write-Host "No folder to backup"
}
Anyone have any good suggestion how I should solve this or use a different method?

Going out on a limb my guess would be that you want to check if $Source contains one or more subfolder(s) whose name starts with a digit, and move those that exist to $Destination. If that assumption is correct I'd suggest to do the following:
$Source = '\\Server\share'
$Destination = '\\Server\Archive'
$Dir = Get-ChildItem $Source -Directory | Where-Object {$_.Name -match '^\d'}
if ($Dir) {
$Dir | Move-Item -Destination $Destination
} else {
Write-Host 'No folder to backup'
}

Related

Excluding Folders with Get-ChildItem - Need Help Debugging a Script

I've searched through both StackOverflow and SuperUser to try to figure this out, and I'm still getting plagued by a problem I can't figure out how to fix. I know it's something simple, but after playing with it for an hour I'm still stumped. Simple question: how the heck do I tell Get-Childitem to exclude folders?
Right up front here's the code that doesn't work:
$sourceDir="E:\Deep Storage"
$targetDir="W:\Deep Storage"
$excludeThese = 'Projects2','Projects3','Projects4';
Get-ChildItem -Path $sourceDir -Directory -Recurse |
where {$_.fullname -notin $excludeThese} |
Get-ChildItem -Path $sourceDir | ForEach-Object {
$num=1
$nextName = Join-Path -Path $targetDir -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $targetDir ($_.BaseName + "_$num" + $_.Extension)
$num+=1
}
$_ | Move-Item -Destination $nextName -Force -Verbose -WhatIf
}
}
The underlying concept here already works:
$sourceDir="E:\Deep Storage"
$targetDir="W:\Deep Storage"
Get-ChildItem -Path $sourceDir -File -Recurse | ForEach-Object {
$num=1
$nextName = Join-Path -Path $targetDir -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $targetDir ($_.BaseName + "_$num" + $_.Extension)
$num+=1
}
$_ | Copy-Item -Destination $nextName -Verbose
}
Basically what this does is it moves folders from one place to another, and if files exist in both places, it renames files coming in. It helps keep my archive drive clear. But there are three folders there that I want to exclude because I still pull assets from them regularly, so I don't need those files moved.
Hence the difference between the two code samples: in the first one, I'm trying to get Get-Childitem to exclude a specific trio of folders, while this second one just grabs everything all at once.
I tried just doing a straight -Exclude with $excludeThese as the variable, without success; I tried skipping the variable approach altogether and just putting the folder names in after -Exclude. Still didn't work. I also tried putting in the entire path to the folders I wanted to exclude. No good--no matter what I did, the -WhatIf showed that the script was trying to move everything, including the folders I was theoretically excluding.
The last trick I tried was one I came across here on SO, and that was to go a gci with the exclude argument first, then do another gci after it. That still failed, so now I have to turn to the experts for help.
I would use a regex string created from the (escaped) directory names to exclude to make sure files withing these folders are ignored.
Also, by using a lookup Hashtable of all file names already present in the target folder, figuring out if a file with a certain name already exists is extremely fast.
$sourceDir = 'E:\Deep Storage'
$targetDir = 'W:\Deep Storage'
$excludeThese = 'Projects2','Projects3','Projects4';
# create a regex string with all folder names to exclude combined with regex OR (|)
$excludeDirs = ($excludeThese | ForEach-Object { [Regex]::Escape($_) }) -join '|'
# create a lookup Hashtable and store the filenames already present in the destination folder
$existingFiles = #{}
Get-ChildItem -Path $targetDir -File | ForEach-Object { $existingFiles[$_.Name] = $true }
Get-ChildItem -Path $sourceDir -File -Recurse |
Where-Object {$_.DirectoryName -notmatch $excludeDirs} |
ForEach-Object {
# construct the new filename by appending an index number if need be
$newName = $_.Name
$count = 1
while ($existingFiles.ContainsKey($newName)) {
$newName = "{0}_{1}{2}" -f $_.BaseName, $count++, $_.Extension
}
# add this new name to the Hashtable so it exists in the next run
$existingFiles[$newName] = $true
# use Join-Path to create a FullName for the file
$newFile = Join-Path -Path $targetDir -ChildPath $newName
$_ | Move-Item -Destination $newFile -Force -Verbose -WhatIf
}
Assuming the excluded directories are at the top:
$sourceDir="E:\Deep Storage"
$excludeThese = 'Projects2','Projects3','Projects4'
get-childitem $sourcedir -exclude $excludethese | get-childitem -recurse

Moving Files based on filename

Im looking to move files based on the last half of the filename. Files look like this
43145123_Stuff.zip
14353135_Stuff.zip
2t53542y_Stuff.zip
422yg3hh_things.zip
I am only looking to move files that end in Stuff.zip
I have this in PowerShell so far but it only will move files according to the first half of a file name.
#set Source and Destination folder location
$srcpath = "C:\Powershelltest\Source"
$dstpath = "C:\Powershelltest\Destination"
#Set the files name which need to move to destination folder
$filterLists = #("stuff.txt","things")
#Get all the child file list with source folder
$fileList = Get-ChildItem -Path $srcpath -Force -Recurse
#loop the source folder files to find the match
foreach ($file in $fileList)
{
#checking the match with filterlist
foreach($filelist in $filterLists)
{
#$key = $file.BaseName.Substring(0,8)
#Spliting value before "-" for matching with filterlists value
$splitFileName = $file.BaseName.Substring(0, $file.BaseName.IndexOf('-'))
if ($splitFileName -in $filelist)
{
$fileName = $file.Name
Move-Item -Path $($file.FullName) -Destination $dstpath
}
}
}
There seems to be some differences between the state goal and what the code actually does. This will move the files to the destination directory. When you are confident that the files will be moved correctly, remove the -WhatIf from the Move-Item command.
$srcpath = "C:\Powershelltest\Source"
$dstpath = "C:\Powershelltest\Destination"
Get-ChildItem -File -Recurse -Path $srcpath |
ForEach-Object {
if ($_.Name -match '.*Stuff.zip$') {
Move-Item -Path $_.FullName -Destination $dstpath -WhatIf
}
}
Actually this can be written in PowerShell very efficiently (I hope I got the details right, let me know):
Get-ChildItem $srcpath -File -Force -Recurse |
where { ($_.Name -split "_" | select -last 1) -in $filterLists } |
Move-Item $dstpath
Alternatively, if you only want to look for this one particular filter, you can specify that directly, using wildcards:
Get-ChildItem $srcpath -Filter "*_Stuff.zip"

Trying to use powershell to put files in folders based on their extension and the name of the folder

I have a directory with three files: .xlsx, .docx, and .txt, I also have folders in that same directory called xlsx, docx and txt. Basically trying to put each file into its corresponding folder, as a way to practice my PowerShell skills. I'm very new to PowerShell and have tried the following. I can tell its wrong, but I'm not quite sure why.
$folders = Get-ChildItem -Directory
$files = Get-ChildItem -File
foreach ($file in $files) {
foreach ($folder in $folders) {
if ("*$file.extension*" -like "*$folder.Name*") {
move-item $file -Destination "C:\Users\userA\Desktop\$folder.name"
}
}
}
Try the code below. With the Where-Object function, you find the corresponding file. I remove the dot because it is included in the extension otherwise.
$folders = Get-ChildItem -Directory
$files = Get-ChildItem -File
foreach ($file in $files) {
$folder = $folders | Where-Object { $_.Name -Like $file.Extension.Replace(".","") }
Move-Item -Path $file -Destination $folder
}
In your example, be careful how your strings are actually been interpreted. If you have "*$item.Name*" the string actually "* variable.Name*". In this case you need to use "*$($var.Name)*" in order to get the correct string.
Here are some adjustments to your approach that make it work. Breaking the -Destination parameter out to a separate variable $newpath lets you set a debug statement there so you can easily examine what it's creating.
$folders = Get-ChildItem -Directory
$files = Get-ChildItem -File
foreach ($file in $files) {
foreach ($folder in $folders) {
if ($file.extension.trim(".") -like $folder.Name) {
$newpath = ("{0}\{1}" -f $folder.FullName, $file.Name)
move-item $file -Destination $newpath
}
}
}
You could even create target folders for extensions if they do not exist yet:
$SourceFolder = C:\sample
$TargetFolder = D:\sample
Get-ChildItem -Path $SourceFolder -File |
ForEach-Object{
$DesinationFolder = Join-Path -Path $TargetFolder -ChildPath $_.Extension.TrimStart('.')
if(-not (Test-Path -Path $DesinationFolder)){
New-Item -Path $DesinationFolder -ItemType Directory | Out-Null
}
Copy-Item -Path $_.FullName -Destination $DesinationFolder -Force
}

Avoiding File Duplication before Copying files to the folder and Subfolder

I am trying to create a code on Powershell that will actually Copy files from one Location( Lets say A) to location B. Now Location B have two subfolders (lets say X and Y). I need to copy the file from A to B but before copying I need to make sure that the files which I am copying should not be there in X or Y in order to avoid file duplication. If the file exist, it should not copy that particular file.
$PathS = Get-ChildItem -Path "\\sc-y-ap-swt-1\AutoClientFiles\reception\*.txt" |
Where-Object { $_.CreationTime -gt (Get-Date).AddDays(-1) }
$PathD = "C:\OCM\data\EverestSwift\inbound\"
$pathtest = Get-ChildItem -path "C:\OCM\data\EverestSwift\inbound\" -Recurse -File
If((Test-Path -Path "\\sc-y-ap-swt-1\AutoClientFiles\reception\*.txt") -eq $false) {
Exit
} Else {
Try {
Foreach ($File in $Pathtest){
if ($File -eq $PathS ){
Write-Host "Duplicate Files"
exit 1
}
Copy-Item -Path $PathS -Destination $PathD -Force
Exit 0
}
} catch [Exception]{
Write-Host $_.Exception.Message
Exit 1
}
}
You can do this, but why. As Cory said, this is why robocopy exists.
What do you mean by same?
The filename can be the same, but the timestamps can be different, thus making it a different file, even if the name is the same. So, you should be looking at name and timestamp or file hashes.
So, see these Q&A about such a use case.
Does Robocopy SKIP copying existing files by default?
How to skip existing and/or same size files when using robocopy
RoboCopy "%%F" %destination% *.srt *.pdf *.mp4 *.jpg /COPYALL /XO /R:0
Yet, doing this with powerShell, your post could be a duplicate of this one.
Copy items from Source to Destination if they don't already exist
Examples from the above:
$Source = 'C:\SourceFolder'
$Destination = 'C:\DestinationFolder'
Get-ChildItem $Source -Recurse | ForEach {
$ModifiedDestination = $($_.FullName).Replace("$Source","$Destination")
If ((Test-Path $ModifiedDestination) -eq $False) {
Copy-Item $_.FullName $ModifiedDestination
}
}
# Or
$Source = '<your path here>'
$Dest = '<your path here>'
$Exclude = Get-ChildItem -recurse $Dest
Get-ChildItem $Source -Recurse -Filter '*' |
Copy-Item -Destination $Dest -Verbose -Exclude $Exclude

Move-Item -Exclude -Recursive Is Not leaving excluded items in place within sub directories

I'm trying to move all items except a certain type of file. In this case *.msg. It does fine if the excluded file resides within the parent folder. However, the moment that same type of file is located within a subdirectory, it fails to leave the file in place and instead moves it to the new location.
username = Get-Content '.\users.txt'
foreach ($un in $username)
{
$destA = "c:\users\$un\redirectedfolders\mydocuments"
$destB = "c:\users\$un\redirectedfolders\desktop"
$sourceA = "C:\users\$un\mydocuments"
$sourceB = "C:\users\$un\desktop"
New-Item -ItemType Directory -Path $destA, $destB
Get-ChildItem $sourceA -Exclude '*.msg' -Recurse | Move-Item -Destination {Join-Path $destA $_.FullName.Substring($sourceA.length)}
Get-ChildItem $sourceB -Exclude '*.msg' -Recurse | Move-Item -Destination {Join-Path $destB $_.FullName.Substring($sourceB.length)}
}
This is due to the filtering done by the Get-ChildItem exclude filter. It's kind of a known issue, and if you really want I could probably dig up some reference documentation, but it may take some time. Regardless, GCI doesn't handle wildcards very well when it comes to filtering things. What you are probably better off doing is piping it to a Where command like this:
$username = Get-Content '.\users.txt'
foreach ($un in $username)
{
$destA = "c:\users\$un\redirectedfolders\documents"
$destB = "c:\users\$un\redirectedfolders\desktop"
$sourceA = "C:\users\$un\documents"
$sourceB = "C:\users\$un\desktop"
New-Item -ItemType Directory -Path $destA, $destB
GCI $sourceA -recurse | ?{$_.Extension -ne ".msg" -and !$_.PSIsContainer} | %{
$CurDest = Join-Path $destA $_.FullName.Substring($sourceA.length)
If(!(Test-Path $CurDest.SubString(0,$CurDest.LastIndexOf("\")))){New-Item -Path $CurDest -ItemType Directory|Out-Null}
$_ | Move-Item -Destination $CurDest
}
GCI $sourceB -recurse | ?{$_.Extension -ne ".msg" -and !$_.PSIsContainer} | %{
$CurDest = Join-Path $destB $_.FullName.Substring($sourceB.length)
If(!(Test-Path $CurDest.SubString(0,$CurDest.LastIndexOf("\")))){New-Item -Path $CurDest -ItemType Directory|Out-Null}
$_ | Move-Item -Destination $CurDest
}
}
Edit: Ok, now excludes folders, and also keeps folder structure.
Edit2: Re-designed to do a ForEach loop on the files, build the destination path as $CurDest, test to make sure it exists and make it if it doesn't, then move the files. Also changed mydocuments to documents which is the path to a user's My Documents folder.