I know how to convert following text to record using text item delimiters “:”, then the SPACE. But maybe someone know better and more effective way (using shell commands or AsObjC, or some other method).
The text is following (note that every paragraph has spaces at beginning. The site doesn’t show them, but there are here. I want eliminate them from key names, as well):
set currentWirelessNetworkInfo to " agrCtlRSSI: -29
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"
set theContent to "agrCtlRSSI: -29
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"
set theContent to do shell script "echo " & theContent's quoted form & " | tr -d ' ' "
set {keylist, textList, recordz} to {{}, {}, {}}
repeat with index from 1 to count theContent's paragraphs
set keylist's end to theContent's paragraph index's text 1 thru ((offset of ":" in my theContent's paragraph index) - 1)
set textList's end to (theContent's paragraph index's text ((offset of ":" in my theContent's paragraph index) + 1) thru -1)
set recordz to recordz & (run script "{|" & (my keylist's item index) & "|: " & (quote & my textList's item index & quote) & "}")
end repeat
recordz
–edited for brevity, list creation reduction, and potential speed improvements
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
set theContent to "agrCtlRSSI: -29
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"
set text item delimiters to {":"}
set theContent to paragraphs of theContent
set myRec to {}
repeat with i in theContent
set tmp to text items of i
set end of myRec to item 1 of tmp
set tmp to (rest of tmp) as text
try
set tmp to tmp as integer
end try
set end of myRec to tmp
end repeat
set rawRecord to {«class usrf»:myRec}
Hmmm! I seem to be having problems with the script. It works somewhat in ScriptDebugger.
it shows properly but doesn’t actually convert to record
script WifiStats
property parent : a reference to {«class usrf»:my list's paragraphs}
property key : a reference to AppleScript's text item delimiters
property item : a reference to my list's text items
property list : " agrCtlRSSI: -29
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6\n channel: 6"
end script
tell WifiStats
set key's contents to {linefeed, ": "}
set its list to its item as text
set key's contents to {{space},tab}
set its list to its item as text
return the contents as anything
end tell
Run Script (JXA)
set WifiStats to " agrCtlRSSI: -29
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6\n channel: 6"
run script "let R=`" & WifiStats & "`" & "
.replace(/[\\s\t]*(\\w[^:]+): (.+)\\x12?/g,
'\"$1\" : \"$2\" \\u{0A}'
).split( '\\u{0A}' ).slice( 0, -1 ).join( ',' );
JSON.parse( '{' + R + '}' );" in "JavaScript"
ASObjC
-- Format the string first into the following format,
-- e.g. using Text Item Delimiters:
set WifiStats to " \"agrCtlRSSI\" = \"-29\";
\"agrExtRSSI\" = \"0\";
\"agrCtlNoise\" = \"-87\";
\"agrExtNoise\" = \"0\";
\"state\" = \"running\";
\"op mode\" = \"station\" ;
\"lastTxRate\" = \"59\";
\"maxRate\" = \"72\";
\"lastAssocStatus\" = \"0\";
\"802.11 auth\" = \"open\";
\"link auth\" = \"wpa2-psk\";
\"BSSID\" = \"8:78:8:0:aa:5c\";
\"SSID\" = \"AndroidAP\";
\"MCS\" = \"6\";
\"channel\" = \"6\";"
-- Then the Foundation framework allows this:
use framework "Foundation"
my (NSString's stringWithString:WifiStats)'s propertyList() as record
Sorry, I’m currently too tired to do the labour of formatting the string in the last example, but it’s fairly straight forward.
I’m sorry that I disappeared temporarily from the radar. Now I can only say one thing:
Friends, I’m impressed!
Because I did not expect that 6 options for solving this problem would appear at once. (This is a very common task faced by users when getting results from utilities, so a good solution will come in handy).
As I tested, they all work, and quite quickly, but 2 of them give the result almost instantly. I will provide the results of speed testing + my full script obtained with your help.
13 ms : scripts from @Mark Anthony and from @Peavine
3 ms : JXA script from @CK
2 ms : script from @robertfern, but for some reason it doesn’t trim spaces before key names when working with result of do shell script
set currentWiFiNetworkInfo to do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I"
set {ATID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ": "}
set theContent to paragraphs of currentWiFiNetworkInfo
set myRec to {}
repeat with i in theContent
set tmp to text items of i
set spaceOffset to item 1 of tmp
set end of myRec to item 1 of tmp
set tmp to (rest of tmp) as text
try
set tmp to tmp as integer
end try
set end of myRec to tmp
end repeat
set AppleScript's text item delimiters to ATID
set rawRecord to {«class usrf»:myRec}
’
2 ms : plain AppleScript version from @CK. I see this solution as the best provided solution.
Here is full script (it gets current WiFi Network state info) using the excellent @CK suggestion :
on currentWiFiNetworkInfo()
script o
property parent : a reference to {«class usrf»:my list's paragraphs}
property key : a reference to AppleScript's text item delimiters
property item : a reference to my list's text items
property list : "" -- EDITED
end script
set currentWiFiNetworkInfo to ¬
do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I" -- ADDED
tell o
set its list to currentWiFiNetworkInfo -- ADDED
set key's contents to {linefeed, ": "}
set its list to its item as text
set key's contents to {[space]}
set its list to its item as text
return the contents as any
end tell
end currentWiFiNetworkInfo
my currentWiFiNetworkInfo()
No, after all, the JXA solution from @CK in this case is the best, since it preserves the necessary spaces in the compound key names, unlike the last script in the previous post:
on currentWiFiNetworkInfo()
set currentWiFiNetworkInfo to do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I"
run script "let R=`" & currentWiFiNetworkInfo & "`" & "
.replace(/\\s*(\\w[^:]+): (.+)\\x12?/g,
'\"$1\" : \"$2\" \\u{0A}'
).split( '\\u{0A}' ).slice( 0, -1 ).join( ',' );
JSON.parse( '{' + R + '}' );" in "JavaScript"
end currentWiFiNetworkInfo
my currentWiFiNetworkInfo()
You mean the fastest solution provided. You can only judge it to be the best if you yourself understand how it works and can judge whether or not the methods used are legitimate. The square brackets in {[space]} certainly aren’t part of AppleScript and replacing them with braces produces exactly the same end result. On the face of it, using reserved terms as property labels is also to be discouraged, but in this case they appear to integral to the method.
… but presumably it’s because the beginnings of the text lines are rendered above as tabs rather than spaces. I see you’ve already noticed that spaces within the labels are removed too.
I’ve modified my script with some script object hocus pocus. Let me know if it fares differently in your speed test, and perhaps I’ll change my default method.
Thanks for updating your version. As I checked, it is still 7 times slower than the JXA solution. It also uncomfortably removes spaces in compound key names.
I took the liberty of tweaking your script. Now it gives the correct result. Moreover, its execution in Script Geek is equal in speed with the JXA solution (3 ms). (Note: in the Script Debugger following script for some reason is still 3 times slower then the JXA solution, I don’t know why this difference with Script Geek testings).
It makes sense to bring it here for users who are weak in JXA, like me:
on currentWiFiNetworkInfo()
set currentWiFiNetworkInfo to do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I"
set recordz to {}
repeat with theParagraph in my (currentWiFiNetworkInfo's paragraphs)
set o to offset of ":" in theParagraph
set theKey to theParagraph's text 1 thru (o - 1)
repeat while theKey begins with space
set theKey to text 2 thru -1 of theKey
end repeat
set recordz to recordz & (run script "{|" & theKey & "|: " & (quote & (theParagraph's text (o + 2) thru -1) & quote) & "}")
end repeat
end currentWiFiNetworkInfo
my currentWiFiNetworkInfo()
I cleaned up a version from above to save fields that are integers as integers not text.
It also does REALS and BOOLEANS.
Also i only call run script once to cut down on processing time
set theContent to "agrCtlRSSI: -29
agrExtRSSI: true
agrCtlNoise: -87
agrExtNoise: 2.10
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"
set text item delimiters to {": "}
set theContent to (theContent's text items)
set text item delimiters to {":"}
set theContent to theContent as text
set theContent to paragraphs of the theContent
set {keylist, textList, recordz} to {{}, {}, {}}
repeat with index from 1 to count theContent
set anItem to theContent's item index
set keylist's end to anItem's text item 1
if (count (text items of anItem)) = 2 then
set textList's end to anItem's text item 2
else
set textList's end to (anItem's text items 2 thru -1) as text
end if
try -- see if field is an boolean
set textList's last item to textList's last item as boolean
set end of recordz to ("|" & my keylist's item index & "|: " & (textList's item index))
on error
try -- see if field is a REAL
if textList's last item contains "." then
set textList's last item to textList's last item as real
else
error
end if
set end of recordz to ("|" & my keylist's item index & "|: " & (textList's item index))
on error -- its text
try -- see if field is an integer
set textList's last item to textList's last item as integer
set end of recordz to ("|" & my keylist's item index & "|: " & (textList's item index))
on error -- its text
set end of recordz to ("|" & my keylist's item index & "|: \"" & my textList's item index & "\"")
end try
end try
end try
end repeat
set text item delimiters to {","}
set recordz to run script "{" & (recordz as text) & "}"
recordz
Yes, they are. I think the length of time for which you’ve been saying this sort of demonstrates their permanency, and they’ve most certainly outlived many apparently stable “core” features of AppleScript in their lifetime. So I think when it comes to defining what the term “legitimate” actually means, I’m taking the pragmatic view in this instance (and I must admit, pragmatism doesn’t always come easily to me), which is to acknowledge that these things have utility, they’re not going anywhere, and they let one do things that aren’t possible otherwise. I’ve previously given a run-down on their history in AppleScript, what capabilities they retain, and the pros and cons in various scenarios.
Yes, correct. They aren’t serving a functional purpose in this case, purely a typographical one, largely for my benefit to be able to distinguish neighbouring brackets from each other more easily. Anyone else who’s dyslexic might understand how confusing consecutive sets of symbols can be.
I actually normally switch them back out for curly ones when publishing, but my tiredness made me overlook it in this instance. That said, they pose no drawbacks here nor do any harm, and if anything, it’s opened up discussion for people to learn about it.
Oh, and those tab characters shouldn’t be there. I don’t know whether that’s something you inserted into the script or whether that was me again. Simply appending a tab item to the tids value in question will resolve their unwelcome appearance.
There’s no particular reason on the face of it, or on the back of it, or from any angle, to discourage their use. Once again, it’s about appreciating that they have utility, but also—as you rightly said—knowing and understanding how to use them in ways that maintain robustness and offer effectiveness. Come to think of it, that’s true of everything.
I would encourage you to try and start making a point to use them, even though it may be outside of your comfort zone. But getting familiar with something usually allows reservations that are less warranted to subside. You may even end up liking it.
Ah, is that where they’re coming from ? I don’t know whether the forum mutated the white space or whether I did it in a hypnogogic state.
try blocks will slow a script down quite significantly when an error is caught. Error catching is a fairly intense set of operations that get performed in order to catch the error, itemise the components of the script responsible for the error, reporting it, and logging it. Luckily, you can probably do away with all of that and simply do this:
tell the textList to set the last item to the last item as {boolean, real, integer, text}