Count directory depth

The last time I needed this kind of code I used a text item delimiter routine to count the slashes in a posix path, but this method is rather inelegant, isn’t there a simpler way to find out how deep a path is?

Let’s say I’ve this path:
“/users/test/downloads/folder-1/folder-2/folder-3/folder-4/”

How to find out 7?

Why do you consider it inelegant. It works perfectly fine and is how I would do it

set myPath to "/users/test/downloads/folder-1/folder-2/folder-3/folder-4/"
set tid to text item delimiters -- save current delimiters
set text item delimiters to "/"
set myPath to rest of text items of myPath
set c to count myPath
if item -1 of myPath = "" then set c to c - 1
set text item delimiters to tid -- reset delimters

Well in the past I didn’t care about code methods in use, the only important thing was that code worked.

But this approach in general is a bit cumbersome because you’ve to write more code. Trying to write better code is not just more interesting, but you finish faster and your code gets slimmer.
Therefore my question.

I don’t know of any other way to get the result.

Here is another option.

set aFolder to "/users/test/downloads/folder-1/folder-2/folder-3/folder-4/"

set folderDepth to (do shell script "echo " & quoted form of ¬
	aFolder & " |tr '/' '\n' |sed '/^$/d' |wc -l") as number
1 Like

Using “do shell script” has a lot of overhead. Ran both thru Script Geek.
pure AppleScript version was 170 times faster

I was curious and tested the three suggestions on my 2023 Mac mini. I used Script Geek set to obsessive mode with 0 iterations (the tested code is run once).

robertfern - returned 0.0000 which means the timing result was less than a half of a tenth of a millisecond

wch1zpink - 6 milliseconds

Fredrik71 - 0.3 milliseconds (with Foundation framework in memory)

Just as a matter of personal preference, I would normally use robertfern’s suggestion. If text item delimiters are not an option for some reason, a repeat loop can be used and it takes a tenth of a millisecond.

set thePath to "/users/test/downloads/folder-1/folder-2/folder-3/folder-4/"
set theCharacters to characters of thePath
set theCount to 0
repeat with aCharacter in theCharacters
	if contents of aCharacter is "/" then set theCount to theCount + 1
end repeat
if (item -1 of theCharacters) is "/" then set theCount to theCount - 1
return theCount --> 7
1 Like

Fredrik71. Your script appears to return an incorrect result in the following instance:

set thePath to "/users/test/downloads/folder-1/folder-2/folder-3/folder-4" -- returns 6 but should be 7

I think that if the path doesn’t end with a “/”, the script must explicitly check if the last item is a folder (and not a file). Folder paths may not always end with a “/”. (If I understand the goal of this script correctly.)

Here is a complete solution which allows you to choose any folder to return it’s folder depth.

Actually, since the choose folder command will always return a path with the ending “/”, there is no need for any code to check what the last character is. There’s also no need to check whether the last item in the path is a file or not, because we have chosen a folder.

activate
set aFolder to POSIX path of (choose folder)

set folderDepth to ((do shell script "echo " & quoted form of ¬
	aFolder & " |tr -dc '/' |wc -c") as number) - 1

activate
display dialog ("Chosen Folder Depth: " & folderDepth) as text ¬
	buttons {"Cancel", "OK"} default button "OK" giving up after 3

I understand your point. However, it just doesn’t cover all possible use cases. It requires a user interaction to choose a folder. In reality, as you know, this folder path can come from all kind of sources, not necessarily the choose folder command.

FWIW, System Events does not include a trailing forward slash:

set theFolder to choose folder
tell application "System Events" to set theFiles to POSIX path of every folder in theFolder --> {"/Users/Robert/Desktop/New Folder 2", "/Users/Robert/Desktop/New Folder 1"}
1 Like

Sure, but while I don’t want to split hairs here, unless you verify that that last component without a trailing “/” is a folder you won’t be fully covered. As @peavine’s example with System Events demonstrates. In real-life scripts that path can come from an infinite number of sources and methods that may or may not include a trailing forward slash. I just ran into this very issue too many times to know that we can’t rely on that trailing slash to be there when expected.

well i think that’s exactly my point.

This following solution will check that the path is actually a directory if it does not end with a “/”.

set thePath to "/Users/test/downloads/folder-1/folder-2/folder-3/folder-4/"

if last character of thePath = "/" then
	set folderDepth to ((do shell script "echo " & quoted form of ¬
		thePath & " |tr -dc '/' |wc -c") as number) - 1
else
	set folderDepth to ((do shell script "if [[ -d " & quoted form of thePath & " ]] ;then echo " & ¬
		quoted form of thePath & " |tr -dc '/' |wc -c" & " ;fi") as number)
end if

if folderDepth > 0 then
	activate
	display dialog ("Chosen Folder Depth: " & folderDepth) as text ¬
		buttons {"Cancel", "OK"} default button "OK" giving up after 3
else
	activate
	display dialog "The Chosen Path Isn't A Folder!" buttons ¬
		{"Cancel", "OK"} default button "OK" giving up after 3
end if

wch1zpink. I tested your script as written, except that I deleted the forward slash in line 1 after folder-4. The script shows a dialog stating “The Chosen Path Isn’t A Folder”. Is that correct or am I missing something?

EDIT: I think I understand now. If the path ends with a forward slash, the script assumes that the path is to a folder. If the path does not end with a forward slash, the script checks to see if the folder exists and, if not, returns an error.

Since shell commands are on the table, why not use stat -F, which should indicate definitively whether a file is a directory or not. You might require more options should the files being tested include symbolic links.

1 Like

When I recreated the original path in post #1, “/users/test/downloads/folder-1/folder-2/folder-3/folder-4/” and then ran every script here, almost all of them gave me the correct result, which was 7.

If I changed the path to: “/users/documents/”, the only script that would come up with the correct result, which was 66 (I counted them) was peavine’s in post #11.

1 Like

These two work equally well for both Files and Folders.
Files:

tell application "Finder" to display dialog ¬
	("Number of Files (including subfolders) is " & ¬
		(count (files of (entire contents of (choose folder)))) as string) & ¬
	"." buttons "OK" default button "OK"

Folders:

tell application "Finder" to display dialog ¬
	("Number of Folders (including subfolders) is " & ¬
		(count (folders of (entire contents of (choose folder)))) as string) & ¬
	"." buttons "OK" default button "OK"

Correction: Can’t take credit though, found them here in Post #9 of a 2007 post on MacScripter: Count contents of folders, subfolders - #9 by Schmye_Bubbula

1 Like

I think you’ve posted this in the wrong thread, as those scripts pertain to counting the number of files or folders in a directory subtree. This thread is about determining the distance from a given directory back up to the root directory. Thus /users/documents, apart from being a very strange place for a documents folder to reside (or a very strange name to give to a user), is two levels down from the root directory, and not 66.