Last password change in macOS

Objective is to determine the last password change time for all the user accounts in macOS (Catalina).

  1. sudo dscl . -read Users/swastibhushandeb accountPolicyData

Output:

<dict>
    <key>creationTime</key>
    <real>1570887333.512722</real>
    <key>failedLoginCount</key>
    <integer>0</integer>
    <key>failedLoginTimestamp</key>
    <integer>0</integer>
    <key>passwordLastSetTime</key>
    <real>1570887334.500102</real>
</dict>
</plist>
  1. date -r can be used to convert the epoch time stamps to human readable format as:

date -r 1570887334

How can the above be wrapped in a bash script/one liner code?Thanks in advance.


date -r $(sudo dscl . -read /Users/username accountPolicyData |
  tail -n +2 |
  plutil -extract passwordLastSetTime xml1 -o - -- - |
  sed -n "s/<real>\([0-9]*\).*/\1/p")
  • date -r <seconds> takes some seconds.
  • $(…) runs a command and captures stdout.
    • sudo dscl . -read /Users/… accountPolicyData returns accountPolicyData as XML.
    • tail -n +2 drops the first line of the output of the previous command, because dscl prints some extra information before the plist starts.
    • plutil -extract passwordLastSetTime xml1 -o - -- -
      • -extract passwordLastSetTime xml1 extracts the value for the key passwordLastSetTime in the input, outputting as xml
      • -o - outputs to stdout
      • -- - reads from stdin
        • -- to stop parsing options
        • - for stdin
    • sed -n "s/<real>\([0-9]*\).*/\1/p") extracts just the integer from the output.
      • -n suppresses the default of printing every input line
      • s/a/b/p substitutes a for b, printing matching lines (overriding -n for individual lines)
        • <real> matches the start of the line containing the integer we want
        • \(…\) is a capturing group
          • [0-9]* matches digits any number of times
        • .* matches to the end of the line, thus removing content in the line we're not interested in
        • \1 is the first capturing group
    • | pipes stdout from the left command into stdin of the right command

You can put this command all on one line if you prefer.