Converting Text to Records

If I run this script (as a quick example), it returns some of the system attributes for my account as quoted text. How do I get that as a record: {HOME:blahblah, SHELL:/bin/tcsh, …, QDTEXT_MINSIZE:10, …} so I can refer to them by name (MyAttribute’s USER) rather than by position in a list? Just appending as record to the action in the repeat doesn’t work.

set theAttributes to {"HOME", "SHELL", "USER", "PATH", "QDTEXT_MINSIZE", "QDTEXT_ANTIALIASING"}
set myMach to {}
repeat with aTerm in theAttributes
	set myMach to myMach & (aTerm & ":" & (system attribute (aTerm as text)))
end repeat

This is an old trick to coerce a string to a record property:

set theAttributes to {"HOME", "SHELL", "USER", "PATH", "QDTEXT_MINSIZE", "QDTEXT_ANTIALIASING"}
set myMach to {}
repeat with aTerm in theAttributes
	set myMach to myMach & (run script "{" & aTerm & ":\"" & (system attribute (aTerm as Unicode text)) & "\"}")
end repeat

Jon

Hi,

Use ‘run script’. Something like this:

set theAttributes to {“HOME”, “SHELL”, “USER”, “PATH”, “QDTEXT_MINSIZE”, “QDTEXT_ANTIALIASING”}
set myMach to “”
repeat with aTerm in theAttributes
set myMach to myMach & (aTerm & “:” & “"” & (system attribute aTerm) & “"” & “,”)
end repeat
set myMach to “{” & ((text items 1 thru -2 of myMach) as string) & “}”
set the_record to (run script myMach)
return user of the_record

I haven’t figured out how to capitalize the record labels. Maybe vertical bars might do it.

gl,

Since you know what the record’s property names will be in advance, the simplest thing is just to hard-code them:

set theAttributes to {"HOME", "SHELL", "USER", "PATH", "QDTEXT_MINSIZE", "QDTEXT_ANTIALIASING"}
set myMach to {}
repeat with aTerm in theAttributes
	set myMach to myMach & (aTerm & ":" & (system attribute (aTerm as text)))
end repeat
set {a, b, c, d, e, f} to myMach -- temp variables
set rec to {HOME:a, SHELL:b, USER:c, |PATH|:d, QDTEXT_MINSIZE:e, QDTEXT_ANTIALIASING:f}

Though personally I’d probably just leave everything in the list and define a bunch of constants so I can refer to its contents without strewing ‘magic’ numbers all over the code:

property HOME : 1
property SHELL : 2
property USER : 3
[...]

get item HOME of myMach

That particular code is very unsafe. If you have to do it that way (e.g. when dealing with completely arbitrary property names), AppleMods’ Record library provides a more reliable version, though you should still bear in mind it’s a very hackish solution. Alternatively, LNS’s List & Record Tools osax provides a [hopefully!] more robust solution. In situations where the record’s properties are all known in advance though, the simplest solution is just to hard-code them.

HTH

I don’t understand the “unsafe”. In what way? Clearly all of these scripts work; is it that at some point they’re likely to stop working, or because mistakes are a disaster?

I don’t know that I would use the word “unsafe”, and in the context of your question, the code examples above are both perfectly fine. However, if you were to use this code in a different situation, you may run into issues. Notably, if your property names are truly unknown (e.g., coming from user input or some random event) then the names may not be suitable for property names (they include spaces or other invalid characters and may not have escaped characters, etc.). In these instances, you would need a little more robust script to clean the property name and, perhaps, to enclose it in pipes (e.g., |property name|).

Jon

There’s also a method that used to be popular on the AppleScript-Users mailing list, though I haven’t seen it mentioned for a while:

set theAttributes to {"HOME", "SHELL", "USER", "PATH", "QDTEXT_MINSIZE", "QDTEXT_ANTIALIASING"}
set myMach to {}

repeat with aTerm in theAttributes
	set end of myMach to aTerm
	set end of myMach to (system attribute (aTerm as Unicode text))
end repeat

script o
	property fred : missing value
	{«class usrf»:fred}
end script

set o's fred to myMach
set myMach to (run script o)

This is about two or three times as fast as Jon’s script for the current purpose (because there’s only one ‘run script’.) But whereas Jon’s automatically converts the strings in theAttributes to property labels that are displayed in the lower case (the default state unless bars are used), «class usrf» retains the case of the originals and adds bars if they’re not lower case throughout. Thus:

With this method, if you don’t want labels with bars, present the “attribute” strings in the lower case. It’s considered bad form, though, to use reserved words as record labels.

As I see hhas has noted, if you already know what the labels will be, there’s no need for these workrounds.

They stop working as soon as a name string doesn’t convert into a valid AppleScript identifier (e.g. “123”, “foo bar”), or the value string contains a character that isn’t legal within an AppleScript literal (an unescaped backslash or double quote, or a non-MacRoman character). At worst, it could allow the unexpected - even malicious - execution of code, e.g.:

