Renaming folder based on content

Does anybody have a workflow to rename a folder (add an ending) based on the folders content?

Let’s say I have many project folders, shared across many platforms/users/OSes.

I have a workflow that marks certain files with “macOS color labels”. Which get lost across sync platforms. What if “green” is done and I want to add a “DONE” to the end of the folders name containing that file?
And if there’s no green file in it…remove the “DONE”.

Something like: “If folder contains a file with color label green and folder name does not end with DONE…add DONE”.

I do not have an existing workflow for this, but I am sure it can be done.

How it gets done will depend on your specifics however.

Firstly:

  1. I assume that you have a sync service that keeps a folder tree in sync between your various computers/systems, eg Dropbox or the like
  2. I assume that you intend for the driver for the folder name changes to be on the Mac
  3. I assume this needs to happen automatically, without your intervention other than creating the “green” labeling.

Some questions:

  1. Will you trigger the folder renaming by having a file in the folder named “green” or will you trigger it by having a file with a tag named “green”?
  2. If you are triggering via a tag, do you want the folder renamed if ANY file in the folder has a “green” tag or ALL of the files? Eg is the folder renamed with “DONE” if ALL of the files are tagged green, or does it only take one file tagged green to make it DONE?
  3. Are all the folders that this applies to under the same parent folder, or are the distributed about your home folder on the Mac? This governs the search scope for whatever rules are created.
  4. What tools are available to you? Hazel, Keyboard Maestro, for example? Or will this be done entirely with built in MacOS tools, eg Shortcuts, shell scripting, AppleScript?

If you provide more information, I can probably give you an outline of how this can be accomplished.

1 Like

1: yes. 2: yes. 3: yes

1: label. But any workflow that considers folder content (either having a file named “green” or labeled “green”) is ok. Triggering the action is driving me nuts. Finding/matching files (recursively) is the easy part… 2: a single file is tagged green. 3: everything in a folder structure. If distributed, I would only need to apply the solution to any other folder 4: Hazel, KM, Python, zsh, …whatever works. I do 80% of my automated workflows in Hazel, 20% in zsh. A shell script that runs once per day (crontab) would also be OK.

Addon: I get the $1 in Hazel (full path+name). Maybe convert to path only and then rename? Is there a $whatever for just the path?

Just to ensure I understand:

The task is to check every folder in a given folder location eg “Projects/Project1” and “Projects/Project2” … “/Projects/Projectn”. For each of those folders, if there is ANY file in that folder tagged “green” then rename the folder to “Projects/ProjectnDONE” and if there is no file tagged “green”, rename to “Project/Projectn”.

Should only the files in Projectn be checked, or should we scan down all of the sub folders of Projectn as well to find something tagged “green?”

Triggering can be done via having Hazel watch Projects with a rule that checks each folder in Projects to see if it needs renaming. It’s better to do this, I think than to watch every folder down in the tree for changes, especially if you don’t need the rename to happen instantly. KM could do the same, running every hour, for example.

Scanning for the green tag is a bit harder. A loop in KM would work, but it a bit harder of you want to recurse down the folder tree, but doable. I would personally use an external program that checks for presence / absence of a green tag recursively if you want to go that way. It is easy to do in Swift.

Correct. And no need for subfolders.

Yes, I can do that in Swift or maybe even Python. I was just wondering if the “usual suspects” (Hazel, KB) could do this…

Doing it in Python is not necessarily easy, because python 3 does not have a library for accessing Finder tags (python 2 actually did). It is reasonably straightforward to scan the files in a folder for a given tag in Swift.

However, if you just need to check the files in a folder without recurring down into sub folders, then doing this in KM is fairly simply. You just loop through every file in a given folder and check each file in a loop. I can put together a template for this if you need some help. I can also given you Swift code that will check every file in a folder and also recurse into sub folders if that helps. To do the recursion in KM, you would create a subroutine macro that checks a folder and recursively call it. It is a bit more difficult to do in KM, than in Swift, however.

Hazel has an trigger for the Color label.
And it could run any script.
So basically you only have to set up a hazelrule Forchheim folder you want to watch, that checks for the Color label, and then a Script (I have almost no idea from todays scripting unfortunately!) that gets to the folders name „above“ and rename it with the addition „Green“.

That’s why I asked “Addon: I get the $1 in Hazel (full path+name). Maybe convert to path only and then rename? Is there a $whatever for just the path?”

