Howto open a file and read one line after another.

Hi,

In Short:
How can I open a text-file and read one line after another (and put into a applescript-variable?)

The story behind:
I’m currently writing my first applescript to grab a copy of all my wordpress files and make a MYSQL-dump, download it via FTP, create a local copy of the blog and zip everything for backups.

I want that all settings for this are saved in a file (bb_settings.txt) and then loaded into applescript.

My solution:

bb_settings.txt contains:
SETTING1 SETTING2 SETTING3 …

(as you see, the settings are separated by spaces)

part of my applescript:


set settings_file to ((path to "docs" as text) & "bb_settings.txt") 
set file_content to open for access file settings_file with write permission 
set variable1 to read file_content until " "
set variable2 to read file_content until " "
set variable3 to read file_content -- for the last setting
close access file_content

It works, but I want to have the settings in the bb_settings.txt file separated by lines, ex:
SETTING1
SETTING2


set variable2 to read file_content until return

doesn’t work.

Any ideas? I checked the FAQ, Google but didn’t come up with an easy solution.

Regards from Berlin / Germany

Phil

Model: iBook
AppleScript: SkriptEditor Version 2.1 (80); AppleScript 1.10
Browser: Safari 412.2
Operating System: Mac OS X (10.4)

If there is three settings, you can use this:

set {x,y,z} to paragraphs of (read file ((path to "docs" as text) & "bb_settings.txt"))

Using this formula (getting “paragraphs”), it considers both return and ASCII 10.

Great code.

Now I can have my settings in a external text file (I call it bb_settings.txt) and load them into AppleScript with


-- Change Path to where you've put your bb_settings2.txt file
property bb_settings_file : "/Users/phra/Desktop/buwp/bb_settings2.txt"

-- *** DO NOT CHANGE THESE SETTINGS HERE, BUT CHANGE Settings in bb_settings.txt ***
property ftp_password : "" -- Password to FTP-Server
property mysql_password : "" -- Password to MySQL-Server
property mysql_user : "" -- MySQL-User
property mysql_server : "" -- MySQL-Server
property mysql_port : "" --  MySQL-Port
property mysql_database : "" -- Name of MySQL-Database to use
property ftp_user : "" -- Login for FTP-Server
property ftp_server : "" -- FTP-Server
property ftp_directory : "" -- Directory to your Blog
property local_destination : "" -- Local Destination
property local_folder_name : "" -- Foldername in Local Destination

get_settings()
-- Here follows the rest of the apple script

-- Open bb_settings_file and load configuration
on get_settings()
	-- Load settings, bb_settings_file is read as POSIX (converts path)
	set {mysql_server, mysql_port, mysql_user, mysql_database, local_destination, local_folder_name, ftp_server, ftp_user, ftp_directory} to paragraphs of (read file POSIX file bb_settings_file)
end get_settings

My bb_settings.txt looks has now one setting per line, like:

SETTING1
SETTING2

Even better would it be to have something like:

Explanation1 SETTING1
Explanation2 SETTING2

So that only every second word per line is read by AppleScript.
Ideas? Isn’t that important but would definly easier to maintain the external settings file, when there’s some explanation in it.

regards

Phil

Model: iBook G3 800
AppleScript: SkriptEditor Version 2.1 (80); AppleScript 1.10
Browser: Safari 412.2
Operating System: Mac OS X (10.4)

For that Phil, you’d probably need to revert to a repeat loop. After modifying your bb_settings.txt file as required, try replacing:

set {mysql_server, mysql_port, mysql_user, mysql_database, local_destination, local_folder_name, ftp_server, ftp_user, ftp_directory} to paragraphs of (read file POSIX file bb_settings_file)

…with:

tell paragraphs of (read POSIX file bb_settings_file)
	repeat with n from 1 to count
		set item n to item n's second word
	end repeat
	set {mysql_server, mysql_port, mysql_user, mysql_database, local_destination, local_folder_name, ftp_server, ftp_user, ftp_directory} to it
end tell

However, since AppleScript’s definition of a word may throw up some surprises here, it might be safer to use something like:

tell paragraphs of (read POSIX file bb_settings_file)
	repeat with n from 1 to count
		set item n to item n's text from second word to end
	end repeat
	set {mysql_server, mysql_port, mysql_user, mysql_database, local_destination, local_folder_name, ftp_server, ftp_user, ftp_directory} to it
end tell

Kai, you’re the man,

Exactly what I was looking for :slight_smile:

Unfortunately I didn’t understand the code good enough to understand why and how this is working, maybe you can add a short comment to each line, yes I’m a newbie and want to understand what is happening here.

AppleScript is so amazing, because solutions to common problems are solved with very few lines.

I realized that there two limitations (not real problems) of your script:

The bb-settings.txt file can look like this:

Servername: www.domain.com
Database-Name: wordpress
MySQL_Login: root
Subdirectory: /path/to/subdirectory

The script now extracts:
www.domain.com” - this is ok.
“wordpress” - this is ok
“_Login: root” - MySQL_Login is treated like two words MySQL and _Login
"path/to/subdirectory - the first / is removed

So we can’t use _ but -, that’s not a problem.
And we need to correct path settings, with something like: set path to “/”&path

