Write to the beginning of a text file :)

Hello all,

The following script will add/write text to the end of a text file. However, I need a script that will add/write to the beginning of a text file. I realize I could read the data to a variable and then add to the beginning of this variable and then write back to the file but I am trying to avoid this solution because some of the text files that I am using are very large.

Any information would be greatly appreciated,

CarbonQuark


set fileRef to open for access alias "Macintosh HD:test.txt" with write permission
write "Hello World" to fileRef starting at EOF
close access fileRef

I don’t think there’s any way to push down the content of a file so something can be prepended. I’m afraid you’re stuck with reading, adding the piece in front, and writing it back or creating a new file with your prepended stuff and writing the old file to the end of it.

Adam,

Thanks for the info! That was what I was afraid of. Some of the text files I have are bigger than 20 megs and crash the script. I was hoping this could be accomplished with applescript / shell scripting.

Thanks again,
CarbonQuark

You might try this to deal with prepending to large files:

property blockSize : 4096

on run
	local origFile
	set origFile to (choose file default location path to temporary items)
	
	prependData to origFile from ¬
		"This is the new start of the file! (new blank line for free, too)" & (ASCII character 10) & (ASCII character 10) ¬
		without deletingOldFile
end run

to prependData to origFile from theNewData given deletingOldFile:deletingOldFile
	local origFileName, oldFileName, newFilePath
	tell application "Finder"
		-- This could be better about choosing file names.
		local folderPath, newFileName, oldFilePath
		set origFileName to name of origFile
		set folderPath to container of origFile as string
		set newFileName to "new " & origFileName
		set oldFileName to "old " & origFileName
		set newFilePath to folderPath & newFileName
		set oldFilePath to folderPath & oldFileName
		if exists newFilePath then error "new file already exists: " & newFilePath
		if not deletingOldFile and ¬
			(exists oldFilePath) then error "old file already exists: " & oldFilePath
	end tell
	local writeFile, readFile
	try
		set writeFile to open for access newFilePath with write permission
		set readFile to open for access origFile without write permission
		set eof writeFile to 0
		write theNewData to writeFile
		local gotReadError, theData
		set gotReadError to false
		repeat until gotReadError
			try
				set theData to (read readFile for blockSize as data)
			on error
				set gotReadError to true
			end try
			if not gotReadError then write theData to writeFile
		end repeat
		close access writeFile
		set writeFile to missing value
		close access readFile
		set readFile to missing value
	on error m
		if writeFile is not missing value then close access writeFile
		if readFile is not missing value then close access readFile
		display dialog m
	end try
	tell application "Finder"
		if deletingOldFile then
			delete origFile
		else
			-- This may fail if a file with this name snuck in while we were copying data.
			set name of origFile to oldFileName
		end if
		set name of item newFilePath to origFileName
	end tell
end prependData

I only tested it with small files (and even smaller blocks, to make sure the looping works).
You probably want to make up your own old file handling and naming stuff. And, as always, be sure you have backups of your files before you attempt any potentially dangerous operations on them (like rewriting them from scratch!). If it turns out to be too slow on your 20MB files, try a larger block size.

Model: iBook G4 933
AppleScript: 1.10.7
Browser: Safari 419.3
Operating System: Mac OS X (10.4)

Edit history: deleted a stale comment and an old log statement from the script text, rearranged local variable delarations, rewrote with label parameter in handler definition to use given (per Nigel Garvey)

Hi, chrys.

You should only use ‘with’ or ‘without’ in the call to the handler. The correct syntax for the first line in the handler statement itself (since you’re also using ‘deletingOldFile’ within the handler) is:

to prependData to origFile from theNewData given deletingOldFile:deletingOldFile

Otherwise, the script appears to work very well. :slight_smile:

Just as a proof of concept, I couldn’t resist doing a version that actually writes the new data to the beginning of the original file, moving the remaining data upfile with a rolling read/write alternation that uses two concurrently open accesses to it. I don’t recommend doing it this way though (except with a duplicate file) as any errors during the process will leave the file in a partially overwritten state!

property blockSize : 4096 -- Must be more than the length of the prepended data.

on run
	local origFile
	set origFile to (choose file)
	
	prependData to origFile from ¬
		"This is the new start of the file! (new blank line for free, too)" & (ASCII character 10) & (ASCII character 10)
end run

to prependData to origFile from theNewData
	-- Open two accesses to the file, one with write permission.
	set readRef to (open for access origFile)
	try
		set writeRef to (open for access origFile with write permission)
		try
			set origLength to (get eof readRef)
			set loopCount to origLength div blockSize
			set theOddBits to origLength mod blockSize
			
			-- The first data to be written will be the new stuff.
			set writeBlock to theNewData
			repeat loopCount times
				-- Read a block's worth of data from the file.
				set readBlock to (read readRef for blockSize as data)
				-- Partially overwrite it in the file with the stuff to be written.
				write writeBlock to writeRef
				-- Prepare to write the just-read stuff after the next read.
				set writeBlock to readBlock
			end repeat
			
			-- Read the remaining, less-than-blockSize data, if any.
			if (theOddBits > 0) then
				set readBlock to (read readRef for theOddBits as data)
				write writeBlock to writeRef
				set writeBlock to readBlock
			end if
			-- Write whatever's left to the file.
			write writeBlock to writeRef
		on error msg
			display dialog msg buttons {"OK"} default button 1 with icon stop
		end try
		close access writeRef
	on error msg
		display dialog msg buttons {"OK"} default button 1 with icon stop
	end try
	close access readRef
end prependData

Thanks chrys & Nigel,

I will give the solution a try.

Thanks:)
CarbonQuark

I’m not sure about this but maybe unix commands can do it without causing errors for large files. Here’s a basic workflow that might work.

  1. create a new text file with the data you want prepended
    do shell script “echo " & data_to_be_prepended & " > new.txt”
  2. use the “cat” unix command to append the “original” text file to the end of the new text file
    do shell script “cat original.txt >> new.txt”
  3. delete the original text file
  4. rename the new text file to the original text file name