Alternative notification system. Quickest way to read/write to a file?

I happened to notice that Nigel’s script closes the access reference on error and that a similar script on the Mac Automation Scripting Guide site closes the file reference on error. Is one or the other preferred or do they do the same thing? Thanks.

https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/ReadandWriteFiles.html

You should use the file reference. If an error has been thrown, there’s every chance the reference number is meaningless.

Thanks Shane–that makes sense.

I would like to amend the above.

  1. The main reason that a try block is needed here is that the compiler throws an error,
    when you try to re-open access to a file that you forgot to close access earlier. (Or, the script crashed earlier before closing the access).

  2. The file alias fAlias in this “classic code” is passed to the subroutine. This means that this file alias cannot raise an error. If the file alias does not exist, then the error is already raised in the calling code.

  3. In case 1) this “classic code” does not write anything, so it is not so correct.

I suggest writing it like this:


on WriteTotextFile(fAlias, textToWrite)
	
	try
		set fRef to (open for access (fAlias) with write permission)
	on error -- access is already opened
		close access (fAlias)
		set fRef to (open for access (fAlias) with write permission)
	end try
	
	set eof fRef to 0
	write textToWrite to fRef -- file the_path
	close access fRef -- (file the_path)
	
end WriteTotextFile

Let me offer my “classic” suggestion:

on WriteTotextFile(fAlias, textToWrite)
	set fRef to (open for access (fAlias) with write permission)
	set eof fRef to 0
	write textToWrite to fRef
	close access fRef
end WriteTotextFile

No try blocks.

The problem with the approach using try blocks is that you’re guessing where the error is, and assuming the script needs closing. If the script left it open, that should be fixed, not fudged. And if something else was responsible, closing it on them is just passing the buck.

Sometimes try blocks will make sense, but the idea of them making a handler somehow bomb-proof is, I think, a case of misplaced confidence.

It would have to be a serious system error in that case rather than a coding error, possibly requiring a restart. Using a file reference closes an open access channel to the file. Using the reference number closes the channel that the script opened on this particular run.

Not necessarily. If the open command fails, fRef isn’t going to be a valid file handle.

In this case, the try block is quite appropriate, since there is no other way to determine if access is already open, except by going through the error. Or, it exists?

This is exactly the case when you need it.

I disagree. If an error occurs the file stays open and the next attempt to open it will cause an error.

In my opinion the most reliable syntax is

on WriteTotextFile(fAlias, textToWrite)
	try
		set fileDescriptor to open for access fAlias with write permission
		set eof fileDescriptor to 0
		write textToWrite to fileDescriptor
		close access fileDescriptor
	on error
		try
			close access fAlias
		end try
	end try
end WriteTotextFile

The try block in the error clause closes the file rather than the file descriptor. This handles the case if the file descriptor could not be created and closes the file properly if the error occurs in the eof or write lines

Outside applets and Script Debugger, yes. But it’s just producing code that doesn’t tell you when there’s been a problem. If you left a file open – or something else did – you should know about it, because it may have other serious consequences.

If these fail, surely you should know about it, rather than blithely continuing on as if nothing happened. You could, for example, end up with a script that behaves perfectly, but doesn’t write half of what you think it does.

The code is just a minimum skeleton.

Of course you should add proper error handling and even a meaningful return value

Maybe everybody would be satisfied with :

on WriteTotextFile(fAlias, textToWrite)
	try
		set fileDescriptor to open for access fAlias with write permission
		set eof fileDescriptor to 0
		write textToWrite to fileDescriptor
		close access fileDescriptor
	on error errMsg number errNbr
		try
			close access fAlias
		end try
		error "The script issued error #" & errNbr & ", " & errMsg & " It would be useful to identify the cause of the problem and kill it."
	end try
end WriteTotextFile

With it, the file will be closed and the user will be warned that something must be edited somewhere.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 23 décembre 2019 13:53:02

Posted before seeing late Stefan’s message :wink:

If the open command fails, it won’t be needed. :wink:

on WriteTotextFile(fAlias, textToWrite)
	set fileDescriptor to (open for access fAlias with write permission)
	try
		set eof fileDescriptor to 0
		write textToWrite to fileDescriptor -- as «class utf8»
		close access fileDescriptor
	on error errMsg number errNbr
		close access fileDescriptor
		error errMsg number errNbr
	end try
end WriteTotextFile

This is an informative thread.

All of the above scripts seem to assume that the file is opened, immediately written to, and then closed. I don’t know if these scripts are written this way just for discussion purposes or if it should actually be done that way?

In my case, I have a long script and the text file is written to on multiple occasions. That being the case, I’ve written the script as shown below. So far this has worked without issue but I’ve only been using it for a few days.

--Near the beginning of the script
set logHeading to "PDF SHRINK ERROR LOG" & linefeed & (current date) & linefeed & linefeed
set openedFile to open for access (POSIX file errorLog) with write permission
set eof of openedFile to 0
write logHeading to openedFile

--Midpoint in the script inside a repeat loop.
write errorText & linefeed & aPosixFile & linefeed & linefeed to openedFile

--Near the bottom of the script.
try
	close access openedFile
on error
	close access POSIX file errorLog
end try

BTW, at the end of the script, the user is offered the option to view the error log. So, I assume the error log has to be closed before that point and not in a handler when the script quits.

For such task I wouldn’t leave the file open all the time.
I would use this handler :

#=====
(*
Handler borrowed to Regulus6633 - http://macscripter.net/viewtopic.php?id=36861
*)
on writeto(targetFile, theData, dataType, apendData)
	-- targetFile is the path to the file you want to write
	-- theData is the data you want in the file.
	-- dataType is the data type of theData and it can be text, list, record etc.
	-- apendData is true to append theData to the end of the current contents of the file or false to overwrite it
	try
		set targetFile to targetFile as «class furl»
		set openFile to open for access targetFile with write permission
		if not apendData then set eof openFile to 0
		write theData to openFile starting at eof as dataType
		close access openFile
		return true
	on error
		try
			close access file targetFile
		end try
		return false
	end try
end writeto

#=====

The very first call would be:
my writeto(targetFile, theData, dataType, false)
so that the file will be created as an empty one.

For other calls the instruction would be
my writeto(targetFile, theData, dataType, true)

This way the file would be closed after every write task.

I use ‘as «class furl»’ because in my recent scripts there is quite always a ‘use Frameworks trucmuche’ instruction and in this case the write instruction dislike other class of file reference.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 23 décembre 2019 15:54:15

I naïve question:
Let’s assume we have two separate application:
Application A: “The_writer”
Application B: “The_reader”

If you are continuously writing to a file (let’s say: for1h) (using Application A) without “closing” the file, would it be possible for the other application (Application B) to read the file ?
I mean with all the updated info which are streamed to the file even before closing it (which will occur 1h later)?
In other words, when are the “bytes” really written into the file ? And if it is possible to read the updated versions of that file before been closed (by Application A)?

I never tested that but
As far as I know, as the file is ‘open for write’ / ‘closed’ after each call to the handler it may be triggered from several applications.

BUT only the first opened application may contain the instruction
my writeto(targetFile, theData, dataType, false)
which resets the eof to 0.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 23 décembre 2019 17:03:00

Thanks Yvan. I plugged that handler into my script and it works great.

It’s not being put forward as such, but rather as a sort of definitive handler.

That looks much nicer. No dangling open file, but no hiding errors.