Showing posts with label shell. Show all posts
Showing posts with label shell. Show all posts

2011/08/30

Lion: Hardware Growler crashes

For anyone using Lion and Hardware Growler (part of the excellent Growl package), you may have noticed that it crashes under some circumstances. (For example, see )

And if you're using it faceless, you may not even notice for awhile - say, til you realize you're waiting for a Growl that'll never come.

Fortunately, this is easy to slap a bandage over, with a simple bash script (all on one line):

while true; do if [ $(ps jaxwwww|grep [H]ardwareGrowler|wc -l) = 0 ] ; then open /Applications/Growl-1.2.2/Extras/HardwareGrowler/HardwareGrowler.app; echo "$(date): launched one"; fi; sleep 1; done

(I know; a hard-coded path - you'll have to fix it if your path differs - if you know a better way, feel free post in the comments.)

I tried using a longer sleep, though the resource utilization was indistinguishable, so I left it at "1", which means it recovers almost instantly.

This can be "automated" even further: Into a text document, paste this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>WindowSettings</key>
<array>
<dict>
<key>ExecutionString</key>
<string>while true; do if [ $(ps jaxwwww|grep [H]ardwareGrowler|wc -l) = 0 ] ; then open /Applications/Growl-1.2.2/Extras/HardwareGrowler/HardwareGrowler.app; echo "$(date): launched one"; fi; sleep 1; done</string>
<key>Columns</key>
<string>44</string>
<key>Rows</key>
<string>15</string>
<key>WinLocULY</key>
<string>678</string>
<key>WinLocX</key>
<string>611</string>
<key>WinLocY</key>
<string>0</string>
</dict>
</array>
</dict>
</plist>


Save it as "something.term".

Double-click / launch the document and it opens Terminal.app for you. Add it to your Login Items, in the Users & Groups (nee Account) preference pane, and it's automatic.

(I believe the "WinLoc" keys are ignored in favor of an offset from the most recent Terminal window; pity - I'd like to place this at the same location on the screen each time. Though of course, Lion's "Auto Resume" handles this nicely too. :)

2010/06/27

Show differences between two Time Machine backups

Below is a quick Bash script to display the difference between two Time Machine backups.

The output is simply the pathnames of the files that were added to Time Machine in that time period. This may of course be piped to another command/script to provide further info - say, the amount of space used.

Cheat: Provide a simple date (formatted the same way that Time Machine does) instead of the "older" backup pathname, and it'll work whether or not there was a backup then. (The order of the arguments is unimportant; the script will figure it out.)

#!/bin/bash

LF=$'\n'
usage="Usage: $(basename $0) Time-Machine-folder-1 Time-Machine-folder-2"
usage=$usage$LF"(Compare the two Time Machine backup folders;"
usage=$usage"find and display files that were backed up in that time period.)"

function tweakDate { # to a fmt understood by find cmd
echo "$1" | awk -v FS="" '{print $1$2$3$4$5$6$7$8$9$10" "$12$13":"$14$15":"$16$17}'
}

if [ ! $# == 2 ] ; then
{
echo "$0 requires two arguments; the Time Machine folders to compare." >&2
echo "$usage" >&2
exit 1
}
fi

baseName1=$(basename $1)
baseName2=$(basename $2)

# NB: operator must be escaped or will signify redirection
if [ "$baseName1" \< "$baseName2" ] ; then
{
olderFldr=$1
recentFldr=$2
olderFldrName=$baseName1
recentFldrName=$baseName2
}
else
{
reverse=TRUE
olderFldr=$2
recentFldr=$1
olderFldrName=$baseName2
recentFldrName=$baseName1
}
fi

if [ ! -d "$olderFldr" ] ; then
{
echo -n "Warning: $olderFldr doesn't seem to exist;" >&2
echo " maybe you're just using it to specify a date?..." >&2
}
fi

olderFldrParent=$(dirname $olderFldr)
recentFldrParent=$(dirname $recentFldr)

if [ ! $olderFldrParent == $recentFldrParent ] ; then
{
echo -n "Warning: These folders are not in the same parent folder;" >&2
echo " this may not be what you want..." >&2
}
fi

olderFldrDate=$(tweakDate $olderFldrName)
recentFldrDate=$(tweakDate $recentFldrName)

if [ ! -d "$recentFldr" ] ; then
{
latestBackup="${recentFldrParent}/$(ls -1 $recentFldrParent | tail -2 | head -1)"

echo "Can't find $recentFldr; searching the latest backup" >&2
echo " ($latestBackup)" >&2
echo " for files modified between $olderFldrDate and $recentFldrDate." >&2
echo " This may take awhile..." >&2
echo $LF >&2

find -x "$latestBackup" -type f -newermt "$olderFldrDate" \! -newermt "$recentFldrDate" -print
}
else
{
echo "Now searching for backups in $recentFldrName" >&2
echo " that are more recent than $olderFldrDate." >&2
echo " This may take awhile..." >&2
echo $LF >&2

find -x "$recentFldr" -type f -newermt "$olderFldrDate" -print
}
fi

# eof

2009/09/03

Snow Leopard "safe sleep" tweak

A quick bash script to dynamically adjust the sleep behavior under Snow Leopard - to avoid the expense of maintaining the sleepimage if the battery is above a certain level.

Thanks again to Joe Kissell for the original script from (TidBITS#893/20-Aug-07).

#!/bin/sh

#
# 20090902 mvgfr: rules have changed (ex: hibernatemode; there is no 7) so recode
# AND be more fault-tolerant...
# (setpoints raised, & cron freq increased, since lately been losing power before sleep)
# 20090901 mvgfr: Snow Leopard now defaults to "Secure Virtual Memory" so change safe mode to 7
# (need programmatic way to check, anything better than system_profiler?)
# 20090728 mvgfr: reduce setpoints by 10 (%)
# 20070908 mvgfr: impl setpoints, tweak msgs, Trash vs rm, debug...
# original: Joe Kissell , (TidBITS#893/20-Aug-07)
#

debugMe="" # simple toggle; empty means NO & anything else means YES

setpointLow=20
setpointHigh=25

MODE=`/usr/bin/pmset -g | awk '/hibernatemode/ { print $2 }'`
LEFT=`/usr/bin/pmset -g batt | grep Internal | awk '{ print $2 }' | awk -F % '{ print $1 }'`

if [ $LEFT == "(removed)" ] ; then LEFT=0; fi # catch case in which no batt is seen

if [ $debugMe ] ; then /usr/bin/logger -t "hibernatemode" "current mode (on entry): $MODE; batt % remaining: $LEFT"; fi

if [ $LEFT -le $setpointLow ] && [ $MODE == 0 ] ; then
{
/usr/bin/logger -t "hibernatemode" "Battery level ${LEFT}%; setting hibernatemode ON"
/usr/bin/pmset -a hibernatemode 3
}
elif [ $LEFT -ge $setpointHigh ] && [ $MODE != 0 ]; then
{
/usr/bin/logger -t "hibernatemode" "Battery level is ${LEFT}%; setting hibernatemode OFF"
/usr/bin/pmset -a hibernatemode 0
mv /var/vm/sleepimage /Users/mvgfr/.Trash
}
fi

if [ $debugMe ] ; then
MODE=`/usr/bin/pmset -g | awk '/hibernatemode/ { print $2 }'`;
LEFT=`/usr/bin/pmset -g batt | grep Internal | awk '{ print $2 }' | awk -F % '{ print $1 }'`;
/usr/bin/logger -t "hibernatemode" "current mode (on exit): $MODE; batt % remaining: $LEFT";
fi

#end

macports notification of outdated

A quick and dirty bash/shell script to get automatic notification of outdated macports:

#!/bin/bash

# 20090902 mvgfr: tweaks
# 20071110 mvgfr: check for outdated macports & notify if any - via growl & stdout (emailed when cron'd)
# 20080209 mvgfr: must run sync before, so it can learn about updates!
# also added "-H localhost" workaround for growl Leopard bug (ignores some)

debugMe="" # presume we'll only use alphanum; like "YES" or empty string for NO

# may take awhile; is this an issue?
if [ ! $debugMe ] ; then /opt/local/bin/port sync; fi

if [ $debugMe ] ; then
potentialOutput="`echo 'debugging:';ls -1|head -1`"
else
potentialOutput=`/opt/local/bin/port outdated`
fi

if [ "$potentialOutput" != "No installed ports are outdated." ] ; then
echo "$potentialOutput" # for cron to email to root
if [ -f /usr/local/bin/growlnotify ]; then
NumLinesToDisplay=$((`echo "$potentialOutput" | wc -l` -1))
if [ $NumLinesToDisplay -lt 1 ]; then
echo "underflow error; see log" | /usr/local/bin/growlnotify -H localhost -s -t 'macports outdated:'
elif [ $NumLinesToDisplay -gt 20 ]; then
echo "overflow error; see log" | /usr/local/bin/growlnotify -H localhost -s -t 'macports outdated:'
else
echo "$potentialOutput" | tail -`echo $NumLinesToDisplay` | \
/usr/local/bin/growlnotify -H localhost -s -t 'macports outdated:'
fi
fi
fi

if [ $debugMe ] ; then echo "`date "+%Y%m%d-%H%M%S"`: $potentialOutput" >> /var/log/port-outdated.log; fi

#end


(Old 2007/12/01 script here.)

2007/12/05

smarter ping

while true ; do echo "`date`: `ping -qc1 domain-name | grep round-trip`"; sleep 5; done

A one-liner that does, in order:
  • show the date/time
  • show the *results* of a single ping (or nothing), on the same line
  • wait 5 seconds
  • do it all over again

And it simply does this til you kill it.

Why? Simply ping'ing doesn't give you any info on *when* things are happening - like when the server that you're waiting to reboot, did; or when the node you're testing stopped responding.

2007/12/04

ID a card/port pair, given a simple serial jack number

We recently installed a really nice Foundry switch (highly recommended) and its admin (as is generally the case) requires addressing ports by the combination of card and port number. However the cables jacking into it are numbered serially; from 1 to whatever.

So when you need to find which cart/port corresponds to which jack, it can get messy - here's a simple script (easily adapted to other scenarios) to simplify:


#!/bin/bash

# simple script to calc the card/port pair, given the patch cable #
# - result is returned as an integer, in format: port + (100 * card#)

# History:
#
# 20070523 mvgfr: incep

# minimal error trapping:
if [ -z "$1" ] ; then exit 0; fi
if [ "$1" -le 0 ] ; then exit 0; fi

patch=$1
# fudge for discountinuity; jump from card #8 to #13 (since there are no port cards in slots 9-12)
if [ "$patch" -gt 192 ] ; then let patch+=96 ; fi

card=$(( ( ( $patch * 100 / 24 ) + 99 ) / 100 ))
port=$(( $patch - ( ( $card - 1 ) * 24 ) ))

echo "$card, $port"

result=$(( ( $card * 100 ) + $port))
echo $result

exit $result #send the result back as the exit code, for potential further processing...

#end

2007/12/01

find the mdns name for an IP addr

This seemed like it might be useful and then after I dug through to finally solve the problem, it may be for only a very limited set of circumstances. :)

And interesting script anyway: Given an IP address, find the mdns (AKA ZeroConf or Bonjour; formerly Rendezvous) name - which might help give you a better idea who's there):

#!/bin/bash

# use mdns (local subnet only) to show machine name for a given host IP addr
# (ex: for machines w/ DHCP addrs and therefore unintersting domain names)

if [ -z "$1" ] ; then echo "must supply a host as 1st/only arg"; exit; fi

if [ -n "$2" ] ; then echo "must supply only one arg"; exit; fi

# is it necessary to ping it first, to make sure it's in the ARP table?
#if ! $(ping -c1 "$1") ; then echo "error pinging host"; exit; fi

if ! arpOutput=$(arp "$1") ; then echo "error in arp host"; exit; fi

arpMAC=$(echo "$arpOutput" | sed 's/.*\([a-zA-Z0-9]\{1,2\}:[a-zA-Z0-9]\{1,2\}:[a-zA-Z0-9]\{1,2\}:[a-zA-Z0-9]\{1,2\}:[a-zA-Z0-9]\{1,2
\}:[a-zA-Z0-9]\{1,2\}\).*/\1/g')
arpMAC=$(echo "$arpMAC" | sed 's/^\(.\):/0\1:/' | sed 's/:\(.\):/:0\1:/g' | sed 's/:\(.\):/:0\1:/g')

echo "starting mdns browse; you'll have to hit ^C to get control back..."

dns-sd -B _workstation._tcp | grep "$arpMAC"

# end

Script to notify if any outdated MacPorts

Updated version here.

-----

Add the script below as a cron job and it'll notify* you if you have any outdated MacPorts.

*Presuming you're configured to have cron job output emailed to you, it'll notify by email. If you have growl installed, it growl at you too. Lastly, it logs to /var/log (which probably requires creating that log file first).

(Updated for recent tweaks.)

#!/bin/bash

# 20071110 mvgfr: check for outdated macports & notify if any - via growl & stdout (emailed when cron'd)
# 20080209 mvgfr: must run sync before, so it can learn about updates!
# also added "-H localhost" workaround for growl Leopard bug (ignores some)

# may take awhile; is this an issue?
/opt/local/bin/port sync

potentialOutput=`/opt/local/bin/port outdated`

if [ "$potentialOutput" != "No installed ports are outdated." ] ; then
echo "$potentialOutput"
if [ -f /usr/local/bin/growlnotify ]; then
/usr/local/bin/growlnotify -H localhost -s -m "macports: $potentialOutput"
fi
fi

echo "`date "+%Y%m%d-%H%M%S"`: $potentialOutput" >> /var/log/port-outdated.log

#end

timestamp bash history entries

Wow; bash3 has a terrific feature; the shell variable HISTTIMEFORMAT will timestamp entries in your history - so you can get a *much* better idea of what happened, especially since it's so easy to have multiple sessions going at the same time, so that commands in the history get somewhat interleaved.

Add
HISTTIMEFORMAT='%Y%m%d-%H%M%S: '

to (for example) your .bash_profile.

And Leopard (MOSX 10.5) has bash3 by default; nice.

MOSX: My $PS1

PS1='\[\e]0;\u@\h\a\]\D{%Y%m%d-%H%M%S} \u@\h \W [\j]\n$ '

This sets the Terminal title bar to:
user@node - current-command

Which is dynamic; it changes as you issue commands. (Slick.)

And it sets the prompt to:
date-time user@node working-dir [jobs]
$

It's an awfully long prompt, so it wraps, and puts the familiar "$" at the start of the *second* line.

Why? It's sometimes *very* handy to have the prompt indicate when that command was issued.

(BTW: Bracketing the window title bar potion in "\[" and "\]" is critical; otherwise (since it doesn't print those char in the prompt itself) readline will miscalculate where you are on the line and things get ugly.)

Show ALL recently-modified prefs

Search 'full-path' for plist (pref) files that were modified more recently than 'ttt' (ex: '5 minutes ago') and dump them in human-readable format:
find 'full-path' -iname '*.plist' -newermt 'ttt' -print | xargs -n1 | sed 's/.plist$//' | xargs -n1 defaults read
(Note: It is important to specify a *full* path.)

Issue a sequence of commands, and walk away

You're ssh'd into a server and need to issue a sequence of commands that'll take awhile - and your connection might drop, terminating those commands. Try this:
nohup bash -c "cmd1; cmd2; cmd3" &

Which starts the sequence and sends it immediately to the background (the trailing ampersand) - and also sets it so that even if the controlling terminal goes away, it continues to run (via "nohup"). If you really do need to kill it, use the -KILL signal.

Caveats: Watch your stdin/stdout/etc - this is generally most useful for commands that require no input and generate no output, since, for example, you just disconnected stdin and stdout by sending it to the background.

The above is handy to know if you need to sudo the commands - since you will: a) not see the "Password:" prompt, and b) not be able to respond to it. Here's the cheat: As long as you're in a default ssh config, there's a timeout; sudo commands issued within the timeout don't prompt. So if you need to sudo the above, issue an innocuous "sudo ls" (or some such) just before, to authenticate and get the timer running.

To find out what background jobs there are:
jobs -l

Which also shows the process ID, so you can watch it via top or some such.

Want a little more feedback? How about:
nohup time bash -c "date; cmd1; cmd2; cmd3" &

Which will display the date the inner commands started and then some stats about the time the other commands took, after they're all done.

Need to make sure they each work, before going to the next? Try this:
nohup bash -c "cmd1 && cmd2 && cmd3" &

The double ampersand tells bash to execute following commands ONLY if the previous one succeeded. (More specifically, if it's exit code was zero.)

And you can get more fancy by putting some "echo" commands in there, piping the output to mail, etc.

Validate/verify MD5 or SHA1 hashes

Want to confirm that download hasn't been altered?

MD5:
if [ $(md5 -q pathHere) == 'md5-hash' ] ; then echo "OK"; else echo 'md5 hash mismatch!'; fi

SHA1:
if [ $(/usr/bin/openssl sha1 pathHere | awk '{print $NF}') == 'sha1-hash' ] ; then echo "OK"; else echo 'sha1 hash mismatch!'; fi


Substitute the path of the file to validate for "pathHere" above; similarly, the published hash for "md5-hash" or "sha1-hash".

Sure you could wrap it in a script, by why bother, when you have QuickSilver?

MOSX: Open a subfolder on a share, whether the share is already mounted or not

Mac OS X's "open" shell command is powerful - though strangely dependent on the mount state of shares.

Executing
open afp://server.example.com/share/top-folder/sub1/sub2

Will open the share - and, if the share was already mounted, ignore the rest of the path.

So, here's a quick command (make sure it's all one one line) that will open the subfolder, whether the share is already mounted or not:
if [ -d /Volumes/share ]; then open /Volumes/share/top-folder/sub1/sub2 ; else open afp://server.example.com/share/top-folder/sub1/sub2 ; fi

Adaptation for other protocols (ex: SMB), other mountpoints and error-checking are left as an exercise for the reader. :)