This PowerShell script is intended to copy photos from one or more source directories to a destination directory, organizing them by image type (JPEG, PNG, BMP, and HEIC).
It asks the user to enter the paths of the source directories and the source directories to exclude, as well as the path of the destination directory. These paths are stored in the variables $sources, $excluded, and $destination, respectively.
The script then converts the string contained in $sources into an array using the -split instruction, which allows for splitting a string based on a specified separator (here, a comma). If the user did not specify any source directories to exclude, the $excluded variable is initialized to an empty array using the Where-Object instruction.
The path of the destination directory is added to the $excluded array in order to avoid copying the photos to the destination directory itself.
The script then checks if the destination directory exists, and creates it if it does not using the New-Item instruction. It also creates destination directories for each file type (JPEG, PNG, BMP, and HEIC).
Finally, the script searches the computer's file system for files and copies the found photos to the destination directories according to their extension. It uses the Get-ChildItem instruction to retrieve the files in the source directories and the Copy-Item command to copy these files to the destination directories. The Where-Object clause is used to verify that the file is not in one of the excluded source directories.
$sources = Read-Host "Please enter the paths of the source directories (separated by commas) :"
$excluded = Read-Host "Please enter the paths of the source directories to exclude (separated by commas) :"
$destination = Read-Host "Please enter the path of the destination directory :"
$sources = $sources -split ","
$excluded = $excluded -split "," | Where-Object {$_}
$excluded = $destination
if (!(Test-Path $destination)) { New-Item -ItemType Directory -Path $destination }
"JPG", "PNG", "BMP", "HEIC" | ForEach-Object { New-Item -ItemType Directory -Path "$destination\$_" }
foreach ($source in $sources) {
if ($excluded -notcontains $source) {
"jpg", "png", "bmp", "heic" | ForEach-Object {
$extension = $_.ToUpper()
Get-ChildItem -Recurse $source -Filter "*.$_" | Where-Object {$_.Directory.FullName -notcontains $excluded} | Copy-Item -Destination "$destination\$extension"
}
}
}
This script works very well except that it still adds the photos of the excluded folders! Why ?
CodePudding user response:
I would use a regex for this to exclude the folders.
Also I'd use Group-Object
to group the found files by extension, making it easier to copy to the destination folders:
$sources = Read-Host "Please enter the paths of the source directories (separated by commas) :"
$excluded = Read-Host "Please enter the paths of the source directories to exclude (separated by commas) :"
$destination = Read-Host "Please enter the path of the destination directory :"
$sources = @($sources -split "," | Where-Object {$_ -match '\S'}).Trim()
$excluded = @($excluded -split "," | Where-Object {$_ -match '\S'}).Trim() $destination
# create a regex of the folders to exclude
# each folder will be Regex Escaped and joined together with the OR symbol '|'
$notThese = ($excluded | ForEach-Object { [Regex]::Escape($_) }) -join '|'
# enumerate the paths for the specified files
Get-ChildItem -Path $sources -Recurse -File -Include "*.JPG", "*.PNG", "*.BMP", "*.HEIC" |
Where-Object{ $_.DirectoryName -notmatch $notThese } |
Group-Object Extension |
ForEach-Object {
$targetPath = Join-Path -Path $Destination -ChildPath $_.Name.TrimStart(".").ToUpper()
# create the target path if this does not already exist
$null = New-Item -Path $targetPath -ItemType Directory -Force
# next copy the files in this group
$_.Group | Copy-Item -Destination $targetPath -Force
}
CodePudding user response:
Replace:
$_.Directory.FullName -notcontains $excluded
with the following, which reverses the operands passed to the -notcontains
operator:
$excluded -notcontains $_.Directory.FullName
Alternatively, use the related -notin
operator:
$_.Directory.FullName -notin $excluded
Both -contains
/ -notcontains
and -in
/ -notin
test an array (collection) for containing a scalar (single object), through element-by-element equality testing. They only differ by the order of operands:
# -contains / -notcontains: array is the *LHS* (left-hand side) operand
@(1, 2, 3) -contains 2 # -> $true
# -in / -notin: array is the *RHS* (right-hand side)
2 -in @(1, 2, 3) # -> $true