set key_ to "a:(display dialog \"hello\"), b"
set value_ to "\", c:(display dialog \"world\"), d:\""

run script "{" & key_ & ":\"" & value_ & "\"}"

There’s more to determining if code “works” than just feeding in some nice, safe, predictable data and getting exactly the result you expect: you also have to figure out - and test for - all the things that could possibly go wrong as well. e.g. What happens if a user provides bad input? What happens if a required file or folder is missing? What are all the things that could possibly go pear-shaped during the code’s execution, and what (if anything) needs to be done to ensure they won’t cause a problem?

HTH

I get the message about safety; thanks. Like: set key_ to “a:(do shell script "rm some_important_file"), b”.

Although it is said that Jonn8’s run method delivers lower case, when I run it it doesn’t. Finally though, the method with pipes added doesn’t work, i.e. get |HOME| of myMach causes an error.

I guess I don’t understand how this is any less safe than anything else that can be done with AppleScript (or any language, really). What’s the difference between:

do shell script "rm path/to/file"

and

set key_ to "a:"
set value_ to "do shell script \"rm path/to/file\""
run script "{" & key_ & ":\"" & value_ & "\"}"

? If the code is malicious, it’s malicious. If you are running code from an unknown source, you should be cautious no matter what and I don’t see how this method is any less safe than anything else. Again, as I mentioned above, and also discussed by hhas and NG, you can run into unexpected results if your property names are not valid, but, again, you can work around this and I would say that is more inconvenient than unsafe. What am I missing here? That said, some of the alternatives provided do have benefits in terms of speed and case sensitivity compared to the original method I posted.

Jon

My take (for what it’s worth in this company) is that to use the “run” script method is potentially dangerous IF (and it seems to me only if) the value can be set from a user dialog or some other form of input. If I’m using the construct in one of my own AppleScripts, it’s as safe as my machine is.

In the first case you’re dealing with malicious code. In the second you’re dealing with malicious data. It’s a lot easier for other parties to introduce incompatible/flawed/malicious data than incompatible/flawed/malicious code.

In practice, I doubt anyone would bother to attack this particular code (exploiting buffer overflows and autoexec-ing attachments is much more productive); I simply mentioned it because it’s the very worst-case scenario, which is always where you should start. It’s much more likely that your script would be killed accidentally, for example, by input data containing a double-quote character that causes the ‘run script’ command to throw a syntax error when it tries to compile the now malformed code. And since the OP is obtaining all their values externally, your code has no control over what those values are; which means the risk of unprotected code going kablooey is high.

Good programming is all about anticipation. FWIW, there’s nothing wrong with using unsafe code in situations you know will be 100% safe, but even then you should have a very large comment stating THIS CODE IS UNSAFE and the reasons why. But in any other situation you should not take the risk, because sooner or later it will come back and bite you; Murphy’s Law guarantees it. :slight_smile:

HTH

Hi everybody,

I don’t think this is about safe code. When posting code, it is implied that the user should add error checking. Personally, I’d take a week or four to explain all the possible situations that may arise.

IMHO, I’ve always thought that creating records labels from strings isn’t worth it. You need to know the labels to get the values from the created records. Have you tried getting the record values by converting strings to labels. I don’t think it can be done. Hence, I agree that you should either start with a record or just work with lists.

I had something else to say, but it’s Friday! :slight_smile:

Have a good day,

Just for the record (excuse me) I am not creating records from music labels, I was interested in the issue because I didn’t really understand records.

Actually, I think Zumwalt’s corollary to Murphy’s Law applies:

It’s not an error checking issue; it’s a bug issue. There’s a difference between leaving folk to figure out sensible stuff like not passing non-numeric values to a math function, say, and giving them literal-quoting code that blows up in normal use because its design is broken.

It can be done: AppleMods’ Record library does it the hacky vanilla way; LNS’s List & Record Tools does it the non-hacky way. There are occasions when you’ve no alternative to doing this; for example, when dealing with LNS’s XMLTools osax which has the annoying habit of storing tags’ attributes as records rather than key-value lists.

OTOH, if you mean that records are a bad choice for storing values by arbitrary keys that can be added and removed at runtime, you’re dead right. Records are intended for representing complex data with a fixed structure that’s defined at compile-time. What folk actually want is an associative list/hash/dictionary type, but unlike most languages AppleScript doesn’t provide a built-in dictionary type so they sometimes (wrongly) end up abusing records for the same purpose.

There’s no ideal solution to this as all the third-party alternatives come with caveats or limitations attached, though available options to consider include AppleMods’ Types library, which includes both AssociativeList and Dictionary objects, or Satimage’s XMLLib, whose PList suite allows basic values to be stored and retrieved by key.

HTH