I have this output on the command line:
% pmset -g custom
Battery Power:
lidwake 1
lowpowermode 1
standbydelayhigh 86400
standbydelaylow 10800
standby 1
proximitywake 0
ttyskeepawake 1
hibernatemode 3
powernap 0
gpuswitch 2
hibernatefile /var/vm/sleepimage
highstandbythreshold 50
displaysleep 2
womp 0
networkoversleep 0
sleep 1
lessbright 1
halfdim 1
tcpkeepalive 1
acwake 0
disksleep 10
AC Power:
lidwake 1
lowpowermode 0
standbydelayhigh 86400
standbydelaylow 0
standby 1
proximitywake 1
ttyskeepawake 1
hibernatemode 3
powernap 0
gpuswitch 2
hibernatefile /var/vm/sleepimage
highstandbythreshold 50
displaysleep 5
womp 1
networkoversleep 0
sleep 1
tcpkeepalive 1
halfdim 1
acwake 0
disksleep 10
As you can see there are two copies of most entries, the only difference being the “headings” (Battery Power vs AC Power). The order of both the entries and the headings is not guaranteed. I want a robust way to find the value for displaysleep. That is, I want to specify Battery Power or AC Power and then get the displaysleep value for that section. Is there a better way than to “not break at linefeed” “non-greedy match”? Something like a hierarchical notation (pseudo code) “get AC ‘Power.displaysleep'” à la XML, Java objects etc?
7
If you don’t mind combining awk
with column
, then it’s really straight forward without needing to store any amount of data in arrays
pmset -g custom |
awk '/^((AC|Battery) Power[:]$|[ t]+displaysleep[ t])/ &&
(ORS = ORS < FS ? FS : RS)' |
column -t
Output:
Battery Power: displaysleep 2
AC Power: displaysleep 5
From here, filtering AC Power
vs. Battery Power
is trivial.
2
Using awk
:
function get_displaysleep_from_power () {
pmset -g custom | awk -v heading="$1" '
BEGIN {heading_found=0}
{
if(!heading_found){
heading_found=match($1,"^"heading)
}
if (heading_found){
if ($1 == "displaysleep") {
print $2;
exit
}
}
}
' -
}
get_displaysleep_from_power "AC"
get_displaysleep_from_power "Battery"
outputs:
5
2
I would harness GNU AWK
for this task following way, let command
output be
Battery Power:
lidwake 1
lowpowermode 1
standbydelayhigh 86400
standbydelaylow 10800
standby 1
proximitywake 0
ttyskeepawake 1
hibernatemode 3
powernap 0
gpuswitch 2
hibernatefile /var/vm/sleepimage
highstandbythreshold 50
displaysleep 2
womp 0
networkoversleep 0
sleep 1
lessbright 1
halfdim 1
tcpkeepalive 1
acwake 0
disksleep 10
AC Power:
lidwake 1
lowpowermode 0
standbydelayhigh 86400
standbydelaylow 0
standby 1
proximitywake 1
ttyskeepawake 1
hibernatemode 3
powernap 0
gpuswitch 2
hibernatefile /var/vm/sleepimage
highstandbythreshold 50
displaysleep 5
womp 1
networkoversleep 0
sleep 1
tcpkeepalive 1
halfdim 1
acwake 0
disksleep 10
then
command | awk '/^[A-Z]/{head=$1;next}{arr[head][$1]=$2}END{print arr["AC"]["displaysleep"]}'
gives output
5
Explanation: for lines starting with uppercase letter I set head
value to 1st field (Battery
and AC
for given data) and instruct GNU AWK
to go next
line that is not doing anything more. For all others lines I set value in 2D array under head-1st field key. In END
I print value under desired key.
(tested in GNU Awk 5.1.0)
If you are looking for a robust way to create a hierarchical data structure from what you see output from pmset
there are several ways:
- Use awk in paragraph mode to easily catch the header and the time of interest;
- Use a regex to parse all the output and build a hash from that;
- Use the Apple tools to parse the binary plist file.
Here is a simple awk to get the value of displaysleep
for all sections:
x=$(pmset -g custom | sed -E 's/([^[:blank:]][^:]*:)/nn1/')
echo "$x" | awk 'BEGIN{RS=""; FS="n"} {
for(i=1;i<=NF;i++) if ($i~"^ displaysleep ")
print $1, $i
}'
Prints:
Battery Power: displaysleep 2
AC Power: displaysleep 10
The problem with parsing the output from pmset -g custom
is that the spaces may be ambiguous. Examples:
-
Both keys and values can have spaces and one or more spaces are used to delimit between key and value.
-
The value for the key
"hibernatefile"
is potentially a file with a space in it. -
The key
"sleep"
not only reports status but may report the reason the actual state is different than assigned. If I enterpmset -g | grep ' sleep '
the return issleep 0 (sleep prevented by bluetoothd, coreaudiod, powerd, sharingd)
With those provisos, you can parse the output of pmset
with the knowledge that some values for certain keys cannot be reliably parsed without further knowledge of that specific key.
Here is a Ruby that will parse the output of pmset -g custom
into a Ruby hash. That hash can then be queried as a two level hash (["Battery Power"]["powermode"]
) or turned into a structure like JSON to query in another way:
pmset -g custom | ruby -r json -e '
dat=$<.read.scan(/^(S+s+Power:[sS]+?(?=(?:^S+s+Power:$)|z))/).
map{|a| sa=a[0].split(/:s*R[ t]+/,2); [sa[0], sa[1].split(/R[ t]+/)]}.
map{|sa|
[sa[0], sa[1].flatten.map{|s| s.scan(/^([^n]+?)s+(S+$)/)}.
flatten(1).to_h] }.
to_h
#output that hash
puts "pmset -g custom as hash:n#{dat}nn"
puts ""Battery Power"."powermode": #{dat["Battery Power"]["powermode"]}nn"
puts "pmset -g custom as JSON:n#{JSON.pretty_generate(dat)}"'
Prints:
pmset -g custom as hash:
{"Battery Power"=>{"Sleep On Power Button"=>"1", "powermode"=>"0", "standby"=>"1", "ttyskeepawake"=>"1", "hibernatemode"=>"3", "powernap"=>"1", "hibernatefile"=>"/var/vm/sleepimage", "displaysleep"=>"2", "womp"=>"0", "networkoversleep"=>"0", "sleep"=>"1", "lessbright"=>"1", "tcpkeepalive"=>"1", "disksleep"=>"0"}, "AC Power"=>{"Sleep On Power Button"=>"1", "powermode"=>"0", "standby"=>"1", "ttyskeepawake"=>"1", "hibernatemode"=>"3", "powernap"=>"1", "hibernatefile"=>"/var/vm/sleepimage", "displaysleep"=>"10", "womp"=>"1", "networkoversleep"=>"0", "sleep"=>"0", "tcpkeepalive"=>"1", "disksleep"=>"0"}}
"Battery Power"."powermode": 0
pmset -g custom as JSON:
{
"Battery Power": {
"Sleep On Power Button": "1",
"powermode": "0",
"standby": "1",
"ttyskeepawake": "1",
"hibernatemode": "3",
"powernap": "1",
"hibernatefile": "/var/vm/sleepimage",
"displaysleep": "2",
"womp": "0",
"networkoversleep": "0",
"sleep": "1",
"lessbright": "1",
"tcpkeepalive": "1",
"disksleep": "0"
},
"AC Power": {
"Sleep On Power Button": "1",
"powermode": "0",
"standby": "1",
"ttyskeepawake": "1",
"hibernatemode": "3",
"powernap": "1",
"hibernatefile": "/var/vm/sleepimage",
"displaysleep": "10",
"womp": "1",
"networkoversleep": "0",
"sleep": "0",
"tcpkeepalive": "1",
"disksleep": "0"
}
}
Alternatively, you can do it the proper Apple Way which is to reference the actual plist file.
Here is a Ruby that parses the plist file into XML:
ruby -e '
# Depending on the version of OS X, the plist can be at
# /Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist
info_plist=File.read("/Library/Preferences/com.apple.PowerManagement.plist")
puts IO.popen("plutil -convert xml1 -r -o - -- -", "r+") {|f|
f.write(info_plist)
f.close_write
info_plist = f.read # xml plist
}'
Prints:
<?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>AC Power</key>
<dict>
<key>DarkWakeBackgroundTasks</key>
<integer>1</integer>
<key>Disk Sleep Timer</key>
<integer>0</integer>
<key>Display Sleep Timer</key>
<integer>10</integer>
<key>System Sleep Timer</key>
<integer>0</integer>
<key>Wake On LAN</key>
<integer>1</integer>
</dict>
<key>Battery Power</key>
<dict>
<key>DarkWakeBackgroundTasks</key>
<integer>1</integer>
<key>Disk Sleep Timer</key>
<integer>0</integer>
<key>Display Sleep Timer</key>
<integer>2</integer>
<key>ReduceBrightness</key>
<integer>1</integer>
<key>System Sleep Timer</key>
<integer>1</integer>
<key>Wake On LAN</key>
<integer>0</integer>
</dict>
<key>SystemPowerSettings</key>
<dict>
<key>Update DarkWakeBG Setting</key>
<true/>
</dict>
</dict>
</plist>
You can then query that XML file.
This might work for you (GNU sed and bash):
f(){ p=${1:-Battery};
pmset -g custom | sed -En '/:$/h;G;s/ (displaysleep.*)n('$p'.*)/2 1/p'; }
Create a shell function that sends the output of the pmset
command through sed and outputs by default Battery Power
or AC Power
if supplied with the parameter AC
.
I wouldn’t normally answer a question that doesn’t contain an attempt by the OP to solve their own problem but since there are multiple answers already:
Using any awk:
$ pmset -g custom | awk -v pwr='Battery' -v tag='displaysleep' 'index($0,pwr" ")==1{p=1} p && $1==tag{print $2; exit}'
2
Assuming the headings don’t appear anywhere else and aren’t repeated, your question amounts to:
- skip over stuff until
- either of the headings is matched
- skip over more stuff until
displaysleep
is matched
And doing that twice