I made a nifty little one liner so cal will show you the current date. It even makes sure not sure not to mess up the formatting. I'm sure this is like, the millionth time this was invented, and I'm sure there's a better way to do it, and it's not even technically a one-liner, though it is short.
DATE=`date +%d`;REP=`echo $DATE | sed 's/./#/g'`;cal | sed "s/ $DATE / $REP /"
and you get something like:
December 2005
S M Tu W Th F S
1 2 3
4 5 6 7 8 9 10
11 12 ## 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
woo!
UPDATE: already found a better one:
cal | sed "s/ $(date +%d) / $(date +%d | sed 's/./#/g') /"
UPDATE 2: it worked till the 17th, oops! Not all the dates have spaces on both sides. The best thing i could think of to do was just add spaces on each end of the lines, so now I'm using:
cal | sed "s/^/ /;s/$/ /;s/ $(date +%d) / $(date +%d | sed 's/./#/g') /"
UPDATE 3: thanks to someone at MacGeekery, I learned how to use shell escape codes. So here's a version that just bolds the current date:
cal | sed "s/^/ /;s/$/ /;s/ $(date +%d) /$(printf '\e[1m&\e[m')/"
UPDATE FOR NEW YEAR: you need to strip the leading zero from the date:
cal | sed "s/^/ /;s/$/ /;s/ $(date +%d | sed 's/^0//') /$(printf '\e[1m&\e[m')/"
EVEN BETTER:
cal | sed "s/^/ /;s/$/ /;s/ $(date +%e) /$(printf '\e[1m&\e[m')/"
or
cal | sed "s/^/ /;s/$/ /;s/ $(date +%e) / $(date +%e | sed 's/./#/g') /"
FINAL ANSWER:
cal | sed "s/.*/ & /;s/ $(date +%e) / [] /"
I made a twitter client with bash and curl. It posts STDIN or arguments to twitter, or displays the you and friends timeline if theres no input. It uses .netrc for authentication.
#!/bin/bash
# a twitter client (uses .netrc for auth)
[ "$1" == "-h" ] && {
echo " use: $(basename $0) -h | [ tweet ]"
echo " -h help"
echo " no arguments gets latest updates"
exit
}
function friends {
curl --connect-timeout 5 -s -n $1 | awk '
{ sub(/^[^>]*>/,"",$0) }
/text/ {
sub(/<[^<]*>$/,"",$0)
m=$0
}
/relative/ {
sub(/<[^<]*>$/,"",$0)
at=$0
}
/_name/ {
sub(/<[^<]*>$/,"",$0)
print $0 " : " m " [" at "]"
}
'
}
function update {
curl -s -n -d "status=$msg" $1 &>/dev/null || echo "tweet broke"
}
if [ -t 0 ]; then msg="$*"; else msg="$(cat -)"; fi
if [ "$msg" ];then
update http://twitter.com/statuses/update.xml
else
friends http://twitter.com/statuses/friends_timeline.xml
fi
Making ssh/scp aware of location on laptops.
~/.ssh/config is nice - I have a bunch of hosts and options aliased there. The problem is that depending on which network I'm on I might need to connect to a given host externally with one configuration, or internally. It would be nice to have the computer figure out which configuration it should use instead of making me do the work.
I futzed around with scripting a wrapper for ssh/scp that would be smart about how it connected based on my wlan location, but finally figured out a way to do it transparently - without using other commands, having multiple aliases for the same machine, or changing usage in any way.
First, for each network I want custom settings for, I make a copy of ~/.ssh/config as ~/.ssh/<name of network> and edit the entries for the machines that are different on that network.
Next, I have a shell script that figures out where I am, and if there is a config file for that network in ~.ssh/. If there is, it spits out the name of the network/filename, else it simply prints "config". I called that script lan. The script looks something like this on my mac with airport:
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | awk '
/ SSID/{for( i=2;i<=NF;i++ )ssid=ssid $i " ";sub(/ *$/,"",ssid)}
END{if( ssid ){print ssid} else {print "config"}}
'
On Linux, something like this should work:
/sbin/iwconfig | awk '
/ESSID/{if( $NF ~ /ESSID:/ ){split($NF,a,"\"");if( a[2] )ssid=a[2]}}
END{if( ssid ){print ssid} else {print "config"}}
'
Finally, I have a couple of functions in my .bashrc, that look like this:
function ssh {
/usr/bin/ssh -F ~/.ssh/"$(lan)" $*
}
function scp {
/usr/bin/scp -F ~/.ssh/"$(lan)" $*
}
The full path to the ssh/scp executable is necessary so the functions don't just call themselves over and over again, and $(lan) returns the output of the lan script. Now, ssh/scp will transparently use the correct config file for all commands.
The nice thing is you can tweak the lan script for ethernet connections or whatever, because all it has to do is spit out a filename, and you can insert other functionality into the base commands. For example, I have mine set up so that calling ssh -ls will return a nicely formatted list of my defined hosts and aliases.
spotted this somewhere and it's priceless. Russian roulette in bash:
sudo [ $[ $RANDOM % 6 ] == 0 ] && rm -rf / || echo "You live"
If you have an Apple laptop with Airport this bash line returns the SSID of the current network:
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -I | awk '/ SSID/ {printf $2}'
With that in mind i made a smart homepage script in php. I run it on my local webserver and use it as my homepage.
Given a list of trusted SSID's it will test internet connection and:
There's other ways I could imagine running this, but it should be pretty easy to modify for diferent behavior.
So there's a Tiger compatible version of GeekTool floating around finally, and I've been playing around with it the past couple days.
I'm still a beginner at shell scripting and the whole gestalt of modern computing, and I've found this not only fun to play with, but also quite educational, especially with regards to those geekiest of tools, bash one-liners.
I've learned a lot from this, for instance that I like awk. It certainly isn't awkward, lol. And take away my propensity for just doing cool crap because I can, and this thing can still be really quite actually real-world useful. It's already replaced half my Dashboard - I find the displayed information simultaneously more noticible and less in the way.
So here's where I'll put my setup, and any other stuff I found that works nice with GeekTool even if it might not maybe be all that actually useful all the time. And of course, links to the work of others.
My Dealy:
Here's what I got on my desktop right now:
I also have separate group that displays the latest satellite weather maps. Sweet.
iCal events: This is actually pretty involved. I published a calender in iCal to my website, then used a php script to grab and format the information i want out of it, then used:
/sw/bin/lynx -dump http://cutup.org/ical.php
to display it. The -dump option just has lynx dump whatevers in the page to the Terminal. I have it set to refresh every 3600 seconds (1 hour). As the newness wears off, and considering how often my iCal changes this may go down significantly. Note the full path to lynx, it seems to need that even though it's in my $PATH.
Processes: There's a few ways to run this. Right now, I like:
ps -cm -U username | awk '/:/ && $5!~/Dashboard/'
c omits the full path of each process, m orders the processes by memory and -U username shows all processes owned by that user. Plain ps only shows processes run through the terminal, ps -A gives me more than I want to see, ps -U was pretty good, I thought. Replacing the m with r orders the processes by CPU usage, I haven't really decided which one I like better.
The awk command /:/ strips out the top line that names the fields - it says only print lines that include a colon. && (and) do not capture lines where field 5 contains the string "Dashboard" (I dunno - it was annoying me for some reason to see them).
I have this refreshing every 10 seconds. What can I say, i like seeing the numbers change.
Bloglines Unread Items While snooping around in the Bloglines API I found a nice url that returns, very simply, the number of unread items in your account. So:
/sw/bin/lynx -dump http://rpc.bloglines.com/update?user=username@address.com&ver=1
gives you a nice little count. I have this set to refresh every 300 seconds (5 minutes).
uptime, memory, and cpu use I managed to fit all this into a one-liner. I was so proud I left it that way, even though I like refreshing my memory and cpu stats every 10 seconds, and uptime doesn't really need to be updated that often. But what the hell, I have a modern laptop computer, well capable of this sort of frippery. I still wonder which is more intensive, having two separate tasks that refresh at different intervals, or one task that doesn't refresh as efficiently as possible. I have no idea where to even start trying to figure that out.
So anyway, I'm running:
uptime | awk '{printf "up : " $3 " " $4 " " $5 " " }'; top -l 1 | awk '/PhysMem/ {printf "RAM : " $8 ", " }' ; top -l 2 | grep 'CPU usage' | awk {print $6, $7=":", $8, $9="user", $10, $11="sys", $12, $13}'
Ooh, exciting, eh? Basically, using printf instead of print makes it so awk doesn't carriage return after it outputs, allowing this all to go on one very spiffy line at the bottom of my screen.
I have uptime piped into awk and discarding everything but field 3. Then there's some slightly strange calls to top to get the CPU and memory stats. Geektool can't deal with interactive proccesses like top, but top allows us to dump snapshots instead, with the -l flag. So top -l 1 outputs 1 snapshot of top. Geektool is cool with that, and then it's a simple matter of piping the output to awk and printing the applicable field, formatted nicely of course since we're using printf.
For CPU use it's a bit trickier. For some reason top -l 1 prints an inaccurate reading. But oddly enough, if you use top -l 2 you get two readings, and the second one is accurate! I found some crazy workaround, and then someone pointed out that I can just use tail. Heh. So with this and the ip stuff that comes next I was able to retire my SystemStats dashboard widget.
Awk is cool. I found it pretty easy to get started with, simple and useful for lots of everyday grepping type stuff. I learned lots of good stuff about it pretty quickly here and here. The more comfortable I get with stuff like this, the more the 1970's era technology of UNIX appeals to the futurist in me.
Local IP I tried to combine this with the external IP but even with awk and printf I was getting a line break so for now they're separate. It goes:
ifconfig | awk '/broadcast/ {print $2}'
which is field 2 from the line containing "broadcast" in ifconfig. I have this refreshing every 120 seconds.
External IP I'm sure there's a better way but here's how I do it. I have a php script on my server that displays the IP adress of whoever hits it. And I have a script in the webserver part of my local machine that calls it. The script looks like this:
if ($ip = @file_get_contents("http://cutup.org/ip.php")) {
$host = gethostbyaddr($ip);
print "$ip $host";
} else {
print "no net connection?";
}
It gets the external IP from my ip printing page and also does a host lookup. So then:
/sw/bin/lynx -dump http://localhost/~username/loc.php
tells me where my computer is. I like how this is web accessible, so if I can access my server remotely I can see where it is :) You never know, it might come in handy some day. I have this set to refresh every 120 seconds as well.
Calendar This one's simple. All it is:
cal
There's some interesting ideas for cal and Geektool I haven't tested yet at macosxhints, but for now this is good for me.
UPDATE: I came up with a nice oneliner to replace the current date with #'s
cal | sed "s/^/ /;s/$/ /;s/ $(date +%d) / $(date +%d | sed 's/./#/g') /"
Weather:
Geektool can display pictures from a url. So the trick is to find .jpgs online that are refreshed every so often and where the name of the file does not change. I found a bunch at weather.com and some more at uswx.com. Right now I have a group, separate from all the other stuff, that has three pictures running:
http://image.weather.com/images/sat/usvis_600x405.jpg
//us visible satellite
http://image.weather.com/images/sat/regions/northeast_sat_720x486.jpg
// northeast infrared satellite
http://www.intellicast.com/WeatherImg/Satellite/world.gif
// world satellite
There's still room for more, but for now this replaces my US Weather dashboard widget nicely. Notice that you call tell from the picture urls that the filename is not likely to change. If there's a date or time in the name of the picture, this wouldn't be so easy.
STUFF I HAD TO FIGURE OUT:
By default, Geek Tool windows are black text. My backgrounds are usually dark, so I have to change the text color to white before I can even see anything there. I wouldn't mention it except that I couldn't figure out whether my command wasn't working or the display was wierd when I got started.
OTHER STUFF THAT MAY BE OF INTEREST:
I picked this up somewhere, it seems to show all the connections on your network
netstat -ab -f inet | grep -i established | sort +4
This one's neat. It shows the IP's of everyone that visited your site:
tail -5000 /var/log/httpd/access_log | grep -v 127.0.0.1 | awk '{print $1}' | uniq
Or even:
tail -5000 /var/log/httpd/access_log | grep -v 127.0.0.1 | awk '{print $1}' | uniq | nslookup -silent | awk '/name = /'
which gives you their hostname. I'm not clear on how to translate this information into a more useful domain name, when I do I imagine I will probably end up using this a lot.
RESOURCES:
macoshints search for "geek tool"
some nice bash stuff:
for .inputrc, this one makes up and down scroll through history matching whatever is already entered. If nothing is entered up and down work normally, but if, say vi is entered, it will be like vi ^R. Very nice:
# "\e[A" and "\e[B" being whatever your terminal uses for up & down.
"\e[A": history-search-backward
"\e[B": history-search-forward
for .bashrc, a function that cd's to a directory and lists the contents it takes ls arguments and a directory. for example, cl goes to home dir and lists contents. cl -l goes to ~/ and long lists contents. and cl -lrt public_html shows the contents of public_html in long list reverse time order.
function cl () {
case $1 in
-*) local o=$1; shift;;
esac
cd $* && ls $o
}
If you have a server at home or on a wireless network you frequent, you'd connect to it with an internal IP. But off the LAN, you connect with its domain name or external IP. Here's a simple and obvious function for your .bashrc that figures out where you are and connects accordingly. If defaults to your username but allows you to specify another.
Pick the function for your OS, substitute the stuff in brackets, add a port if you need to, and you'll probably rename the function to something better than sssh, like the server name or something.
# smart ssh for linux
function sssh() {
WLAN=$(/sbin/iwconfig 2>/dev/null | awk '/ESSID/{split($4,a,"\"");print a[2]}')
[ $1 ] && local AT='@'
[ "$WLAN" == "<wireless_ssid>" ] && ssh $1$AT$<internal_ip> || ssh $1$AT<domain_name>
}
# smart ssh for mac
function sssh() {
WLAN=$(/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | awk '/ SSID/ {print $2}')
[ $1 ] && local AT='@'
[ "$WLAN" == "<wireless_ssid>" ] && ssh $1$AT<internal_ip> || ssh $1$AT<domain_name>
}
Then you connect with sssh [username] wherever you are and it figures itself out.
Yeah, it's obvious but it's not like I can claim I thunk of it on my own.
Just for kicks I made a little script that takes a screenshot of my desktop, shrinks it, and uploads it to my server. I set it to run every minute with cron, and added some code that keeps it from updating if my screensaver is running.
Here's the code. It's pretty mac specific, using the screencapture and sips commands:
#!/bin/bash
test=`ps -c -U $(whoami) | awk '/ScreenSaver/'`
if [ "$test" ]
then
exit
else
/usr/sbin/screencapture -C temp_screen_cap.jpg
sips --resampleWidth 300 --setProperty format jpeg --setProperty formatOptions low temp_screen_cap.jpg --out temp_screen_cap.jpg
scp "temp_screen_cap.jpg" user@server.com:path/to/screen.jpg
rm temp_screen_cap.jpg
fi
You need to modify the upload path and you can change the width, and this required generating a PGP pair for the local and remote machines as described here, otherwise you'd need to enter a password every time scp runs.
The entry in crontab looks like this (replace [TAB] with real tabs):
*/1[TAB]*[TAB]*[TAB]*[TAB]*[TAB]/Full/path/to/script > /dev/null 2>&1
For "porn mode" just comment out the line with a "#"
I dunno, I like it :)
UPDATE: Here's a more fleshed out script I use.
A recent hint at macoshints shows how to create a shell script that will make all the Applications on your system available with tab completion. I've gotten used to Quicksilver and was interested in how this replicates some of that idea in the shell. I started poking around and ended up doing this another way.
Apparently, programmable completion has been around in bash for a while, but it's not really scripted by default. However, there are scripts and packages floating around to demonstrate it's capabilities. I like fink, and sure enough, fink has a bash-completion package. Here's what I had to do to get it working: Installed the bash-completion package from fink:
fink install bash-completion
Added the following lines to my .bashrc file:
source /sw/etc/bash_completion
bind '"\t":menu-complete'
The first line calls the bash-completion script that fink installed (make sure you source it after any lines that affect PATH's), and the second line enable single-tab completions. This makes a Tab display the first possible completion, another Tab display the next, and so on, instead of the default double-tab-ask-first list.
Once this is turned on, it not only completes commands, but it can complete gui apps
open -a (TAB)
including ones it finds in Developer (it found xcode), complete ssh and scp (from known hosts), like
ssh [username@](TAB)
and man pages the same way, and etc ...
I wish there was a man file to enumerate more of what it does, but I learned that this all leverages the shell builtin complete. And looking in the /sw/etc/bash_completion file, we can see which of those it uses, and how. Neat stuff like directory commands only see directories, user commands only see users, network interface completion, man completion ... theres A LOT in there!
Theres also a /sw/etc/bash_completion.d/ folder that contains some more scripts, most of them seem to be custom for mac, like open.sh and fink.sh. I looked at the open script and right at the top is the search path. It was trivially easy to add extra places for it to look for apps. I added my games folder where I keep some apps separate. Seems easy enough to add more scripts too.
It's crazy, my Terminal just got a whole lot smarter, and it will take me a while to catch up. Unix heads always had all this stuff ages ago, but sometimes I guess they forget to let us know.
UPDATE: Debian seems to include the bash completion scripts in its base install, all you have to do is uncomment a couple of lines in the default .bashrc to get it running. And it's available as a package in cygwin as well.