I’ve seen amazing things done to extract data from most anything with command-line tools such as awk, sed and regex. Just like “there’s more than one way to skin a cat”, there’s more than one way to get a result.
During some recent scripting research I noticed in the man page for the command I was using an option that allowed me to convert the data to an easier to parse format. Although the output for this option was much longer than normal output, I was able to avoid devising a complex regex for getting the data I needed.
Enough babble! I present yet another way to extract information from a blob of data or “cat skinning technique #12″.
This command when run in the Terminal returned a load of information about my OS X user account.
dscl . read /Users/tempuser
I appended an attribute called “Comment” and I gave the attribute a value of “Temporary account.”
sudo dscl . append /Users/tempuser Comment "Temporary account."
I could read this attribute quickly using:
dscl . read /Users/tempuser Comment
The result was:
Comment: Temporary account.
I added a second and third comment by running the append command a couple more times:
Comment: Temporary account. Expires: July 31, 2013. Manager: Martin Moose.
Now, how could I go about getting the expiration date from the comment? This is where awk-, sed- and regex-loving scripters would begin piping the results into something like:
dscl . read /Users/tempuser Comment | sed -n '3p'
The problem with this command was it left a blank leading space (note how the values for the comment were slightly indented in the above result).
I could pipe this again into another sed command along with some complicated regex magic to remove the leading space, which actually gave me what I wanted:
dscl . read /Users/tempuser Comment | sed -n '3p' | sed -e 's/^[ \t]*//'
As an administrator needing to get the job done I would be happy with this solution. If I were to post that one-liner into a forum, though, I’d be ridiculed for using the same command multiple times or for piping more than once.
I learned a few years back to try to exhaust the options provided by a single command rather than snipping away at results using a centipede of short commands. After viewing the man page for dscl I found a useful option—it could output the result in plist format. That’s the same format for preference files. Administrators familiar with managing preferences are also familiar with command line tools like defaults and PlistBuddy.
I added the extra option:
dscl -plist . read /Users/tempuser Comment
Although it returned lengthier output I had structure to the information:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>dsAttrTypeStandard:Comment</key> <array> <string>Temporary account.</string> <string>Expires: July 31, 2013.</string> <string>Manager: Martin Moose.</string> </array> </dict> </plist>
Both the defaults and PlistBuddy command line tools only read plist files, which meant I needed to redirect this information into a file. The /private/tmp folder was a convenient place to store transient stuff:
dscl -plist . read /Users/tempuser Comment > /private/tmp/myfile.plist
All I needed to do was read the file. Because this plist file contained an array, PlistBuddy was much better suited to reading it than defaults. After a little trial and error I put a two-liner together:
dscl -plist . read /Users/tempuser Comment > /private/tmp/myfile.plist /usr/libexec/PlistBuddy -c "print :dsAttrTypeStandard\:Comment:1" /private/tmp/myfile.plist
In plain language the PlistBuddy command said: “Read the value for the key ‘dsAttrTypeStandard:Comment’ and return index 1 (indexes start at 0) from the file myfile.plist.” The result returned was:
Expires: July 31, 2013.