I have an unusual workflow when copying photos from my camera's SD card to my Windows desktop. For any photos I my desktop, I want to also delete on the SD card. In this post (part 1 of 2), I describe my PowerShell script to "repeat" what I deleted on the desktop on the SD card. In my next post, part 2, I expand the PowerShell script to (try) monitor for file system changes instead.
My workflow:
- I copy all files (JPGs and RAWs) from the camera's SD card to my desktop in a brand new folder. The filenames have a fixed prefix followed by 4 numbers, e.g.
DSCF0001.JPG
and the associated RAWDSCF0001.RAF
. - On my desktop, I use FastStone Image Viewer to view and compare photos, and delete the JPGs I don't want.
- Now, I delete the same files (both JPEG and RAW) on the SD Card, but leave the retained files alone - just in case I decide to re-process the RAWs in-camera, or so that I can copy good photos from the SD card to another notebook / desktop.
PowerShell Script to Check for Deleted Files
Say I have DSCF0001.JPG
, DSCF0002.JPG
, DSCF0003.JPG
, DSCF0004.JPG
, and I delete DSCF0002.JPG
.
I wrote a PowerShell script, find_deleted.ps1
, to:
- Get the highest and lowest JPG file number in the folder e.g.
1
to4
- the script uses a simple Regular Expression to extract the numeric part (-replace
is a String function) - Run through the sequence from first to last, and check if a file still exists with that file name - e.g. the file with number
2
has been deleted. - Generate a batch file with delete commands of the photos I deleted - e.g. outputs a script containing
del DSCF0002.JPG
.
You shouldn't have a file called run_delete.cmd
already, unless you really want to append to it.
param (
[int]$start = -1,
[int]$end = -1,
$out = "run_delete.cmd"
)
$folder = pwd
$output = "$folder\$out"
$files = Get-ChildItem -Path ".\DSCF*.JPG" -Name | Sort-Object
if ($files -eq $null) { Write-Host "No JPGs found!"; exit }
$first = if ($start -ge 0) { $start } else { [int] ($files[0] -replace 'DSCF(\d+).JPG','$1') }
$last = if ($end -gt $start) { $end } else { [int] ($files[$files.length-1] -replace 'DSCF(\d+).JPG','$1') }
Write-Host "Checking JPG files from $first to $last"
for ($i=$first; $i -le $last; $i++) {
$file = 'DSCF' + $i.ToString('0000') + '.*'
if (!(Test-Path $file)) {
$file1 = 'DSCF' + $i.ToString('0000') + '.JPG'
$file2 = 'DSCF' + $i.ToString('0000') + '.RAF'
Write-Host "del ""$file1"" ""$file2"""
Out-File $output -Encoding ASCII -Append -InputObject "del ""$file1"" ""$file2"""
}
}
Running the PowerShell Script
Now, to run the script from the Command Prompt (cmd
) in the folder where my photos reside after deleting any JPGs that I don't want:
cd my_photos
powershell -ExecutionPolicy ByPass -File find_deleted.ps1
If you run an unsigned script powershell find_deleted.ps1
, you may get a RuntimeExeception that the script "cannot be loaded because the execution of scripts is disabled on this system." To avoid this, the -ExecutionPolicy
is required.
If I deleted the first or last file, then of course the number range determined by the script would be incorrect. So, the -start
parameter sets the start, and -end
optionally sets the end number.
powershell -ExecutionPolicy ByPass -File find_deletes.ps1 -start 10 -end 20
The script creates the file run_delete.cmd
, which I move to the SD card (the same folder where the photos reside) and run it manually. Something like:
move run_delete.cmd F:\DCIM\
F:\
cd DCIM
run_delete.cmd
Conclusion
A reasonably simple script, since PowerShell has good support for Regular Expressions. On to part 2...