File problems

When I run this script the first time, it creates the file, writes to it, seems to close it. Run it again and the “set thisFile to …” line triggers an “already open” error". Open the file in TextEdit and close it, trash it and empty trash: no matter what, AppleScript still says it’s open. It appears that I can never use that file path again in AppleScript.

What am I doing wrong here?


set filePath to [my file path]
set thisFile to (open for access file filePath with write permission)
write "foobar" to thisFile
close thisFile

Model: MacBook Pro
AppleScript: 2.5
Browser: Safari 602.4.8
Operating System: Mac OS X (10.10)

Mirroring “open for access,” the syntax is “close access”

set filePath to [my file path]
set thisFile to (open for access file filePath with write permission)
write "foobar" to thisFile
close access thisFile

Hey t.spoon!!

You are a gentleman or lady, and a scholar. Thank You.

But out of curiosity: I’ve been away from As for a while, last one I did was Yosemite, and this is the syntax I used. Has this file syntax been changed or made more rigid in Sierra?

Thanks again!

You’ll need comment from the experts on that one, I have no idea. But that’s the syntax I’ve been using for many years.

You did a perfect job t.spoon! The difference is closing the file. The last line of TS is invoking the close command which is a general command for objects that can be closed like windows and documents. The command t.spoon is using is close access which is part of the file handling commands of the StandardAddition.osax. When opening a file using open for access command, you need to close the file with the associate command; close access.

In short: what happens with LouK’s script is that he opens a file but never closes because he’s using the close command which is wrong (close access is correct). So when the script is ran a second time the system sees that the file is already open with write acces so it will return an error. That is because a file can only be opened once with write permission at the same time system wide (it can be opened with read access many times)

Thanks DJ, but what I was saying we need the experts for is knowing why simply using “close,” which LouK said used to work pre-Sierra, would have ever worked… and if it did, why it would have stopped in Sierra.

I just checked to be sure.
Here it doesn’t work with Yosemite.

Yvan KOENIG running Sierra 10.12.3 in French (VALLAURIS, France) vendredi 17 mars 2017 11:24:33

It’s possible that LouK never actually noticed it not working before. Failure to close a file access usually only comes to light when the access has write permission, the script fails to close the access when it’s finished, and the application running the script (to which the system has granted the access) doesn’t then quit itself. If a script applet, say, fails to close an access it’s opened, the access is released anyway when the applet quits. Script Editor doesn’t usually quit after running a script, so if the script doesn’t close a write permission access it’s opened, the access remains allocated to Script Editor and another can’t be opened with write permission to the same file. On the other hand, as DJ says, any number of accesses without write permission can be open at the same time, so you’d only notice they hadn’t been closed if the script happened to use the wrong one belonging to the same application.

That certainly sounds plausible to me - that his previously used syntax was never working, but it’s failure also wasn’t causing a problem either.

Thanks Nigel.

Or it reaches its limit of open files :). Unlikely these days, but I can remember seeing it happen in the old days, in cases where people regularly enclosed everything in Finder tell blocks and the Finder used up its quota.

And a blatant plug: if a script leaves files open when run in Script Debugger, you’ll get a dialog listing the open files, with a button that will let you close them.

I supposed there must be a limit, but I’ve no idea what it is. Probably a difficult one to reach. :slight_smile:

There seems to have been a change in recent years whereby file access channels are allocated to the applications running the scripts even when opened in tell statements directed elsewhere. A better idea, I think.

The current ASLG contains the intriguing statement: “If you open more than one channel at once with write permission, behavior is unspecified.”

256 per process to be exactly. It’s not that high on Mac OS X.

do shell script "ulimit -n"

UPDATE: As Shane mentioned, in Sierra it seems to be a lot higher and somehow ulimit command returns the wrong value in Sierra.

Yes, a result of – and possibly one of the reasons behind – the move to redirect scripting addition command calls to the host.

DJ,

When I run that in Script editor, it returns 2560.

When I run:

do shell script "sysctl -A | grep kern.maxfiles"

I get:

“kern.maxfiles: 12288
kern.maxfilesperproc: 10240”

I don’t think I’ve changed anything here, although I have a vague recollection of the limit being increased a few versions of the OS back.

And I just ran this in Script Editor:

set deskPath to path to desktop as text
repeat with i from 1 to 2500
	set thePath to deskPath & "Test folder:File-" & i
	open for access file thePath with write permission
end repeat

It worked fine.

Running 10.12.3 (16D32).

The “ulimit” script returns “256” in Script Editor on my machine (Mac OS 10.11.6), but the “sysctl” result’s the same as Shane’s.

Running latest version of El Capitan:

-- run it in a shell so SE doesn't freeze
set theScript to "set n to 0
repeat
	try
		open for access \"/etc/hosts\"
	on error
		return n
	end try
	set n to n + 1
end repeat"

do shell script "osascript -e " & quoted form of theScript

Mine stops at 232 (of course the process has already files open like stdin, stdout, stderr but also libraries using dlopen)

So there seems to be a difference between El Capitan and Sierra then. I’ve corrected my post above and thanks for the update Shane.

Update: To increase the max files open you can use ulimit on older systems.

set theScript to "set n to 0
repeat
	try
		open for access \"/etc/hosts\"
	on error
		return n
	end try
	set n to n + 1
end repeat"

do shell script "ulimit -n 1024
osascript -e " & quoted form of theScript

This returns 244 in Script Editor on my machine, with or without write permission. Modifying it to open the same file repeatedly, its still 244 without write permission and of course 1 with.

set deskPath to path to desktop as text
set aRefs to {}
repeat with i from 1 to 2500
	set thePath to deskPath & "Test folder:File-" & i
	try
		set end of aRefs to (open for access file thePath)
	on error
		exit repeat
	end try
end repeat

set refCount to (count aRefs)
repeat with i from 1 to refCount
	close access (item i of aRefs)
end repeat

return refCount

What “owns” the access channels in DJ’s script(s)?

When I run your script it will return 233, but that was not the point of my script. It was to show that the value returned by ulimit was correct and sysctl returns the wrong value in El Capitan. I noticed using lsof that osascript will load all SE application states so the difference between Nigel’s outcome and mine is all that extra application cache that is stored on my machine and opened.

Not sure what you mean by “owns” but when an file is openend the file will be opened by the kernel and an id is returned. That id is used as a reference to read and write data to file descriptors (which can be a file, pipe or socket). The kernel is the big owner of everything which maintains a file table for every process. From what I read from the documentation is the the shell has it’s own resource limits which can be managed using the built-in ulimit command (use ‘ulimit -a’ to see all settings). While some of the resources are limited doesn’t mean it is actually “owned” by the shell itself. Since everything runs in a shell it all applies the same rules unless the value is changed for the specific shell and all its processes.

Of course that is how it works on El Capitan. Why Shane has no problem using his script is still mysterious to me. I’m curious if all resource settings are ignored because ulimit is a great tool

Thanks, DJ.

I meant “owns” in the sense of “to what process are the access channels allocated?” In the Script Editor scripts, it’s the application Script Editor. I was wondering what it would be in a shell script. I think your reply covers that.

It’s always the process that has the standard addition dylib loaded who makes the system call. You’re right that the SE process will do it for scripts run in SE app and it will be the osascript process that opens file for the script in a shell. If the script is loaded by an 3rd application (read: current application is the 3rd party app) the standard addition osax will be “injected” into the 3rd party process and the system call will be made from there.

Since 10.6 it is no longer possible to inject an osax into every process but in the old days the “access channels” depended on the application context of your script. Quite an improvement in security for osaxen has been made (including the changes in 10.5 and 10.5.8).