Custodian's note: this page is no longer maintained, and its contents have been absorbed into the current bbs de jour, linked here. This page will preserve the old versions and serve as a redirect..

This page contains information that I've found to be useful in my various computing experiences. Perhaps it will be helpful to you, as well :)

Topics:

Shells and shell scripting
loops
testing for arguments
dynamically updating terminal window title
zsh command prompts and aliases

General Unix stuff
recursive media whacking with wget
apache host-allow to restrict directories by client host
System stats with phpsysinfo

Mac OS X Specific stuff
SSH key management in Mac OS X
Saving and recalling Terminal.app sessions
Monitoring throughput with ifstat
Running Mac OS X services on alternate ports
Automating the use of background color to distinguish between terminal windows
Script to enable a dock pinning menu

Shells and Shell Scripting


For loops:
for file in `ls`
do
mv $file $file.copy
done

For loops with limited iterations:
i=0
for file in `ls`
do
let i=i+1
if [ $i -gt 10 ]
then
mv $file.copy
fi

If loops in bash:
if [ 1 = 1 ] ; then echo "yes" ; else echo "no" ; fi

Testing for existance of two command line arguments
if ( ! [ $1 ] || [ ! $2 ] ) ; then echo "yup" ; else echo "nope" ; fi
(and yeah, you could just test for $2, but part of the purpose here is also to demonstrate boolean operators in if statements...)

Kill all processes that match a specified argument using this short shell script called smack. Note: be sure you supply text that matches only what you want to kill. Also note that this is depreciated in 10.2, which has a 'killall' command.
#!/bin/sh
kill -9 `ps auxww | grep -i $1 | grep -v grep | grep -v smack | awk '{print $2}'`
Now just chmod +x smack and put it somewhere in your $PATH, then you can
andre@skoo[~]% smack Explorer
(mmm, that feels good ;)

With zsh, use the following in your ~/.zshrc to dynamically update the title of the term based on the current working directory: (note that you should substitute vt100 for whatever your contents of $TERM are)
case $TERM in
        vt100*)
        precmd () {print -Pn "\e]0;%n@%m: %~\a"}
        ;;
esac

Also with zsh, use the following in your .zshrc for a nifty prompt:
PS1="$(print '%{\e[;31m%}%n%{\e[;0m%}@%{\e[;30m%}%m%{\e[0;37m%}[%~]%{\e[0m%}% ')"

zsh aliases in .zshrc. the following would open an ssh session to yak, and set the window title accordingly
alias yak="yak -Pn \"\e]0;.o0 yak 0o.\a\" ; ssh yak"

General Unix Stuff


To tar a directory:
tar -cvf file.tar directory

wget media whacking

Having trouble whacking the media out of those pesky cgi driven pages? Try this tip for using wget to parse links out of a html source file. Using a web browser, open the page which contains links to media files you want to download in its own window (this will alleviate problems with frame based sites). Save the source to a file. Then execute the following:
wget -i source.html -F
If the links in the source are relative and not absolute, you'll also need to use the -B switch to specify the base URL for the files (i.e. the part of the URL up to the path or filename provided in the source).

If you've got a whole bunch of these files, put them in a directory, make sure they're named *.* (i.e. only one period in the filename which seperates the artist/song name from the extension), then use this little script to do the dirty work for ya:
#!/bin/sh
for file in `ls`
do
dir=`ls $file | awk -F "." '{print $1}'`
echo "now processing in $dir"
mkdir "$dir"
cd "$dir"
wget -F -i ../$file
cd ..
done

To provide host-allow protection on an URL (regex savy), into the main config file for apache 1.3 or later, append the following lines:

Order Deny,Allow
Deny from all
Allow from .tribal.org
Allow from .blackmagiclabs.com
Allow from .blackmagiclabs.org
Allow from .blackmagiclabs.net

phpSysInfo provides an attractive web based display of system statistics. Obviously, it is php based - if you don't already have it set up, follow the instructions at this page. It worked flawlessly for me in 10.1.3. Once you've got php set up, decompress the phpSysInfo source and put the directory in your document root (/Library/WebServer/Documents on os x). You might also consider renaming the phpSysInfo-2.0 directory to "stats" for ease of access. Comment out lines 78 - 86 of includes/tables/hardware.php to remove the columns for the non-existant pci, scsi, and ide information (non-existant for me, ymmv). Also, to keep from displaying stats on filesystems you really don't care about, make the following changes in the filesystems function of os/class.BSD.common.inc.php:

Change line 250 to: $df = execute_program('df', '-k -t hfs');
Change line 254 to: $s = execute_program('mount', '-t hfs');

Of course, if you want to display filesystems of types other than hfs (appelshare mounts, or ufs file systems), then you probably should just go ahead and not make the above edits... You can also change the default template that is used by changing "classic" on lines 66 and 67 of index.php to whichever template you want as default.

Mac OS X Specific Stuff


Mac OS X ssh key management (for mortals)

Open a terminal and execute the following:
ssh-keygen -t rsa

Accept the default filename (id_rsa.pub) and press return twice (no passphrase)

You now have two new files in ~/.ssh: your public and private keys. The private key is called id_rsa, and the public key is id_rsa.pub. Keep your private key file safe!! If it were to fall into the wrong hands, nefarious characters might be able to perpetrate as you, logging in to your remote accounts with narry a password.

In order to enable password-less logins, copy id_rsa.pub from your workstation to ~/.ssh/authorized_keys2 on the remote machine.

Note that this is for (I believe) ssh version 2. If this is not what's running on your remote account, try ssh-keygen with no arguments, and copy the resulting identity.pub to ~/.ssh/authorized_keys on the remote machine. Also note that either authorized_keys or authorized_keys2 may contain multiple entries; allowing password-less logins from multiple hosts; just be sure not to muck up the contents of either file with line breaks; I recommend vi or even just redirection with appending (upload your public key to the remote box, then cat id_rsa.pub >> ~/.ssh/authorized_keys2) in order to add additional host key entries.

That's it!

To get Mac OSX's lookupd to consult /etc/hosts before the netinfo db (depreciated in 10.2): as root:
niutil -create . /locations/lookupd/hosts
niutil -createprop . /locations/lookupd/hosts LookupOrder FFAgent DNSAgent

Terminal transparency in Mac OS X (depreciated in 10.2): from a shell, execute:
defaults write com.apple.Terminal TerminalOpaqueness 0.8

identd in Mac OS X (depreciated in 10.2) - in /etc/inetd.conf (comment out the other similiar line)
auth stream tcp wait root /usr/libexec/tcpd identd -w -t120

To save configurations of Terminal.app windows:

Arrange your windows to your liking. From the Shell menu, choose Save As, choose "All Windows", click the checkbox and save the term file in ~/Library/Application Support. Now when Terminal lanuches, it will open your windows in the exact same configuration. In order to have a terminal execute a command when it opens, edit the term file you saved. The ExecutionString string gets executed when the terminal opens. For example,
ssh -t islandless.net screen -x -p 0
will cause the terminal to open an ssh session to islandless.net, re-attach to a running screen, and switch to window 0.

Displaying Network Interface Utilization in your BitchX status bar

This one does involve compiling some source code (so you'll need dev tools). Following these instructions will allow you to monitor your network bandwidth in your bitchx status bar. It looks a little somethin' like this: (note the 0.26 KB/s in, 0.37 KB/s out)



A few things are involved here. You need to download and compile a nice lightweight tool called ifstat which polls the kernel at regular intervals to obtain network utilization data. Then you need to do a little bit of BitchX scripting to get it displayed inside BitchX.

First, download the
source from freshmeat. We need to make a quick mod to some of the source to get it to function properly in Mac OS X. Decompress and untar the source directory, then find the file called drivers.c. Add the following lines directly after #ifdef USE_KVM (should be around line 143 ; If you're not on Mac OS X / darwin, then you probably don't need this bit.):
#ifdef __APPLE__
/* fast fix for darwin */
#define if_list if_link
#endif
Then, ./configure --without-ifmib --with-kvm and make like normal. If completed properly, you'll end up with an ifstat binary which, when invoked with no options, will produce output every second to STDOUT that displays network traffic. Go ahead and try to run it now; chances are good that you'll get a permissions error:
andre@skoo[~]% ifstat
ifstat: /dev/mem: Permission denied
If you have this error (and you will if you're in darwin / Mac OS X), then do the following as root:
chmod 2755 ifstat ; chgrp kmem ifstat
This changes the permissions on the ifstat binary so that other users can use the ifstat program, just not alter / delete it, and also sets the set-group-ID-on-execution bit. ifstat will run as group kmem, which has access to /dev/mem. Now you should probably go ahead and put ifstat in /usr/local/bin.

We're ready for the BitchX scripting bit. First thing you need to do is start maintaining a .bitchxrc file, if you're not already. It goes in your home directory and is the primary method for loading scripts and other config info into BitchX; it's read by BitchX when launched. In this file we'll put the following few lines of BitchX script.
exec -name ifstat_proc ifstat 10
on ^exec "%ifstat_proc *" {
	wset * status_format2 %L %! %K %>%D %J  $1 KB/s in, $2 KB/s out
}
Note that for this example to work, "ifstat" must be in the system path (the directories returned by echo $PATH); if it's not, just replace "ifstat" with the full path to the ifstat binary. Now just launch BitchX, and you should see the status bar begin to update. If you don't see it, try using the /window triple toggle command in BitchX. This type of output is quite handy on unix / linux routers :) For those of you who like to use ifstat by itself, note PelDaddy's patch for single-line, non-scrolling output.

Running Mac OS X 10.2 ftp server on alternate ports

We get this about once every couple weeks in #macosx, so I figured it's worth documenting... There's two steps: 1) add an entry in netinfo for the new service with corresponding port, and 2) add an entry in /etc/xinetd.d for the new service. This involves executing commands as root; we'll use the sudo command for this. The first time you use sudo, you will be prompted for your password. This is normal :)

In this example, I will create an ftp server on port 4242. I'll be using the name "ftp2" throughout... you can use something different, but it must not already exist (ls /etc/xinetd.d/ to get a list of existing services).

Step 1) add entry to netinfo:
andre@skoo[~]echo 'ftp2 4242/tcp' | sudo niload -m services .
andre@skoo[~]nidump services .
ftp2    4242/tcp
Step 2) add entry to /etc/xinetd.d
andre@skoo[~]sudo -s
root@skoo[~]zsh
root@skoo[~]x=`cat /etc/xinetd.d/ftp | sed 's/service ftp/service ftp2/'` \
> echo $x > /etc/xinetd.d/ftp2
root@skoo[~]cat /etc/xinetd.d/ftp2
service ftp2
{
        disable = no
        socket_type     = stream
        wait            = no
        user            = root
        server          = /usr/libexec/ftpd
        server_args     = -l
        groups          = yes
        flags           = REUSE
}
root@skoo[~]exit
root@skoo[~]exit
andre@skoo[~]
And finally, before any of these changes will take effect, you need to tell the xinetd process to re-read the contents of /etc/xinetd.d by sending it a SIGHUP:
andre@skoo[~]sudo kill -HUP `ps aux | grep xinetd | grep -v grep | awk '{print $2}'`

Here's the breakdown: We're adding a service that we'll call "ftp2", in order to distinguish from the existing "ftp" service. When we load this into netinfo, we're including the port and protocol with which this service is associated; namely tcp port 4242. Once that is done, we did a nidump to verify the contents of the netinfo /services diretory (note that it only shows the one we've added; this is normal). Next, we need to tell the system what to do upon receiving an incoming request on tcp 4242. We've created a duplicate of the original ftp xinetd file with the name ftp2, and we also changed the service name inside that file from ftp to ftp2 (then we used the cat command to dump the contents of the new file; make sure yours looks like the one above). Incidentally, we executed "zsh", which invokes the z shell; this ensures that the following shell command will work properly. The /etc/xinetd.d/ftp2 file tells the system which application to fire up when it receives a connection on port 4242, and controls some other parameters of the service. One important note here is that the name of the service as added in step 1 has to match the name of the file in /etc/xinetd.d that is used to control the service, and also the name of the service as defined on the first line of that file.

In order to remove these modifications, there's a couple options. To disable a service, you can edit the corresponding file in /etc/xinetd.d and simply set the disable bit to "yes". This way you can easily re-enable the service by re-editing the file. Alternatively, you could remove the file you added in /etc/xinetd.d, and also the entry in netinfo:
andre@skoo[~]sudo rm /etc/xinetd.d/ftp2
andre@skoo[~]sudo niutil -destroy / /services/ftp2

Using background color to distinguish between terminal windows using shell profile and applescript

So there I was, staring at a field of minimized terminal window icons, thinking to myself "I need to find an easy way to distinguish between sets of terminal windows". It makes the most sense to me to group them by the host to which I am connected, but you can use these methods for your own nefarious purposes :)

First step is to build a shortcut for sshing into a machine and stick it in your shell config file (in my case, ~/.zshrc, but this varies depending on which shell you use. I'll leave it up to you to adapt this stuff to your shell):
alias core="ssh core.dreness.com"
This will enable you to simply type "core" and then ssh to that host. Next we need to figure out which color to use for the background. The terminal.app applescript dictionary says that the background color value is a text string, such as "black", "gray", etc... but that just won't do, since most of the 8 or 9 colors that can be described that way are fairly useless as background colors. Even though it's possible to manually specify the background color using the color picker "Terminal menu --> Window Settings...", I had a hard time figuring out how to programmatically articulate non-standard colors. Turns out that an easy way to figure out how applescript and terminal.app describe the colors is to simply ask it:
tell application "Terminal" to set x to the background color of the front window
The result window will be populated with a three or four item list (depending on whether or not there's any alpha transparency) which can be used to describe colors back to terminal.app via applescript. For example:
tell application "Terminal" to set the background color of the front window to {32767, 29478, 27360}
At this point, we're able to gather the numerical representation of the colors we want by simply using the color picker to choose a color, then using applescript to get the values of that color. This is where it got a bit sticky for me, as I can't seem to figure out how to embed a one-line applescript (using osascript) into a shell alias... the applescript command uses double quotes which I cannot seem to escape properly. My workaround is to create a single shell script for each terminal color, and then to invoke that script in the shell alias for a specific host. For example, in ~/bin/term.core, I have the following:
echo "tell application \"Terminal\" to set the background color \
of the front window to {28300, 32767, 26494}" | osascript
The modified shell alias file would look like this:
alias core="~/bin/term.core ; ssh core.dreness.com"
... but why not take it one step further and have it change back to the default color after you're through?
alias core="~/bin/term.core ; ssh core.dreness.com ; ~/bin/term.default"
... but while we're at it, we might as well add some terminal window titles too...
alias core="print -Pn \"\e]0;core:\a\" ; ~/bin/term.core ; ssh dreness.com ; ~/bin/term.default"






Get it? Good :)

Enabling Dock Pinning can be accomplished in a great variety of ways. One way is to manually edit /System/Library/CoreServices/Dock.app/Contents/Resources/English.lproj/DockMenus.plist to include the following:
<dict>
    <key>command</key>
    <integer>1014</integer>
    <key>name</key>
    <string>pinning</string>
    <key>sub</key>
    <array>
        <dict>
            <key>command</key>
            <integer>1020</integer>
            <key>name</key>
            <string>Start</string>
        </dict>
        <dict>
            <key>command</key>
            <integer>1021</integer>
            <key>name</key>
            <string>Middle</string>
        </dict>
        <dict>
            <key>command</key>
            <integer>1022</integer>
            <key>name</key>
            <string>End</string>
        </dict>
    </array>
</dict>

Another way is to use a script I wrote to automate the process. It comes in two parts. First is the main shell script,
dockPin. Then there is also the sed command file, sed.dockPin, which is used by dockPin. To use these scripts, download them both to the same directory, chmod +x dockPin, then sudo ./dockPin. It needs to run as root so it can modify the DockMenus.plist file; it will also make a backup of the original so that you may revert if desired. Once the script has been run, control-click on the Dock divider to access the updated menu.

These scripts also demonstrate one method of inserting multiple lines with sed. Took me about 1.5 hours to figure out how to do that. Never again :) Now, you may be thinking "dude, this takes like 15 seconds to do by hand... why did you spend two hours making a shell script?". Well, under normal circumstances, I wouldn't, but this will be rolled into a custom package I made for myself in the form of a post-install script. The installer sets up a customized environment from a fresh install. I'll post notes on that eventually.


dre@mac.com

Incidentally, this information is provided "as is", with no warranty, expressed or implied, regarding accuracy or suitability for a specific purpose. I take no responsibility for what happens to you or your loved ones from the use or misuse of the information contained herein :)