Move all even files to different directory | Powershell and regex - powershell

I'm trying to move all even files from the current location to the directory "foo", but I have a problem matching them with regex.
Filenames are in this format:
11.txt, 121.txt, 342.txt
The command I'm currently using is:
Get-ChildItem | Where-Object {$_.Name -match '^[0-9]*[02468]$'} | Move-Item -Destination .\foo
The previous command I was using works OK but only for two-digit files 1.txt-99.txt
Get-ChildItem | Where-Object {$_.Name -match '^[0-9]+[02468]'} | Move-Item -Destination .\foo
I tried at https://regex101.com/ site with .NET flavor and checked this regex ^[0-9]*[02468]$ and it works OK - matches all even numbers, but for some reason, I have a problem with above mention PS command...

An alternate method I have used previously for odds/evens is a division method. The solution Santiago has provided in the comments is also a valid regex method for this problem.
# Gets files and starts loop on files
Get-ChildItem "C:\Temp\AllFiles" -File | ForEach-Object {
# If the BaseName of file is divisible by 2, else
If($_.BaseName % 2 -eq 0) {
Move-Item -Destination "C:\Temp\Evens" -Path $_.FullName
} Else {
Move-Item -Destination "C:\Temp\Odds" -Path $_.FullName
}
}

Related

Windows 10, Rename all *.jpg files in all subdirectories, with number starting from 1

I want a solution for the same problem, but in Windows 10.
Recursively rename .jpg files in all subdirectories
I tried with following powershell command,
Get-ChildItem -Recurse -Include *.jpg | % { Rename-Item $_ -NewName ('{0:D1}.jpg' -f $i++)}
but it renames the files in sequential order without resetting the index to 1 in every sub folder.
I think you need two separate Get-ChildItem cmdlets for this. The first will gather all subdirectories and when looping though that, the second will gather the files in each directory:
Get-ChildItem -Path 'X:\RootFolder\where\the\files\are' -Recurse -Directory | ForEach-Object {
$count = 1 # reset the counter for this subdir to 1
Get-ChildItem -Path $_.FullName -Filter '*.jpg' -File | ForEach-Object {
$_ | Rename-Item -NewName ('{0:D1}.jpg' -f $count++) -WhatIf
}
}
Remove the -WhatIf if you are satisfied with the results shown in the console.
P.S. the title says *.png, but your code deals with *.jpg. Doesn't matter, as long as you set your filter to the correct extension and adjust the new name in the code accordingly
As of my knowledge you have to do use it as a nested foreach:
Foreach ($directory in (Get-ChildItem -Directory)){
$i = 1
Get-ChildItem $directory.Fullname -Recurse -Include *.jpg | % { Rename-Item $_ -NewName ('{0:D1}.jpg' -f $i++)}
}
I tested it and it worked :)
If it worked for you, please mark it as the accepted answer.

File Sorting Based on Similar File and Folder Names

Im still generally new to powershell, and I am trying to create a program that will take files based on their name, and move them into folders that have a similar name but not exactly the same.
For example, Lets say I have 3 files, Apples.txt, Grapes.txt, and Oranges.txt. And I want to move them into corresponding folders, ApplesUSA, GrapesNY, OrangesFL.
I could just hard code it using a loop and a If-Then Statement. i.e If Apples.txt exists move to ApplesUSA. But I want it to be dynamic, so if other files and folders are added later I dont have to update the code. Is there a way to write a statement that would say if FileA and FolderB are similar in name (both contain apples in the name somewhere) then move fileA to FolderB and so on.
Any help appreciated. Thanks!!!!
try Something like this
$PathWithFile="C:\temp\Test"
$PathWithDir="C:\temp\Test"
Get-ChildItem $PathWithFile -file -Filter "*.txt" | %{
$CurrentFile=$_
$Dirfounded=Get-ChildItem $PathWithDir -Directory | where {$_ -match $CurrentFile.BaseName} | select FullName -First 1
if ($Dirfounded -ne $null)
{
move-Item $CurrentFile.FullName -Destination $Dirfounded.FullName -WhatIf
}
}
A oneliner similar to #Esperento's
gci *.txt -af|%{$File=$_.FullName;gci "$($_.BaseName)*" -ad|%{Move $File -Dest $($_.FullName) -whatif}}
The verbose version:
PushD "X:\path\to\base\folder"
Get-ChildItem *.txt -File | ForEach-Object{
$File = $_.FullName
Get-ChildItem "$($_.BaseName)*" -Directory | ForEach-Object {
Move-Item $File -Destination $_.FullName -whatif
}
}
PopD
Both versions require PowerShell V3 for the -File and -Directory parameters (and their aliases -af/-ad) This can be substituted by an additional |Where-Object{ $_.PSIsContainer} respective | Where-Object{!$_.PSIsContainer}

error from Powershell when try to rename with sequential prefix *recursively*

