I want to move all images in a directory, including subdirectories, to a new location while maintaining the existing folder structure.
Following the example, here, I put the objects into a variable, like so:
$picMetadata = Get-FileMetaData -folder (Get-childitem K:\myImages -Recurse -Directory).FullName
The move must be based on the results of a logical expression, such as the following for example.
foreach ($test01 in $picMetadata) {
if ($test01.Height -match "^[0-9]?[0-9] ") {
Write-Host "Test01.Height:" $test01.Height
}
}
Still at an early testing phase So far, I'm having no success even testing for the desired files. In the example above, I thought this simple regex test might provide for anything from "1 pixels" to "99 pixels", which would at least slim down my pictures collection (e.g. an expression without the caret, like "[0-9][0-9] " will return "NN pixels" as well as "NNN Pixels", "NNNNNN pixels", etc.)
Once I figure out how to find my desired images based on a logical, image object dimensions test, I will then need to create a script to move the files. Robocopy /MOV would be nice, but i'm probably in over my head already.
I was going to try to base it on this example (which was provided to a User attempting to COPY (not move / copy/delete) *.extension files). Unfortunately, such a simple operation will not benefit me, as I wish to move .jpg,.png,.gif, etc, based on dimensions not file extension:
$sourceDir = 'K:\myImages\'
$targetDir = ' K:\myImages_psMoveTest\'
Get-ChildItem $sourceDir -filter "*" -recurse | `
foreach{
$targetFile = $targetDir + $_.FullName.SubString($sourceDir.Length);
New-Item -ItemType File -Path $targetFile -Force;
Copy-Item $_.FullName -destination $targetFile
}
Perhaps you have a powershell script that could be used for my intended purpose? I'm just trying to move smaller images out of my collection, without having to overwrite same name images, and lose folder structure, etc.
Thank you very much for reading, and any advisory!
(Edit: Never opposed to improving Powershell skill, if you are aware of a freeware software which would perform this operation, please advise.)
If I understand your question correctly, you want to move image files with a pixel height of 1 up to 99 pixels to a new destination folder, while leaving the subfolder structure intact.
If that is true, you can do:
# needed to use System.Drawing.Image
Add-Type -AssemblyName System.Drawing
$sourceDir = 'K:\myImages'
$targetDir = 'K:\myImages_psMoveTest'
Get-ChildItem $sourceDir -File -Recurse | ForEach-Object {
$file = $_.FullName # need this for when we hit the catch block
try {
# Open image file to determine the pixelheight
$img = [System.Drawing.Image]::FromFile($_.FullName)
$height = $img.Height
# dispose of the image to remove the reference to the file
$img.Dispose()
$img = $null
if ($height -ge 1 -and $height -le 99) {
$targetFolder = Join-Path -Path $targetDir -ChildPath $_.DirectoryName.Substring($sourceDir.Length)
# create the target (sub) folder if it does not already exist
$null = New-Item -Path $targetFolder -ItemType Directory -Force
# next move the file
$_ | Move-Item -Destination $targetFolder -ErrorAction Stop
}
}
catch {
Write-Warning "Error moving file '$file': $($_.Exception.Message)"
}
}
Related
I tried following Remove leading spaces in Windows file names but it's not working for my use case.
I have a lot of folders and filenames that either have a blank space at the front or at the end. How would I go about removing those spaces in bulk?
This was the command-line command I used after following the linked post:
for /R %A IN ("* ") do #for /F "tokens=*" %B IN ("%~nxA") do #ren "%A" "%B"
But it didn't work out.
Update: thank you to all who replied trying to help. I think there is just a Windows-level glitch in the file system. I ended up just having to manually create new folders without leading and trailing spaces and then dragging all the files over manually then renaming those to non-trailing and leading names as well.
It's unclear whether or not you want a PowerShell solution, but there's a reasonable assumption to be made you might.
If you wanted a PowerShell solution, you could try this:
function Test-LeadingTrailingWhitespace {
param(
[Parameter(Mandatory)]
[String]$String
)
$String[0] -eq ' ' -Or $String[-1] -eq ' '
}
Get-ChildItem -Path "<path_to_folder>" | ForEach-Object {
if ($_.PSIsContainer -And (Test-LeadingTrailingWhitespace -String $_.Name)) {
$Destination = Split-Path -Path $_.FullName -Parent
$NewName = $_.Name.Trim()
Move-Item -Path $_ -Destination (Join-Path -Path $Destination -ChildPath $NewName)
}
elseif (Test-LeadingTrailingWhitespace -String $_.BaseName) {
$Destination = Split-Path -Path $_.FullName -Parent
$NewName = $_.BaseName.Trim() + $_.Extension
Move-Item -Path $_ -Destination (Join-Path -Path $Destination -ChildPath $NewName)
}
}
To be on the safe side, you could add -WhatIf or -Confirm on the Move-Item cmdlet. The former will tell you what would have changed without that parameter without actually making any changes (like a 'dry run'). The latter will prompt you for confirmation before making each change, giving you a chance to validate incrementally and not make changes en masse from the moment you hit enter.
Trim() is a method available for all strings in PowerShell:
Returns a new string in which all leading and trailing occurrences of a set of specified characters from the current string are removed.
You can loop over files and folder and check if they actually have a leading or trailing whitespace before renaming, this would avoid errors like:
Rename-Item: Source and destination path must be different.
We can use the -match matching operator with a simple regex ^\s|\s$ (starts with whitespace or ends with whitespace - regex101 link for a simple example) to see if the file or folder should be renamed:
Get-ChildItem path\to\startingfolder -Recurse | ForEach-Object {
$newName = switch($_) {
# handle folders
{ $_.PSIsContainer -and $_.Name -match '^\s|\s$' } {
$_.Name.Trim()
break
}
# handle files
{ $_.BaseName -match '^\s|\s$' -or $_.Extension -match '^\s|\s$' } {
$_.BaseName.Trim() + $_.Extension.Trim()
break
}
# if none of the above conditions were true, continue with next item
Default {
return
}
}
Rename-Item -LiteralPath $_.FullName -NewName $newName
}
Personally, I'd do this in two steps to rename folders and files separately. This to overcome the problem that when a folder is renamed, the items inside that folder all have a new path.
Using switch -Force allows renaming items such as hidden or read-only files
Using -ErrorAction SilentlyContinue swallows the error when the new name is equal to the existing name
$rootPath = 'X:\thepath'
# first the folders and subfolders (deepest nesting first)
(Get-ChildItem -Path $rootPath -Directory -Recurse | Sort-Object FullName -Descending) |
Rename-Item -NewName {$_.Name.Trim()} -Force -ErrorAction SilentlyContinue
# next the files
(Get-ChildItem -Path $rootPath -File -Recurse) |
Rename-Item -NewName {'{0}{1}' -f $_.BaseName.Trim(), $_.Extension.Trim()} -Force -ErrorAction SilentlyContinue
I need to batch copy a sort of file extensions from subfolders to specific folders for each location.
All folders have a 6 digit number
let's say folder Rood folder: BATCH
Subfolder 1: 000000
Subfolder 2: 111111
despite their extensions, most files have the name as the subfolder but some of them may have extra alphanumeric characters, therefore the script should grab only the ones that are not larger than 6 digits.
Example Subfolder1: 000000.pdf 000000.eps the script would need to grab all pdf within subfolders and export them to a PDF exclusive new folder, and the same would apply for eps files.
I know nothing about powershell but I know that something like this would work for an specific subfolder but I'm still missing the parts where it distributes them to a new PDFONLY and EPSONLY folders and the fact the I want to apply this to all the folders whiting the root folder.
Get-ChildItem -Path "C:\BATCH\*" -Include *.pdf,*.eps -Recurse | Copy-Item -Destination D:\
You can use Group-Object to group all the files by their extension and then loop over each group of objects, create a new folder with the desired name and lastly copy all objects of each group to their folders.
$target = 'D:\' # Set destination here
Get-ChildItem -Path C:\BATCH\* -Include *.py, *.ps1 -Recurse |
Group-Object Extension | ForEach-Object {
# Example of `$folderName` would be `PDF ONLY`
$folderName = '{0} ONLY' -f $_.Name.TrimStart('.').ToUpper()
$destination = Join-Path $target -ChildPath $folderName
# If the `$destination` folder doesn't exist
if(-not (Test-Path $destination)) {
# Create it
$null = New-Item $destination -ItemType Directory
}
Copy-Item -LiteralPath $_.Group.FullName -Destination $destination
}
Similar to Santiago's answer but with an extra filter and without grouping you can do this:
$destination = 'X:\somewhere'
Get-ChildItem -Path "C:\BATCH" -Include '*.pdf','*.eps' -File -Recurse |
Where-Object {$_.BaseName -match '^\d{6}$' } | # filter files with a BaseName of just 6 digits
ForEach-Object {
$targetPath = Join-Path -Path $destination -ChildPath ('{0}ONLY' -f $_.Extension.TrimStart(".").ToUpper())
# make sure the target path exists
# for directories, using the -Force switch either creates a new folder
# or returns the DirectoryInfo object of an existing folder.
$null = New-Item -Path $targetPath -ItemType Directory -Force
$_ | Copy-Item -Destination $targetPath
}
I am trying to clean up and group files in a directory by creating subdirectories and then copying the files into the new directories.
Example:
test01.a.jpg
test01.a.txt
test01.b.bak
test01.b.txt
test02.a.txt
test02.a.jpg
test02.a.bak
test03.a.txt
test03.a.bak
test03.b.txt
I wish to create subdirectories like test01, test02, test03 and ideally copy the relevant files over. All groups will have a txt file but more or less of the others.
To create the directories I have got as far as
gci -file *.txt | New-Item -ItemType directory $_.name
which doesn't work as expected.
If your files have names like that, you can simply split the filename on the dot and take only the first part as new folder name.
Then test if a subfolder like that already exists (if not create it) and move the file.
Something like this
$sourcePath = 'D:\Test' # put the real path to the files here
# if you want only the files with extension .txt, use filter '*.*.txt'
(Get-ChildItem -Path $sourcePath -File -Filter '*.*.*') | ForEach-Object {
# use the first part of the file name for subdirectory name
$destinationPath = Join-Path -Path $sourcePath -ChildPath $_.Name.Split(".")[0]
if (!(Test-Path -Path $destinationPath -PathType Container)) {
# if a subdirectory with that name does not already exist, create it
$null = New-Item -Path $destinationPath -ItemType Directory
}
# now move the file to that (new) destination folder
$_ | Move-Item -Destination $destinationPath
}
Actually, algorithm is simple (we don't need to previously compare names of files. All we need is just use a $_.BaseName property)
<#Creating folders and moving files using BaseName property #>
gci *.txt | % { new-item -ItemType Directory -Path ($_.Directory.ToString() + "\" + $_.BaseName.ToString())}
gci -file | % { Move-item $_.Fullname ($_.Directory.ToString() + "\" + $_.BaseName.ToString())}
I was downloading a huge torrent (1.2tb with over 6000 folders) divided in 2 parts, so I placed the 2nd part on the designed place and it was not a problem since the master-folder of the torrent is exactly what was needed. The 1st part master-folder was named with some generic torrent name instead of the name I needed, so instead of renaming the torrent name to "source", which I think would have worked and renamed the currently generic name to "source". In files tab I selected all the files and right-click>relocate all of them and bittorrent simply moved all of the files to the same directory, without any subfolder, and created a mess.
So I have a un-finished backup of this torrent and the files are in place, so my idea was using the un-finished one's name, match with the finished ones and put the finished ones in the un-finished matching name's path folder. I hope that was clear.
I tried to resolve this using PowerShell, but I dont know much, so I came up with this and nothing happens, something is wrong. Anyone knows a solution?
$itemlistA = Get-ChildItem -Path "D:\BitTorrent\" |
ForEach-Object {
$objnameA = $_.Name
$objPathA = $_.FullName
}
$itemlistB = Get-ChildItem -Path "E:\DesiredPath\" -recurse |
ForEach-Object{
$objnameB = $_.Name
$objPathB = $_.FullName
}
ForEach-Object{
if($objnameA -eq $objnameB){
Copy-Item -path $objPathA -Destination $objPathB
Write-Host "ffff Object ($objnameA) new Path ($objPathB) ffff"
}
}
If I'm understanding your intent correctly, the script below will accomplish your goal, assuming your goal is to copy files from a flattened directory into some (potentially) nested directories so that the incoming files overwrite files with matching names.
The O(n^2) performance of the nested loops could be improved with a sort and more efficient search.
You'd need to edit the script's params to reflect your own environment.
param(
$pathToFiles = "$PSScriptRoot\BitTorrent\",
$desiredPath = "$PSScriptRoot\DesiredPath\"
)
$itemlistA = Get-ChildItem -Path $pathToFiles | Select-Object -Property Name, FullName
$itemlistB = Get-ChildItem -Path $desiredPath -Recurse | Select-Object -Property Name, FullName
foreach ($fileA in $itemlistA) {
foreach ($fileB in $itemListB) {
if ($fileB.Name -eq $fileA.Name) {
Copy-Item -path $fileA.FullName -Destination $fileB.FullName -Verbose
break
}
}
}
I have a main folder where other folders reside with .mkg files . These folders have a certain format.
Folder = NameSerie.SxEy.Randomstuf
Item in folder = NameSerie.SxEy.Randomstuf.mkv
Where x is the season number and y is the episode number.
What i want to do is automaticly create a folder if the nameSerie isnt already created and put the .mkv files in this folder.
So, if we have folders named NameSerie.SxEy.Randomstuf we check if folder NameSerie exists, if not we create one. Then we enter folder NameSerie.SxEy.Randomstuf and copy the NameSerie.SxEy.Randomstuf.mkv file in the NameSerie folder.
the file name needs to change from NameSerie.SxEy.Ra.n[dom}stuf.mkv to NameSerie.SxEy.mkv but I cant seem to figure out how to remove the random stuf after the NameSerie.SxEy.< >.mkv
This is the code that i have managed to create but im still stuck. I have managed to create a folder if one does not exists but this only works if the .mkv file is not in a folder.
$Location = "\\<ip>\Share\Media\Series"
#rename files
Get-ChildItem $Location | Rename-Item -NewName {$_.Name.Replace(" [480p]","") }
#make folder for serie if it does not exist
Get-ChildItem "$Location\*.mkv" |
Foreach-Object {
$FullName = $_.Name
$pos = $FullName.IndexOf(" - ")
$Name = $FullName.Substring(0, $pos)
Write-Host $_.FullName
$TARGETDIR = "$Location\$Name"
if( -Not (Test-Path -Path $TARGETDIR ) )
{
New-Item -ItemType directory -Path $TARGETDIR
}
Move-Item -Path $_.FullName -Destination $TARGETDIR
}
You could use the parameter -Recurse when you list files from the path like this:
Get-ChildItem "$Location\*.mkv" -Recurse
instead of:
Get-ChildItem "$Location\*.mkv"
With the -Recurse parameter, the script will list files in the current directory and for each directory in the current directory, it will do the same recursively. So the .mkv inside other folders won't hide any longer.