05401, 05403, 05446, 05462, 05482, 05673, 05701, 37signals, 40hz, 2008, 2010, aardman animations, ac propulsion, adium, ads, aim, airport, al franken, algorithm, amazon, andy hertzfeld, animation, apache, apple, applescript, architecture, archive, art, article id, asterisk, at&t, atom, automobile, away message, backpack, badge, barack obama, basecamp, bash, beos, bernie sanders, bicycling, bill atkinson, billboard, blacklisting, blog, blogroll, blogzot, bluetooth, blunt, bluray, book, bookmarklet, bot-net, brad bird, browser, btv, bug, build, bungie, bunny, burlington, call of duty, camera, camping, can-spam, cars, centralized, channel camp, chocolate, classic, classic mac workshop, clipcat, clothing, cms, collection, color classic, comedy, comedy central, comic, computer, concert, conversion, cookbook, corrosion, cowards way out, crack, crashing, creature comforts, criticism, daring fireball, darwin, dashboard, david byrne, dcl, death, delicious, derbi, design, development, digg, dilemma, discussion, disney, domain, download, drivers, dvd, dynetk, e-mail, e3, easter, ebox, eckhart, eckhart köppen, eckhart koppen, eddie izzard, edward gorey, einstein, eject, election, electric motorcycle, electric motorsport, electric vehicle, electronics research laboratory, elmo, emate, emulator, encryption, environment, environmental impact, erin mckeown, escale, exploit, express 530t, expressionengine, feature, feed, feedburner, filtering, finance, firmware, fixdavsvn, flickr, flynn center, focus, font, food, ford, for sale, free, freeverse, freezing, fresh air, frog design, front row, fusion, games, gears of war, geek, geek technique, geocities, git, github, gmail, google, gpr, grammar, grant hutchinson, graylisting, gtd, hack, haiku, halo, hayao miyazaki, health, higher ground, highrise, hiking, hiroshi noguchi, history, hope, hotspot, html, html5, hulu, humor, hybrid, hybrid technologies, hypercard, intel, internet, interview, ipad, iphone, ipod touch, isao takahata, itunes, jabber, japan, javascript, jetblue, jfk, john gruber, john oliver, jon stewart, kid koala, launchd, layover, leopard, liberal, long trail, lorem ipsum, mac, macbook pro, mac mini, macpaint, mac pro, macworld, maczot, magazine, mail, maine, makkintosshu, marathon, marketing, mark hoekstra, matthias melcher, media, mesagepad, messagepad, microbus, microsoft, mobileme, model s, modern warfare, money, monitoring, moon river, motorola, movie, movies, mrtg, multitasking, music, mwsf07, mystic, nascar, ncx, nda, netflix, network, newt/0, newton, newton os, newton press, newtonscript, newtontalk, newton x press, nick park, nitch, npr, on point, openpbx, open source, operation ivy, optimization, organic, os 6, os 8, os9, osheaga, osx, os x, owc, package, palm, password, patch, paul guyot, pbx, pdf, pesticides, photography, pico card, pilot, pixar, playstation, plist, plug-in, pod jungle, politics, productivity, ps3, psp, pump-and-dump, quickdraw, quicksilver, racism, rack-n-roll, radio, ratatouille, rebooting, recycling, regex, regular expressions, remake, required reading, restoration, retrochallenge, review, roadster, room without a window, rss, scion, screencast, script, search, security, server, sesame street, seven days, shame, shelburne, shelburne museum, shirt, shoppinging cart, signature, simon bell, small dog electronics, snow leopard, social, software, solution, sony, spam, spam haus, startup item, statistics, status, stefano paris, stephen colbert, steve jobs, steven colbert, steven frank, studio 360, studio ghibli, subethaedit, subversion, susan kare, swiss, swuser, sync, syndication, sysmon, tablet, tags, tax, technorati, ted talk, television, terry gross, tesla motors, textpatter, textpattern, the colbert report, the daily show, the flaming lips, the gashleycrumb tinies, the radiator, the world, times argus, titles, tkip, todd kollins, tom gage, tools-osx, trailer, trash, travel, tree, trends, troubleshooting, truetype, twitter, typography, tzero, unicel, universal access, unna, update, upgrade, url title, user interface, v710, venue, verizon wireless, vermont, victor rehorst, video, virtualization, vmware, volkswagen, volvo 122, vpr, vw, wait wait don't tell me, wall-e, wallace & gromit, wavelan, web, web 2.0, webkit, web site, whitepaper, wifi, wikipedia, windows, winter warm-up, wireless, wpa, writing, wwdc, wwnc, xbox 360, xbox live, xhtml, xserve, yahoo, ze frank, zero emission
Articles Tagged "bash":
tools-osx 2011-06-02 ¬
2011-06-02
I’ve updated my collection of Mac OS X command line tools with improvements to the trash tool:
trashcan now list trash contents across all volumes (with a disk usage total) as well as empty the trash (including a secure empty option; both emptying operations require confirmation).
Note: Obviously, the new version of trash is now destructive. I have tested and use this myself, but I cannot guarantee it won’t securely erase your entire hard drive if it encounters some odd edge case and I cannot be held responsible for that. So, use at your own risk and keep backups!
Go download them all (clipcat, eject, swuser, and trash) or get the source code on github!
tools-osx 2011-02-25 ¬
2011-02-25
There’s been a small addition to my collection of Mac OS X command line tools:
swusergives you control of Mac OS X’s Fast User Switching from the command line, including options for switching to login window, user by name, or user by ID. Unfortunately, it’s not compatible withscreen.
Go download them all (clipcat, eject, swuser, and trash) or get the source code on github!
tools-osx 2010-12-08 ¬
2010-12-09
I’ve got a new release of my collection of Mac OS X command line tools for you, including:
trashcorrectly increments filenames (à la Finder) if the same filename already exists in your Trash instead of complaining that the file already exists. It also has a teensiest bit more logic regarding trash on the boot volume vs. other volumes.ejectnow supports ejecting mounted network volumes and includes a-foption to force a stubborn volume to eject (to be used only in extreme cases).clipcatis a new addition, submitted by David Kendal, which allows printing & concatenating of Text Clippings!
Go ahead and download them or grab the source code on github!
Regular Expressions in Bash (and Alternatives) ¬
2010-11-03
While cleaning up some old bash code and preparing tools-osx for release, I happened across a very useful bit of information: bash does support regular expressions! Well, at least bash 3.0 and newer do.
I first learned regular expressions in Perl, so I’ve pined for =~ in other scripting languages ever since. With bash, I, like most others, get by most of the time by piping things through grep for matching and sed for replacements, but the bane of my existence has always been capturing groups (capturing parentheses).
For example, let’s say we want to grab just the volume name out of a path like /Volumes/Macintosh HD/Users/Shared/, the following regular expression would be perfect for that:
^/Volumes/([^/]+)
That says match a string that starts with “/Volumes/” followed by one or more characters that are not “/” (capturing the one or more characters that are not “/”). So, if we were to match that against the aforementioned example path, it would capture:
Macintosh HD
Well, now I know that you can do this using bash 3.0+‘s built-in regular expressions support:
if [[ "/Volumes/Macintosh HD/Users/Shared/" =~ ^/Volumes/([^/]+) ]]; then
vol="${BASH_REMATCH[1]}"
fi
Very straightforward for those who are familiar with regular expressions. However, it took my a while to get that to even work. Why? I assumed that I needed to quote the regular expression (in bash quoting is extremely important). The first tutorial I was going by pulled the regex from command line input and used it from a variable, so that offered little evidence for or against quoting the regular expression, but another that I found clearly was quoting the regular expression. Eventually I read the comments on the latter tutorial and there were some that found the regular expression worked in single quotes and some found that it had to be left unquoted.
For me, on Mac OS X 10.5 Leopard, bash regular expressions have to be left unquoted.
Note: bash 3.0+‘s built-in regular expressions are, like grep -e or egrep, POSIX extended regular expressions, not full Perl-compatible regular expressions, so make sure you understand the differences in syntax.
So, now comes the big caveat with all of this new found power and why it’s taken so long for me to discover it: bash 3.0 and newer have only started becoming common in the last few years, so it’s not widely supported yet. I looked through the Mac OS X source code and found that only Mac OS X 10.5 Leopard and 10.6 Snow Leopard have included a version of bash newer than version 3.0. Mac OS X 10.4 Tiger (including 10.4.11) and earlier all had bash 2.05 or earlier. So, you should really only use bash’s built-in regular expression support if you know the environment will have version 3.0 or newer.
I know, it certainly dashed my hopes a bit too.
In Which We Come to Understand an Alternative
However, all is not lost, there is a rudimentary alternative in read. It’ll never be as powerful as regular expressions, but it can allow simple captures like the example discussed above. Let me just throw you into the deep end and see if I can then explain how to swim.
Again, here’s that bash regular expression code snippet I came up with to parse the volume name out of a path:
if [[ "/Volumes/Macintosh HD/Users/Shared/" =~ ^/Volumes/([^/]+) ]]; then
vol="${BASH_REMATCH[1]}"
fi
And here’s that same capture using read:
IFS=/ read -r -d '' _ _ vol _ <<< "Volumes/Macintosh HD/Users/Shared/"
Wow, it’s certainly more compact, but it doesn’t look like it contains much actual functionality, right? Just a couple switches and some underscores.
Let’s step through it, argument by argument:
IFS=/– Characters found in$IFSare word delimiters, so we’re setting our delimiter to “/”.read– Well, that’s thereadcommand we’re calling to pull all this off.-r– Specify “raw” input (no backslash escaping).-d ''– Read until we hit ‘’ (an empty string) instead of a newline (so, essentially, read the entire input)._ _ vol _– This is confusing part, this is actually where we tellreadwhich variable to store each matching field in. Let’s break it down further:_– The first character of our input string is a “/” (and so is our delimiter), so the first field is going to match an empty string (everything between the start and the first “/”, i.e. nothing), so we’ll just dump that in$_to discard it._– The second match is going to be “Volumes” (everything between the first “/” and the second “/”), but we don’t care about that either, so discard it into$_as well.vol– The third match (everything between the second “/” and third “/”) is what we’re actually looking for (the volume name), so we’ll store that in$vol._– The fourth match (and all further matches; everything between the third “/” and fourth “/”, and so on, and so on) are also nothing we care about, so also toss them into$_.
<<<– This is abash“here string” operator, it indicates that the following string be sent as standard input to the command."Volumes/Macintosh HD/Users/Shared/"– This is the string we want to run throughreadto capture from.
Putting it back together a bit, we’d have something like this:
IFS=/– Split on the “/”.read -r -d '' _ _ vol _– Store the 3rd field in$vol.<<< "Volumes/Macintosh HD/Users/Shared/"– From the string “Volumes/Macintosh HD/Users/Shared”.
And, just like the regular expression code, we end up with the following match stored in $vol:
Macintosh HD
Okay, you may have caught on that that read example was not actually the exact same capture as the regular expressions was, here’s why: the string doesn’t have to start with “/Volumes/”. We could’ve matched against “/Users/Shared/” and it would’ve captured “Shared”. That’s not going to cut it!
Fortunately, we could just wrap the call to read with a string comparison of the first zero through nine characters of the path name against “/Volumes/”, as so:
path="Volumes/Macintosh HD/Users/Shared/"
if [ "${path:0:9}" = "/Volumes/" ]; then
IFS=/ read -r -d '' _ _ vol _ <<< "$path"
fi
Not so scary now, I hope, and far more backwards compatible with older versions of bash.
If you’re looking to capture from a string that can be reasonably split on a delimiter, like we did with the “/”, read is an excellent alternative to regular expressions (esp. when paired with other string comparisons). That said, if you know you can rely on having bash 3.0+, by all means, use the regular expressions!
Announcing tools-osx ¬
2010-10-26
Ever since Mac OS X was released, I’ve found myself working via the command line more and more every year. While there are some native commands like open, pbcopy, and pbpaste with NeXTSTEP roots which help one switch back and forth between the CLI & GUI, I’ve always found a few gaping holes.
Over the last few days, while learning git and playing with github, I’ve grabbed two of the bash scripts I’ve managed to keep ahold of through many a hard drive since 2007: eject & trash. They’ve been polished up a bit and are now part of a collection named tools-osx which I’ll be adding to as I fill empty spaces in my Mac OS X command line toolbox.
Go ahead and download them or grab the source code on github!
Automating Countdown Tweets with Bash ¬
2009-11-02
Modern Warfare 2 will be out in a few days, so I felt @cowardswayout should count down to the release day. I could spend a few minutes at some point during each of the next few days—assuming I can remember to—posting a nearly-the-same message to Twitter or I could automate it. Yeah, better automate.
In trying to keep my bash-fu hightened, I tossed together the following bash script:
#!/bin/bash# # mw2_countdown # # Post countdown to Modern Warfare 2 release to Twitter every day #release_year=2009 release_month=11 release_day=10 year=10#$(date +%Y) month=10#$(date +%m) day=10#$(date +%d) username='cowardswayout' password='somethingiwontleakhere'if (( $release_year == $year && $release_month == $month && $day <= $release_day )); then if (( $day == $release_day )); then printf -v message "Modern Warfare 2 (http://bit.ly/dQMPz) is out! Go get your copy!" else printf -v message "Modern Warfare 2 (http://bit.ly/dQMPz) in %s..." $(( $release_day - $day )) fi curl -u $username:$password -d status="$message" http://twitter.com/statuses/update.xml fi
Since I host with Mac OS X Server and am anal about doing things “The Mac Way”, I whipped up a launchd job to run it every morning at 1am:
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.cowardswayout.mw2_countdown</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/mw2_countdown</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>1</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</dict>
</plist>
As you can see, I installed the bash script in /usr/local/bin/mw2_countdown and the launchd job went in /Library/LaunchDaemons/com.cowardswayout.mw2_countdown.plist.
I changed the permissions so that only root has read/execute access to the bash script, since the Twitter account password is stored in plain text:
sudo chmod 700 /usr/local/bin/mw2_countdown
And loaded the launchd job:
sudo launchctl load /Library/LaunchDaemons/com.cowardswayout.mw2_countdown.plist
Now I only have to remember to remove the bash script and launchd job sometime after 11/10/09. Naturally, this script can be easily tailored to your own needs.
Update: I’ve updated the script to prepend 10# to each call like $(date +%y) to force it to be evaluated as base 10 and also switched from using test (square brackets) to using the correct arithmetic evaluations (double parentheses).
fixDAVsvn ¬
2009-03-07
Subversion, mod_dav, and mod_dav_svn are all pre-installed on Leopard Server, authentication via Open Directory is a piece of cake, and you can even mostly config & manage via Server Admin. Nayan Hajratwala has a good tutorial explaining the setup and the few lines you have to manually add to the apache config files, but there’s one problem: whenever you update any site using Server Admin, it replaces all occurrences of ‘DAV svn’ with ‘DAV Off’, completely defeating the purpose.
When I just had just a couple of Subversion repositories and wasn’t changing my apache configs very frequently it was fine to manually switch ‘DAV Off’ back to ‘DAV svn’ and restart apache, but recently I’ve exceeded my tolerance. So, this morning I whipped up the following bash script which accepts the names of config files that’ll need to be fixed, performs the substitution, and restarts apache:
#!/bin/bash# # fixDAVsvn - Replace occurrences of 'DAV Off' in specified apache2 configs with # 'DAV svn' and restart apache. This helps do some dirty work on Mac # OS X Server when using Server Admin to modify apache configs if # you use DAV svn anywhere. # # v0.1 - 2009-03-07 - Morgan Aldridge <morgant@makkintosshu.com> # Initial development. #date=`date "+%Y-%m-%d-%H%M"`function usage() { printf "Usage: fixDAVsvn file [...]\n" }if [ $# -gt 0 ]; then until [ -z "$1" ]; do src=$1 dst=$src-$date.bak rsync -a $src $dst # back up the original file cat $dst | sed 's/DAV Off/DAV svn/g' > $src # replace 'DAV Off' w/'DAV svn' shift done/usr/sbin/apachectl restart # reload the apache configs else usage fi
I’d suggest installing it into /usr/local/bin/.
Example usage:
sudo fixDAVsvn /etc/apache2/sites/0003_any_80_svn.domain.tld.conf
Let me know if you find this useful.
Update: I’ve since released a launchd job which automates the process of running fixDAVsvn when the apache config files are modified (esp. by Server Admin).
Automatic Delicious Backups Under Leopard ¬
2009-01-26
I’ve been using delicious.com, nee del.icio.us, for my bookmarks on and off since early 2006, but only recently have I decided to really keep all my bookmarks there. Yahoo!‘s future has been somewhat questionable as of late and I’m not entirely sure I like the idea of not having my data backed up in a place where I can get at if the service goes down. I don’t really have control over the former, but the latter I do.
Delicious has a tool to export your bookmarks that gives you an HTML bookmark file for easy importing into a browser, but, alas, it’s not really scriptable. However, they do offer a way to dump all your bookmarks to xml using their API.
So, I whipped up the following bash script to perform the backup and tossed it in ~/bin/deliciousBackup:
#!/bin/bash# # deliciousBackup - delicious.com bookmarks backup # # See: http://delicious.com/help/api#posts_all # # v0.1 2009-01-25 - Morgan Aldridge <morgant@makkintosshu.com> # Initial version. #user='<username>' pass='<password>' date=`date "+%Y-%m-%d"` src='https://api.del.icio.us/v1/posts/all' dst='Users/<username>/Documents/Backups'curl -s --user $user:$pass $src | bzip2 > $dst/delicious.com-$user-$date.xml.bz2
I needed to make deliciousBackup executable, but also wanted to make sure it wasn’t readable by any other user/group since it contains my password in plain text:
chmod 700 ~/bin/deliciousBackup
Of course, if someone ever had direct access to my hard drive they could still pull my Delicious password from that file, so consider yourself warned.
I prefer to run most of my backup scripts and such using launchd instead of cron since it follows my home folder sync better, so I tossed the following .plist in ~/Library/LaunchAgents/com.makkintosshu.deliciousBackup-morgant.plist:
<?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>Label</key>
<string><tld>.<domain>.delciousBackup-<user></string>
<key>ProgramArguments</key>
<array>
<string>/Users/<user>/bin/deliciousBackup</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>12</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</dict>
</plist>
The following call loaded my LaunchAgent without a logout:
launchctl load ~/Library/LaunchAgents/com.makkintosshu.deliciousBackup-morgant.plist
You’ll note that the LaunchAgent runs every day at noon. That’s because I currently run this on my MacBook Air which may not be online most nights, but is usually online by mid-day.
Feel free to use this method if you’re so inclined.
Testing the OS Version on Darwin/Mac OS X in bash ¬
2008-08-14
I’ve been updating an installer bash script that needs to install different files depending on the version of Mac OS X (and Darwin, for that matter) that the machine is running and so set out to find the easiest, most straightforward way to check the OS version.
Of course, the hostinfo command shows you most of the juicy details one would need, but it’s not worth trying to parse it. sysctl lets you query various kernel states, including the OS release version:
sysctl -n kern.osrelease
Which will spit back something like the following (on Mac OS X 10.5.4, in this example):
9.4.0
This is the Darwin release number. To convert a darwin release version number to a Mac OS X version number just subtract 4 from the major revision (9, in this case) and then prepend the ’10.’ to the entire thing. So, 9.4.0 becomes 10.5.4.0.
You can also use uname -r to get the OS release version, but I’m going to stick with sysctl for now.
Of course, you can’t do a direct comparison, so you’ll want to compare either the major or the minor revision (the last release field is always zero, so it can be ignored). The easiest way to do that is to pipe the output from sysctl through the cut command.
The following will give you the major release number (again, 9, in this case):
sysctl -n kern.osrelease | cut -d . -f 1
It cuts the output of sysctl on the ‘.’ delimiter and returns the first field. We can return the second field (the minor revision; 4, in our example) like so:
sysctl -n kern.osrelease | cut -d . -f 2
In my script, I simply needed to test if the machine was running Tiger or earlier, or Leopard or newer. Here’s a quick example of getting that functionality:
#!/bin/bash
if [ `sysctl -n kern.osrelease | cut -d . -f 1` -lt 9 ]; then
echo "Tiger or earlier"
else
echo "Leopard or newer"
fi
If you’re doing something more advanced, it might be easier to set variables first:
darwinos_major=`sysctl -n kern.osrelease | cut -d . -f 1`
darwinos_minor=`sysctl -n kern.osrelease | cut -d . -f 2`
Then just reference $darwinos_major & $darwinos_minor whenever needed.