I have no problem adding sequential prefixes to filenames. The following works great on the top directory in question.
$path="E:\path\newtest1"
$count=4000
Get-ChildItem $path -recurse | Where-Object {!$_.PSIsContainer -and $_.Name -NotMatch '^\d{4}\s+'} | ForEach -Process {Rename-Item $_ -NewName ("$count " + $_.name -f $count++) -whatif}
BUT if there are files in subfolders within the top directory, these are all completely missed. Whatif reports that for any deeper file it "does not exist".
I have tried the following, based on looking at some pages on other recursion problems, but as you can probably guess I have no clue what it is doing. Whatif shows that it does at least pickup and rename all the files. But the following does it too much and makes multiple copies of each file with each number:
$path="E:\path\newtest1"
$count=4000
Get-ChildItem -recurse | ForEach-Object { Get-ChildItem $path | Rename-item -NewName ("$count " + $_.Basename -f $count++) -whatif}
Really keen to get some guidance on how to get the first of these two snippets to work to find all files in all subdirectories and rename them with sequential number prepended.
Try it like so:
Get-ChildItem $path -recurse -file | Where Name -NotMatch '^\d{4}\s+' |
Rename-Item -NewName {"{0} $($_.name)" -f $count++} -whatif
When you supply $_ as an argument (not a pipeline object), that gets assigned to the Path parameter which is of type string. PowerShell tries to convert that FileInfo object to a string but unfortunately the "ToString()" representation of files in nested folders is just the filename and not the full path. You can see this by executing:
Get-ChildItem $path -recurse -file | Where Name -NotMatch '^\d{4}\s+' | ForEach {"$_"}
The solution is either to A) pipe the object into Rename-Item or B) use the FullName property e.g. Rename-Item -LiteralPath $_.FullName ....

Rename file by replacing character and overwrite

On Windows XP, in a folder of files, I need to rename some files, replacing one character in the filename with another and overwriting any files that already have that name.
For example, the folder contains these 2 files:
fileA.xml
fileb.xml
I need to rename fileA.xml to fileb.xml, overwriting the original fileb.xml
Using PowerShell, I have this command:
Get-ChildItem *.* -include *.xml | Rename-Item -NewName { $_.name.Replace("A","b")}
The rename doesn't work as the file already exists.
Doesn't have to be done in PowerShell, but this is the closest I've come so far.
You can try the Move-Item command instead, with the -Force parameter.
Get-ChildItem . -include *.xml | Move-Item -Destination { $_.name.Replace("A","b")} -Force
First, you need to filter to get the files that you actually want to rename.
Get-ChildItem . -include *.xml | Where-Object { $_.name -match "A$" }
And feed this to Move-Item to rename:
Get-ChildItem . -include *.xml | Where-Object { $_.name -match "A$" } | Move-Item -destination { $_.name -replace "A$", "b" }

Get-ChildItem results looks like relative paths in Powershell

I would like to scan and move folders (and sub folders or even deeper) from one folder to another using Powershell.
Currently I'm using this pipe of commands.
Get-ChildItem -recurse -path sub\WORK -filter "* OK" | Where-Object { $_.PSIsContainer } | foreach { Move-Item -path $_ -destination sub\OK }
Unfortunately it doesn't work because the found results are relative to .\sub\WORK, when trying to move them Move-Item complains that the folders are not in the current folder:
Move-Item : Cannot find path 'C:\TMP\2011-12-12 test 2 OK' because it does not exist.
I expect that $_ would contain: 'C:\TMP\sub\WORK\2011-12-12 test 2 OK' because these are objects in Powershell and no strings like in Linux.
In case you use Get-ChildItem, be very careful. The best way is to pipe the objects to Move-Item and you don't need to think about it more:
Get-ChildItem -recurse -path sub\WORK -filter "* OK" | Where-Object { $_.PSIsContainer } | Move-Item -destination sub\OK
(no need to use Foreach-Object)
The main reason why I'm answering is this one: Get-ChildItem constructs object differently, depending on the parameters. Look at examples:
PS C:\prgs\tools\Console2> gci -include * | % { "$_" } | select -fir 5
C:\prgs\tools\Console2\-verbose
C:\prgs\tools\Console2\1UpdateDataRepositoryServices.ps1
C:\prgs\tools\Console2\22-52-59.10o52l
C:\prgs\tools\Console2\2jvcelis.ps1
C:\prgs\tools\Console2\a
PS C:\prgs\tools\Console2> gci | % { "$_" } | select -fir 5
-verbose
1UpdateDataRepositoryServices.ps1
22-52-59.10o52l
2jvcelis.ps1
a
Then if you use $_ in a cycle and PowerShell needs to convert FileInfo from Get-ChildItem to string, it gives different results. That happened when you used $_ as argument for Move-Item. Quite bad.
I think there is a bug that reports this behaviour.
You are correct that objects are being piped down the pipeline instead of strings. This is good in that it is more flexible. The drawback is that if you don't explicitly tell the system which property of the object to use you are at the mercy of the system designers. See if explicitly telling the system the property that you want will help:
Get-ChildItem -recurse -path sub\WORK -filter "* OK" | Where-Object { $_.PSIsContainer } | foreach { Move-Item -path $_.Fullname -destination sub\OK }
I just learn't that the PSPath is automatically used in Copy-Item, Move-Item etc. when you don't specify the source in a pipeline, so something like:
gci .\sub\Work | move-item -Destination .\sub\OK
(simplified example)
would work and it would use the PSPath of the passed object to determine the source.
Since the Get-ChildItem returns objects like you said, you can use Get-Member to see what the object has to offer ( that is know about its properties and methods)
Get-ChileItem path | Get-Member
You could see that FullName is one of the properties that you could use.
Here is what worked for me.
Get-ChildItem -Path .\ -Recurse -filter "* OK" | %{Join-Path -Path $_.Directory -ChildPath $_.Name } | Move-Item -Destination sub\OK