How to delete empty folders with Applescript? - applescript

I'm writing an Applescript which prompts the user to select a folder that it will then run the script on. The script runs and basically sorts files into folders. At the end of the script, I want the empty folders to be deleted.
The script works wonderfully until I want to delete the empty folders at the end.
This is what picks the folder:
tell application "Finder"
set packageFolder to (choose folder with prompt "Please choose your
logo package folder") as string
end tell
And this is the code to delete the empty folders:
tell application "Finder"
repeat with oneFolder in (get folders of packageFolder)
if (count items) of oneFolder is 0 then delete oneFolder
end repeat
end tell
I get the following error:
Result:
error “Can’t get every folder of |”Macintosh HD:Users:michael:
Desktop:Logo Package Script Test:\”.” number
-1728 from every class cfol» of “Macintosh HD:Users:michael: Desktop: Logo Package Script Test:”
What am I doing wrong here?

What about hidden files and folders?
The answers provided here and here in this thread, consider any folder which contains only hidden1 files and/or hidden folders to be empty too - resulting in them being deleted, which may lead to the loss of important data.
A Safer Solution:
Using the following solution an empty folder is considered to be empty, only if it does not contain a file(s) and/or non-empty folder(s) - whether they're visible or hidden. However, the exception to this rule is for any folder containing only a hidden .DS_Store file - they will be considered empty, and therefore deleted too.
This solution utilizes the Bash find command and executes it via AppleScripts do shell script command.
tell application "Finder"
set packageFolder to choose folder with prompt "Please choose your logo package folder"
set posixPath to quoted form of POSIX path of packageFolder
do shell script "find " & posixPath & " -name '.DS_Store' -type f -delete && find " & posixPath & " -empty -type d -delete"
end tell
Explanation:
Essentially, this AppleScript executes the following Bash commands:
find /path/to/directory/ -name '.DS_Store' -type f -delete
find /path/to/directory/ -empty -type d -delete
Let's breakdown the parts of those two commands for a better understanding of what's happening:
The first command:
find - Search a folder hierarchy for file and folder name(s) which meet a given criteria.
path/to/directory/ - This (contrived) path points to the directory where the search should begin from. This will be replaced with a real path, i.e. the path to whichever folder is chosen and assigned to the packageFolder variable via the choose folder command.
We provide that path (as a POSIX path) to the do shell script command via the part which reads:
set posixPath to quoted form of POSIX path of packageFolder
The quoted form part ensures the folder path, (i.e. the one assigned to the posixPath variable), is provided to do shell script avoiding further interpretation by the shell - it essentially ensures the path to the chosen folder, whose name may include spaces, is handled correctly.
-name - The name of the file to search for, i.e. .DS_Store.
-type f - This option specifies that we only want to include a regular file.
-delete - This option deletes all files which match the previously specified criteria, i.e. files named .DS_Store.
The second command:
find - as per the first command.
path/to/directory/ - as per the first command.
-empty - This option specifies that we want to include either a regular file or a directory only if it is empty.
-type d - This option specifies that we only want to include directories.
-delete - as per the first command., however this time we delete empty folders only.
Example Directory (Before and After).
Given a chosen source directory tree like this:
.
├── a
│   ├── .hidden-dir-a
│   │   └── foo.txt
│   └── .hidden-dir-b
├── b
│   └── bb
│   ├── .DS_Store
│   └── bbb
├── c
│ └── .hidden.txt
└── d
├── dd
│   └── ddd
└── quux.txt
The resultant directory tree (i.e. after running the AppleScript) will be:
.
├── a
│   └── .hidden-dir-a
│   └── foo.txt
├── c
│ └── .hidden.txt
└── d
└── quux.txt
Refactor:
The current AppleScript (above) assumes that you're using the path which is assigned to the packageFolder variable elsewhere in your script, hence it hasn't been changed. Consequently, it introduces the line which reads;
set posixPath to quoted form of POSIX path of packageFolder
to obtain a POSIX path equivalent to the chosen folder path.
However, If the packageFolder variable is not used elsewhere, you could refactor the script to the following instead:
tell application "Finder"
set packageFolder to quoted form of (POSIX path of (choose folder with prompt "Please choose your logo package folder"))
do shell script "find " & packageFolder & " -name '.DS_Store' -type f -delete && find " & packageFolder & " -empty -type d -delete"
end tell
1 Hidden files and folders on MacOS are those whose name begins with a dot (.). For instance .gitignore

This works for me on the latest version of macOS High Sierra
set packageFolder to (choose folder with prompt "Please choose your
logo package folder") as string
tell application "Finder"
set theFolders to folders of entire contents of folder packageFolder
repeat with i from 1 to count of theFolders
set thisFolder to item i of theFolders
if (count of items of thisFolder) is equal to 0 then
delete thisFolder
end if
end repeat
end tell

This is what ended up working:
on killEmpty(fol)
tell application "Finder"
repeat with f in (get fol's folders)
my killEmpty(f)
end repeat
if (count items of fol) is 0 then delete fol
end tell
end killEmpty
tell application "Finder"
repeat with f in (get folders of folder packageFolder)
my killEmpty(f)
end repeat
end tell
I found this solution here:
https://discussions.apple.com/message/20188767#message20188767
Thanks again everyone. Sorry about the inaccurately worded question.

Related

AppleScript - How to get a folder's first child path (only when the first child is a folder, not a file)?

How do you get a folder's first child path (when the first child is a folder)?
Say you have...
Folder 1 ¬
File 1
File 2
Folder 2 ¬
File A
File B
File C
File D
Use case:
I select a batch of folders, all on the same level as Folder 1 with:
set foldersToProcess to choose folder with multiple selections allowed
Then, I loop through each each of the queued folder foldersToProcess, where in the process I want to look into the first child folder of Folder 1 (being Folder 2), every single time.
How do I do this?
You should use try block because maybe some of the chosen folders doesn't contain subfolders at all. So, without try block it will throw error when asking for folder 1 of empty list.
To get first folders as System Event's folder references:
set chosenFolders to (choose folder with multiple selections allowed)
set firstFolders to {}
repeat with anAlias in chosenFolders
try
tell application "System Events" to ¬
set end of firstFolders to folder 1 of folder (anAlias as text)
end try
end repeat
To get HFS paths of first folders, edit corresponding code line with:
tell application "System Events" to ¬
set end of firstFolders to path of folder 1 of folder (anAlias as text)
To get Posix paths of first folders,edit corresponding code line with:
tell application "System Events" to ¬
set end of firstFolders to POSIX path of folder 1 of folder (anAlias as text)

Bash - Copy files iteratively

I want to copy a list of files, called dti_fin_fa, from one folder to another.
These files are scattered in different folders.
Controls
└───C01
│ └───difusion
│ └───Deterministic
│ └───dti_fin_fa.nii
└───C02
│ └───difusion
│ └───Deterministic
│ └───dti_fin_fa.nii
└───C03
│ └───difusion
│ └───Deterministic
│ └───dti_fin_fa.nii
I want to select and copy all the dti_fin_fa, keeping the folder structure, so, in my new directory, I would have the folder distribution just seen above. The problem is that in this folders, (deterministic, difusion, etc), there are lots of other files I don´t want to copy, so I can´t just copy the main folder (C01 etc)
This is what I have:
#!/bin/bash
DIR="/media/Batty/Analysis"; cd "$DIR" || exit
for group in Controls; do
for folder in $group/*; do
for dti in $folder/difussion/Deterministic/dti_fin_fa.nii; do
echo $dti
cp $dti /media/Roy/Analysis/Controls/ --verbose
done;
done;
done;
The problem is that this code copies each of the dti_fin_fa.nii images into /media/Roy/Analysis/Controls/, hence keeping just the last one, instead of creating all the other subfolders.
Could something like this work?
cp $folder/difusion/Deterministic/$dti /media/Roy/Analysis/Patients/ --verbose
Iterate through each control, make the equivalent directory and copy only the file you want:
for group in "Control"/* "Patients"/*; do
orig="$group/difusion/Deterministic"
dest="/media/Roy/Analysis/Controls/$(dirname group)/$orig"
mkdir -p "$dest"
cp "$orig/dti_fin_fa.nii" "$dest/"
done
I suppose you execute this from the DIR directory of your example. So here orig is the relative path to the image folder, dest is the absolute path to your destination directory, including the preserved original folder structure. mkdir -p ensures that such end directory exists and finally the image is copied.

linux - batch move files into a directory and rename those files according to sequential syntax in that directory

I have two directories - A and B - that contain a bunch of photo files. Directory A is where I keep photos long-term, and the files inside are sequentially named "Photo-1.jpg, Photo-2.jpg, etc.".
Directory B is where I upload new photos to from my camera, and the naming convention is whatever the camera names the file. I figured out how to run some operations on Directory B to ensure everything is in .jpg format as needed (imagemagik convert), remove duplicate files (fdupes), etc.
My goal now is to move the files from B to A, and end up with the newly-added files in A sequentially named according to A's naming convention described above.
I know how to move the files into A, and then to batch rename everything in A after the new files have been added (which would theoretically occur every night), but I'm guessing there must be a more efficient way of moving the files from B to A without re-naming all 20,000+ photos every night, just because a few new files were added.
I guess my question is two parts - 1) I found a solution that works (us mv to rename all photos every night), is there any downside to this? and 2) If there is a downside and a more elegant method exists, can anyone help with a script that would look at whatever the highest number that exists in A, then re-name the files, appending onto that number, in B as they are moved over to A?
Thank you!
This bash script will only move and rename the new files from DiretoryB into your DirectoryA path. It also handles file names with spaces and/or any other odd characters in their name in DirectoryB
#!/bin/bash
aPath="./photos-A"
bPath="./photos-B"
aPattern="Photo-"
lNum=$(find $aPath -type f -name "*.jpg" -printf "%f\n" | \
awk -F'[-.]' '{if($2>m)m=$2}END{print m}')
while IFS= read -r -d $'\0' photo; do
mv "$photo" "$aPath/$aPattern$((++lNum)).jpg"
done < <(find $bPath -type f -name "*.jpg" -print0)
Note
The command to find the last numbered photo, aka $lNum will run over all 20K+ files, but it should be fairly quick. If it's not, you can always run this once and store the latest number into a file and read from that file.
Proof of Concept
$ tree photos-A/
photos-A/
├── Photo-1.jpg
├── Photo-2.jpg
├── Photo-3.jpg
├── Photo-5.jpg
├── Photo-6.jpg
├── Photo-7.jpg
└── Photo-8.jpg
0 directories, 7 files
$ tree photos-B/
photos-B/
├── bar.jpg
├── baz\ with\ spaces.jpg
└── foo.jpg
0 directories, 3 files
$ ./mvphoto.sh
$ tree photos-A/
photos-A/
├── Photo-10.jpg
├── Photo-11.jpg
├── Photo-1.jpg
├── Photo-2.jpg
├── Photo-3.jpg
├── Photo-5.jpg
├── Photo-6.jpg
├── Photo-7.jpg
├── Photo-8.jpg
└── Photo-9.jpg
0 directories, 10 files

How to move items to folder based on first three characters of file Mac

I am trying to move files such as
68 - Logo - Standard (B&W).pdf
to the corresponding folder which would be something like
68 - Oak Hill xyz
These folders are in a sorted logos folder, which has all of the folders in a list like
68 - Oak Hill Xyz
284 - Juniata Hill Xyz
294 - Highland Hill xyz
I have found multiple versions of different AppleScripts to accomplish this, but they seem to be outdated or no longer work properly when I try them. I have also tried this terminal script to no avail.
find . -type f -name “*pdf” -maxdepth 1 -exec bash -c 'mkdir -p “${0%%-*}”’ {} \; \
-exec bash -c 'mv "$0" "${0%%_*}"' {} \;
These pdf files are also in a different folder than the corresponding numbered folders are in.
This should do what you want (making a few assumptions...):
property destination_folders : "/Users/<your name>/Downloads/Logos/BW PDF"
set files_to_sort to choose file with multiple selections allowed
tell application "System Events"
repeat with a_file in files_to_sort
set prefix to text 1 through 3 of (get name of a_file)
set destination to my folderStartingWith(prefix)
if destination is not false then
move a_file to destination
end
end repeat
end tell
on folderStartingWith(txt)
tell application "System Events"
try
set target_folder to first folder of folder destination_folders whose name begins with txt
on error errstr
display dialog errstr
return false
end try
return target_folder
end tell
end folderStartingWith
Just substitute your user_name where it says in the first line, run the script, and select the files you want to process in the dialog that appears. This will present an error message and skip over any files that do not have corresponding folders.
This does not follow folder hierarchies (though could be modified to do so). It could also be adapted into a droplet, folder action, or finder service if this is something that you have to do regularly.

Finding and listing all subfolders but nothing else

I am trying to make a looping script that will take a parent folder and find all the sub folders. This way I can take them one at a time and run a second script on them.
This would work if I was looking for text files. I want to try to use it for find just folders but don't know how to and can't find anything in that direction.
-- Search for Subfolder
set ParentPath to choose folder
set allFiles to (do shell script "find " & quoted form of POSIX path of ParentPath & " -iname \"*.eps\""))
-- Process files
repeat with nFile in allFiles
my runScript(nFile)
end repeat
In theory it would be this.
-- Search for Subfolder
set ParentPath to choose folder
set allFiles to all folders inside ParentPath
-- Process files
repeat with nFile in allFiles
my runScript(nFile)
end repeat
Thanks!
With bash, you'd say:
shopt -s globstar nullglob
# the trailing slash below restricts matches to directories.
for dir in **/; do
some command with "$dir"
done
This recursively finds all directories under the current directory. If you're only looking for subdirectories of the current dir: for dir in */
find <dirname> -type d > subdirlist
You don't need to specify depth levels for find, either; it will keep going down to the end. You actually only specify a max depth if you don't want it to go too far.
You can use a find loop as the basis for your processing script, as well. As one example:-
find *.mp3 d.mp3 -type f | while read mp3s
do
echo $mp3s
<more commands>
done
Just try below:
find ./ -type d
Found this works perfectly.
set ParentPath to (choose folder) as string
set FolderList to {}
tell application "System Events"
set ItemList to disk items of folder ParentPath
repeat with CurrentItem in ItemList
if (class of CurrentItem) = folder then
set end of FolderList to ((path of CurrentItem) as alias)
end if
end repeat
end tell

Resources