I could use echo “${$1%/*}/” to get the folder name. But i don’t like to have that many scripts buried in different applications. I would leave it as a script and run it through cron. A simple, click&go solution in Hazel would be my preferred variant, but it seems it’s not possible.

As I am stated, I have no real clue about scrippting. If I use a script, it is always written by someone else.
But as far as I know, you can get the path of a file with a script, and you can set different variables to use it further.

So, it should be possible to get the path of the file with the Tag, shorten it by the name of the file, to get the path of the folder, and therefore extract the name of the folder to change it thereafter.

And you can run the Hazel-Rule also on the subfolders.
https://www.noodlesoft.com/manual/hazel/advanced-topics/processing-subfolders/

Interesting question. Would you only tag a file with the green tag if it is in a folder you wanted to rename?

If so I would just use a shell script that used the spotlight database to find all files tagged green and change the folder name.

I would also use the spotlight database to check the files in a folder to see if you need to remove the done ending.

I will write this later tonight and post and example.

So here is a Hazel rule that I think will do what you want.

The assumption is that you have a folder in which all of your project folders live, as per my earlier assumptions. You would add this Hazel rule to that parent folder, and it will operate on every folder in the parent. It also assumes that if a file is tagged with “green” anywhere in a project folder or any of its subfolders, then the project gets DONE added and if there is nothing tagged with green the DONE is removed. You could modify the mdfind query so that it would only examine files, or folders, or files of a given type (eg Pages documents) if that met your needs better.

Here is the rule:

Basically, all it does is look at every folder in your top level projects folder.

For each folder, use an embedded shell script that will use an mdfind query and get the count of anything in that folder or below tagged with green, and decide if the folder name should or should not end in “DONE”. If this results in a change in the folder name, do the change.

Note that if you actually named a folder “abcDONE” and wanted it to become “abcDONEDONE” if something were tagged green, that will fail here. I assumed you would not do that.

Note the use of a “trick” in shell scripting, where I add DONE to the end of the folder name using:
new="${old%DONE}DONE".
The trick is that in zsh, ${old%DONE} removes DONE from the end of the value of the shell variable old, and then the second DONE outside the curly brace adds is back. That way, if the folder is already “abcDONE” then we remove the DONE and read it, getting abcDONE which is the desired result. This prevents duplicating DONE, and is a bit faster and more concise (although more obscure) than an if statement that checks to see if the folder already ends with DONE and then adding DONE if not. I often find that shell scripting leads me to do things in a way that I would not in any other language.

Please note that I did not test this, so you should play with this on a test folder tree and not your production folders. There is no inherent error checking, so please don’t unleash this on your real work until you have made sure it works on a test folder tree!

Looks nice, but is it not running now around and around?

@Ulli I am not sure I understand what you mean?

As I said earlier, I can not really “read” a script, but from what I read it seems, that this script/rule is running now over and over again with the same folders and files.
Is there a “Stop” in it, that is not renaming the folder, if the folder already contains the “Done” in the name, because the script has already renamed it?

@Ulli Yes, I understand.

Two responses:

  1. Firstly, the way the script is written, for each folder, it will check to see if there is any file in the folder or its subfolders tagged as green. If so, then the folder needs to end with DONE and if not, the DONE should be removed from the folder name if it is present. Hazel works by scanning a folder and applying the rules created for that folder to every entry in the folder. This rule checks each entry to see if it is a folder (the only condition specified) and if so, has a single action which runs the embedded shell script. The script is what checks to see if there is anything tagged “green” (using the mdfind command which queries the Spotlight database) and then figures out the correct folder name - with or without DONE as appropriate. It is entirely possibly for this rule to run on a given folder many many times, since Hazel runs at frequent intervals executing these rules, but that does not matter as if the folder has the correct name already nothing happens, and if it does not it gets renamed. The overhead of executing this script is minimal so it doesn’t really matter if runs over and over.

  2. What I did not address, and which is a good point, is something that relates to the way Hazel works. Specifically, when Hazel executes a rule on a file, it has a way of remembering that it has already executed a rule on a given file so that the rule does not keep on running over and over again UNLESS the file has been changed in the intervening time since the rule was last run. I admit that I am not always clear on how exactly Hazel keeps track of this, and that sometimes catches me. I believe that the exit status from the script would govern if the rule executes repeatedly or not, and in this case I would want it to repeatedly execute because you would want the folder name to react to a change made to file in the folder (adding or removing the green tag) even though the folder itself was not directly changed to trigger Hazel to process it again.

To make sure this works as needed would require a bit of experimentation. For example, the script may have to exit with a failure status to make sure it executes repeatedly. As written it does not.

If the OP adopts this approach and it does not work as expected due to Hazel execution issues, either he will figure that out or if needed I will play with it.

I hope this clarifies things (although rereading it I am not sure that I did!).

OP is dealing with other stuff right now. :smiley: Thank you very much, I will play with that in the next days.

I had many ideas on “rename folder based on content” and will take it from here. Will keep you posted on how it works.

Can you paste the script as text to copy it? :wink:

Here you go.

Please remember this is not tested so you should test this on a dummy folder with contents you do not care about before releasing it on your real data.


# $1 is the folder we are processing

old="$1"

if [[ $(mdfind -count -onlyin "${old}" '(kMDItemUserTags == "green")') > 0 ]] ; then
	# Something in this folder is tagged green, so add "DONE" if necessary.
	new="${old%DONE}DONE"
else
	# Nothing in this folder is tagged green, so remove "DONE" (assume Done only appears once!)
	new="${old%DONE}"
fi

# Rename if there has been a name change
if [[ "${old}" != "${new}" ]] ; then
	mv "${old}" "${new}"
fi

Thanks. Something is not working. Didn’t have time to debug. Don’t worry, I will find time to do it…

Pointing towards kMDItemUserTags was a huge help.

I used it to create a txt file with directories to rename with:
mdfind -onlyin “/PATH” kMDItemUserTags=“Green” | sed ‘s:[^/]$::’ | sed 's:/$::’ > ~/Desktop/rename.txt

And renamed them with:
IFS=$’\n’ ;for i in $(cat ~/Desktop/rename.txt); do mv $i $i"_DONE"; done