My apologies for using a few ‘syntactical contortions’ - which I’ll attempt to clarify in the following annotated (and slightly modified) version. For this, I’ve adopted a generally more common form of syntax that (I hope) might be slightly easier to follow:

property bb_settings_file : "/POSIX/path/to/bb-settings.txt" -- amend as appropriate

-- store current value of AppleScript's text item delimiters
set tid to text item delimiters

-- set text item delimiters to a space - so that we can find where the first space in each paragraph occurs
-- (I find this technique is usually faster than using 'offset')
set text item delimiters to space

-- the following composite statement aims to do several things:
-- first, read the target file (bb_settings_file)
-- next, create a list from the paragraphs of the target file's text
-- finally, attribute that list to a variable called 'bb_settings_list'
set bb_settings_list to paragraphs of (read POSIX file bb_settings_file)

-- iterate through each item of bb_settings_list, using an index number: 'n'
repeat with n from 1 to count bb_settings_list
	
	-- now get the text of each item, starting after the first space
	-- this operation effectively 'removes' the initial text (up to and including the first space)
	set textAfterFirstSpace to text from text item 2 to end of item n of bb_settings_list
	
	-- next, replace the current list item with the 'clean' text that we've just extracted
	-- (since this technique changes each list item 'in place', it avoids having to build a completely new list)
	set item n of bb_settings_list to textAfterFirstSpace
end repeat

-- attribute the items of the resulting list (with the initial 'explanatory words' now removed) to a list of variable labels
set {mysql_server, mysql_port, mysql_user, mysql_database, local_destination, local_folder_name, ftp_server, ftp_user, ftp_directory} to bb_settings_list

-- restore original value of AppleScript's text item delimiters
-- (in case the original text item delimiters might still be needed in a calling subroutine, for example)
set text item delimiters to tid

-- do something with the resulting variables
-- (here, they're simply returned to confirm their values)
{mysql_server, mysql_port, mysql_user, mysql_database, local_destination, local_folder_name, ftp_server, ftp_user, ftp_directory}

To clarify the original syntax further, I should perhaps try to explain something about tell blocks - which, since they don’t necessarily have to be aimed at an application, can also target various values or evaluations.

Consider, for example, a series of simple calculations. Instead of saying:

set origNum to 12
set modNum to 10
set resultList to {}
repeat with addNum from 1 to 3
	set resultList's end to origNum mod modNum + addNum
end repeat

resultList
--> {3, 4, 5}

…we might (on a good day) realise that part of each calculation (the ‘origNum mod modNum’ bit) is duplicated on every iteration of the repeat loop. So, for a more efficient routine, we could evaluate that calculation first, attribute the resulting value to another variable (‘modCalc’) - and then perform the individual additions:

set origNum to 12
set modNum to 10
set modCalc to origNum mod modNum
set resultList to {}
repeat with addNum from 1 to 3
	set resultList's end to modCalc + addNum
end repeat

resultList
--> {3, 4, 5}

Alternatively, we could (in certain situations) use a tell block to carry out the same initial evaluation - but skip the stage that attributes the result to a user-defined variable:

set origNum to 12
set modNum to 10
set resultList to {}
tell origNum mod modNum to repeat with addNum from 1 to 3
	set resultList's end to it + addNum
end repeat

resultList
--> {3, 4, 5}

The ‘it’ referred to above is an AppleScript defined variable that points to the default target of the tell statement. In this example, ‘it’ is the result of ‘origNum mod modNum’ (12 mod 10) - which evaluates to 2.

Apart from offering a (very) small speed bump, this kind of syntax can also help to reduce a bit of verbiage - especially since certain properties don’t always need an explicit reference to ‘it’. Compare this, for example:

set someSampleText to "If this text is too long, it will be cut in the middle."
if (count of someSampleText) > 32 then set someSampleText to text 1 thru 16 of someSampleText & "." & text -15 thru -1 of someSampleText

someSampleText
--> "If this text is . in the middle."

…with this:

set someSampleText to "If this text is too long, it will be cut in the middle."
tell someSampleText to if (count) > 32 then set someSampleText to text 1 thru 16 & "." & text -15 thru -1

someSampleText
--> "If this text is . in the middle."

There are potential pitfalls attached to this sort of approach, and it’s probably only worth using if you feel comfortable with it. However, I hope this helps to explain some of the syntax that I used earlier. If I were to revert to that scripting style, the annotated script shown above might look something like this:

property bb_settings_file : "/POSIX/path/to/bb-settings.txt" -- amend as appropriate

set tid to text item delimiters
set text item delimiters to space
tell paragraphs of (read POSIX file bb_settings_file)
	repeat with n from 1 to count
		set item n to item n's text from text item 2 to end
	end repeat
	set {mysql_server, mysql_port, mysql_user, mysql_database, local_destination, local_folder_name, ftp_server, ftp_user, ftp_directory} to it
end tell
set text item delimiters to tid
{mysql_server, mysql_port, mysql_user, mysql_database, local_destination, local_folder_name, ftp_server, ftp_user, ftp_directory}

It always helps to know exactly what data we’re dealing with - and these values certainly put a different complexion on the most appropriate method. I mentioned earlier that the above script examples had been modified from the earlier versions - and I hope that the revisions now address the issues you’ve raised.

My apologies for the length of this reply. If you still need further help or clarification on any of this, just shout. :slight_smile: