Can't use a variable in a "POSIX file" reference in a "Finder" tell?

set mountPath to "/Volumes/newtees"

tell application "Finder"
	if exists (POSIX file "/Volumes/newtees") then log "yes to 1"
	if exists (POSIX file mountPath) then log "yes to 2"
end tell

set POSIXref to POSIX file mountPath

tell application "Finder"
	if exists POSIXref then log "yes to 3"
end tell

Result:

exists file “newtees” → true
(yes to 1)
exists POSIX file “/Volumes/newtees” → false
exists file “newtees” → true
(yes to 3)

However, if I move the “set POSIXref to POSIX file mountPath” inside a Finder tell, then it, too fails. With an error: “Finder got an error: Can’t get POSIX file “/Volumes/newtees”.”

Please explain, I found it very unintuitive to find that if I type out the text, it works, but if I assign the text to a variable and then use the variable, it doesn’t work. But only if done inside a Finder tell. Outside a Finder tell, “set POSIXref to POSIX file mountPath” works fine.

Thanks,

t.spoon.

Nigel provides a good explanation of this in:

https://macscripter.net/viewtopic.php?id=45607

The script that returns false can be fixed by adding “my”, which forces the script to evaluate POSIX file, which the Finder doesn’t understand.

set mountPath to "/Volumes/newtees"

tell application "Finder"
	if exists (POSIX file "/Volumes/newtees") then log "yes to 1" --returns true
	if exists my (POSIX file mountPath) then log "yes to 2" --returns true
end tell

Thanks! I had tried searching for an answer first, but I didn’t find the right terms. Great thread you linked too to explain it, although to me the result is still extremely unintuitive behavior on Applescript’s part.

Let me add that the best approach generally is your third one: make file references outside tell blocks. Doing it inside tell blocks is writing code that going to break if the target app ever gets sandboxed.

Yes, I had already modified my code by moving the line that makes the POSIX file outside the “tell” before I even saw the replies, and did read that entire other thread and see that doing so was preferable to using “my.”

Elsewhere, I’ve also used:

set fileExist to (do shell script “[ -f ” & quoted from of filePath & ” ] && echo true || echo false”) as boolean

Which I mainly use when I’m already dealing with paths that start with “~” because I prefer this:


set somePath to "~/downloads/someFile.txt"
set fileExists to (do shell script "[ -f " & somePath & " ] && echo true || echo false") as boolean

to this:


set somePath to "~/downloads/someFile.txt"
set {delimitHolder, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "/"}
set pathFromHome to text items 2 through end of somePath as text
set AppleScript's text item delimiters to delimitHolder
set theFile to POSIX file ((POSIX path of (path to home folder)) & pathFromHome)
tell application "Finder" to set fileExists to exists theFile

Note to anyone finding this - be careful with the former. While short, and supporting relative paths, it doesn’t work with spaces in the file name. I can’t just use “quoted form of somePath” because quoted paths don’t work with ~ for the user folder. I have a library function for find and replace, so I tend to actually call this with the “somePath” variable being a call to escape the spaces in “somePath.”

FWIW, System Events understands tilde abbreviation, at least in recent versions of the OS:

set somePath to "~/downloads/shane.zip"
tell application id "com.apple.systemevents" to exists item somePath

But more app dependencies mean more authorization dialogs, so I’d prefer:

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions

set somePath to "~/downloads/shane.zip"
set somePath to (current application's NSString's stringWithString:somePath)'s stringByExpandingTildeInPath()
current application's NSFileManager's defaultManager()'s fileExistsAtPath:somePath

Which is also about 30x faster than System Events, which is in turn much faster than the shell route. And even if one doesn’t know ASObjC, it’s relatively readable in terms of what it’s doing.

Thanks Shane. We have enough people running scripts frequently enough that the time differences here actually do matter. We have scripts that might check the existence of a file 6 times, that are run by 25 people each 40 times a day. So that’s ≈ 6,000 runs per day, where even if it’s only saving .06 seconds, that’s collectively saving people 6 minutes a day.

I just tested these, and on first run, my script run times in Script Debugger were:

ASObjC: 0.56 s
Shell: 0.07 s
System Events: 0.03 s
Finder: 0.01 s

I tried them all again, and found a lot of variation.

Run 2:
ASObjC: 0.00 s
Shell: 0.06 s
System Events: 0.73 s
Finder: 0.00 s

So the time for System Events and ASObjC are all over the place.

I ran System Events 10 more times:
0.04
0.03
0.03
0.02
0.02
0.02
0.02
0.02
0.02
0.02

And ran ASObjC 10 more times and it was 0.00 every time.

Does something have to be initialized with ASObjC? It was only the first run that was many times slower. I tried changing file paths for the test in case it was caching something, but runs with new paths were still 0.00

With System Events it wasn’t even the first run that was the slow one, so no idea what’s going on there.

I put them each inside a repeat 1000 times loop and got:
ASObjC: 0.19 s
Shell: 49.52 s
System Events: 22.16 s
Finder: 2.53 s

Based on these results, I think I’ll just stick with Finder. It was consistently super fast, and I’m familiar with it. Based on the results of the 1000x repeat, ASObjC is way faster - but by “way faster,” I mean it’s 0.00234 seconds faster. It would collectively save everybody 14 seconds per day, assuming we never get any repeats of my initial .56s run.

As you know from helping me in the past, I’ll certainly use ASObjC when it’s the only way to get the job done. But 4 people work on this code base, and none of us know ASObjC, so there’s a strong preference for avoiding it when it’s not necessary until at least a couple of us take the time to learn it. I suppose on the flip side, if we started using it whenever possible that would help us learn it… but that’s a scary approach to learning for code that’s in production.

Tests in editors can be wildly inaccurate. They include instrumentation code, and although SD tries to compensate for it, it simply can’t be done accurately. Use my Script Geek.app, or use an applet. Editors are especially inaccurate here, where some code sends events and some doesn’t. That said, at least some of your results look reasonable – the differences are big enough.

That’s partly the nature of AppleScript, but there’s also disk access and caching involved here. If you run a repeat loop checking if a file exists, caching means you’re mostly timing the overhead of whatever route you chose.

It does with everything – it’s just more apparent with ASObjC because it’s lazily loaded, only when actually first called. It also become a lot quicker in recent versions of the OS, where I suspect some pre-loading is happening. But yes, it has to load once per AppleScript component instance.

It is. But I would have thought the odd two-line snippet like this, even if tucked away in a library, might just be a good place to start.