views:

1172

answers:

16

I constantly hear from other people about how much of the stuff they've used to customize their *nix setup they've shamelessly stolen from other people. So in that spirit, I'd like to start a place to share that stuff here on SO.

Here are the rules:

  1. DON'T POST YOUR ENTIRE DOTFILE. Instead, just show us the cool stuff.
  2. One recipe per answer
  3. You may, however, post multiple versions of your recipe in the same answer. For example, you may post a version that works for bash, a version that works for zsh, and a version that works for csh in the same answer.
  4. State what shells you know your recipe will work with in the answer.
  5. Let's build this cookbook as a team. If you find out that an answer works with other shells other than the one the author posted, edit it in. If you like an idea and rewrite it to work with another shell, edit the modified version in to the original post.
  6. Give credit where credit is due. If you got your idea from someone else, give them credit if possible.

And for those of you (justifiably) asking "Why do we need another one of these threads?":

  • Most of what I've seen is along the lines of "post your entire dotfile." Personally, I don't want to try to parse through a person's entire dotfile to figure out what I want. I just want to know about all the cool parts of it.
  • It's helpful to have a single dotfile thread. I think most of the stuff that works in bash will work in zsh and it may be adapted to work with csh fairly easily.
A: 

This is a pretty simple one, but I'm a newb with this stuff.

Works with: bash, zsh

This is just one line:

(bash)

viewglob

or

viewglob --shell-mode=bash

(zsh)

viewglob --shell-mode=zsh

If you have the viewglob program installed (may be done in Debian by running "apt-get install viewglob"), this will bring up a window that displays all the directories and files in the current directory.

If anyone knows how to make this not tell me "viewglob: This shell is already being monitored" on occassion, please feel free to edit it in.

Jason Baker
+7  A: 

Any post on dotfiles would be missing something without a link to dotfiles.org, a repository of such.

Have a browse there whenever you go to customize a new application.

Rich
+1  A: 

I have .profile that I use on machines with Bourne (mercifully few), Korn or Bash shells. It is carefully crafted to allow per-machine configuration. I don't use command files 'per shell'; what needs to be set is set in the environment, and shells inherit that environment.

# Set machine-specific environment
mc=`uname -n`
if [ -r $HOME/.$mc ]
then
    . $HOME/.$mc
fi
unset mc

Then I have some weird stuff that sets the PATH - not shown.

Next, there's the common stuff - all running scripts of varying degrees of complexity:

. libpath                                           # Set LD_LIBRARY_PATH
. ttyset                                            # Set STTY values
. kshrc                                             # Set KSH environment
...
. setprompt                                         # Set prompt
. manpath                                           # Set MANPATH

I regard the umask setting as important.

umask 022

# Set group-specific environment
group=`id | sed 's/.* gid=[0-9]*(\([^)]*\)).*/\1/'`
if [ -f "$REAL_HOME/.$group" ]
then
    . $REAL_HOME/.$group
fi

# Set user-specific environment -- assume LOGNAME or USER set OK
# Beware Linux: by default, username = group name so things get done twice!
: ${LOGNAME:=${USER:-jleffler}}
export LOGNAME
if [ "$group" != "$LOGNAME" ] && [ -f "$REAL_HOME/.$LOGNAME" ]
then
    . $REAL_HOME/.$LOGNAME
else
    cd
    case "$-" in
    *c*)    : OK;;
    *)              echo "User $LOGNAME logged in to `pwd` at `date`";;
    esac
    trap "clear; exit 0" 0
fi
unset group

The 'case "$-"' bit looks for shells run with '-c', I think -- I added it back in 1995, and don't recall the exact reason why (the checkin comment refers to a different change). It looks as though it is intended to ensure that non-interactive shells do not print the startup message. It works; I've never seen the message when I didn't expect it, but I'm not sure that it is a necessary test.

Note the 'unset' operations. Since this file is 'dotted', I regard it as important that it does not clutter up the shell with variables that are incidental. All of the dotted scripts are equally careful. Clearly, for example, the manpath script sets $MANPATH; it doesn't set anything else, though.

You may decide that this approach is too fussy -- that'd be a fair assessment. However, it has stood me in good stead over a vast array of machines (HP-UX, AIX, SunOS, Solaris, Linux, MacOS X, Cygwin, DEC OSF/1) over along time (RCS record goes back to 1989).

Jonathan Leffler
+2  A: 

I use ZSH because it has nice *-expansion (press tab to expand the file names and you can then edit the list) and **-expansion (easily handle e.g. **/*.pl i.e. all perl files down here).

1) Show hostname and/or running command and/or working directory in the title bar.

This works for both X11 xterm and Mac OS X Terminal and also works over remote ssh connections. Very convenient for keeping track of many shell windows on many servers, especially with Exposé on OS X.

Note: Echo and print are ZSH built-ins, so no external commands are run for this. Zsh variable expansion can be "interesting" but is powerful.

# Terminal title updating for Zsh by haa
# At prompt, display current path name
precmd () { 
    echo -n "\e]0;"
    # Enable on remote servers to show hostname:
    #print -Pn "%m: "

    print -Pn '%(!.# .)'        # Print # for root shell
    case "$PWD" in
    ??????????????????????????????????????????????????*)
        # Longer than 50, display "...endofpath"
        # Change nonprintable characters into '?'s
        echo -nE "...${(l:50:::::)PWD//[^[:print:]]/?}"
        ;;
    *)
        echo -nE "${PWD//[^[:print:]]/?}"
        ;;
    esac
    print -Pn " (%m)"
    echo -n "\007"
}
# When running, display commandline
preexec () { 
    echo -n "\e]0;"
    # Enable on remote servers to show hostname:
    #print -Pn "%m: "
    print -Pn '%(!.# .)'        # Print # for root shell
    case "$1" in
    ??????????????????????????????????????????????????*)
        # Longer than 50, display "commandlinestart..."
        echo -nE "${(r:50:::::)1//[^[:print:]]/?}..."
        ;;
    *)
        echo -nE "${1//[^[:print:]]/?}"
        ;;
    esac
    print -Pn " (%m)"
    echo -n "\007"
}

This is in .zshrc so it works for interactive sessions only.

2) Add alias -- add some command you just ran easily as an alias for future use. Find the command in history, add "addalias name " in front of it and you're done.

# AddAlias by haa
# addalias aliasname command with parameters you just ran
addalias(){
        echo -n "Comment> "
        read comment
        name="$1"
        shift
        echo "alias $name=\"$*\"        # $comment" >> $HOME/.zshenv
        alias $name="$*"
}

PS. I like my aliases in .zshenv (not .zshrc) so they work with commands run via ssh (non-interactive sessions).

haa
+1  A: 

Compiling all C++ sources in a directory

This is useful when you want to compile a C++ program without having to deal with makefiles.

ZSH

alias -g compile='g++ -Wall *.cpp -o '

Bash

alias compile='g++ -Wall *.cpp -o '

This is with almost all warnings enabled. Insert any other flags you so desire (and if anybody knows of any flags that should be in there, feel free to comment or edit them in).

Now suppose you have a directory with main.cpp, file.cpp, and any header files that are included. All you have to do is:

$ compile foo

And you now have an executable file named foo.

Jason Baker
have a look at Makefiles..
dalloliogm
+3  A: 

Make ls produce colored listings if supported. This code works on Linux and Mac OS X, for Zsh and Bash, probably also on other Unix/BSD platforms and with other sh-style shells.

if ls -F --color=auto >&/dev/null ; then
    alias ls='ls -F --color=auto'
else
    if ls -F -G >&/dev/null ; then
            alias ls='ls -F -G'
    fi
fi
x-way
A: 

Prune a set of empty directories

Works with: bash, zsh

function prunedir () { 
   find $* -type d -empty -print0 | xargs -0r rmdir -p ; 
}

Use with some caution!

Brent.Longborough
+1  A: 

Show colorized diff of CVS/SVN/GIT working copy files

If the colordiff utility is available provide handy aliases for quickly comparing local files against the repository version. Tested with Zsh

if which colordiff >&/dev/null ; then
function cvs-cdiff {
    a=''
    if [ -z "$0" -o -z "$1" ]; then
     a='.'
    fi
    cvs diff -ur HEAD $a $@ | colordiff
}
function svn-cdiff {
    a=''
    if [ -z "$0" -o -z "$1" ]; then
     a='.'
    fi
    svn diff -r HEAD $a $@ | colordiff
}
function git-cdiff {
    a=''
    if [ -z "$0" -o -z "$1" ]; then
     a='.'
    fi
    git diff $a $@ | colordiff
}
fi
x-way
+3  A: 

When I compile programs from source (rather than installing them through a package manager), I tend to install them into my home directory under ~/apps/appname. This makes it easy to get rid of applications I don't want anymore (or ones that finally have a real package) by simply saying rm -rf ~/apps/appname. However, this means I have a bunch of directories I need to add to my PATH. Instead of adding each bin directory by hand I use a simple for loop:

for dir in $(ls -d ~/apps/*/bin); do
    PATH="$dir:$PATH"
done
Chas. Owens
A: 

Two little functions that I use a lot. If you don't know ack, you should. It's like grep but more so.

# Use ack to search my history file; pipe to less if necessary
hack()
{
    var=$(history | ack $1 | wc -l)
    if (( $var > 22 ))
    then
        history | ack $1 | less
    else
        history | ack $1
    fi
}

# Start mpd (if necessary), search for music using mpc, play music.
mp-start()
{
    ps -C mpd > /dev/null || mpd
    mpc search any $1 | mpc add -
    mpc play
}
Telemachus
+5  A: 

I a litte function which creates an back of an file

bu () {
    cp $1 `basename $1`-`date +%Y%m%d%H%M`.backup ;
}

Function to extract all kinds of compressed files

extract () {
        if [ -f $1 ] ; then
                case $1 in
                        *.tar.bz2)        tar xjf $1                ;;
                        *.tar.gz)        tar xzf $1                ;;
                        *.bz2)                bunzip2 $1                ;;
                        *.rar)                rar x $1                ;;
                        *.gz)                gunzip $1                ;;
                        *.tar)                tar xf $1                ;;
                        *.tbz2)                tar xjf $1                ;;
                        *.tgz)                tar xzf $1                ;;
                        *.zip)                unzip $1                ;;
                        *.Z)                uncompress $1        ;;
                        *)                        echo "'$1' cannot be extracted via extract()" ;;
                esac
        else
                echo "'$1' is not a valid file"
        fi
}
optixx
+3  A: 

colorize Stderr in ZSH

exec 2>>(while read line; do print '\e[91m'${(q)line}'\e[0m' > /dev/tty; print -n $'\0'; done &)
optixx
A: 

Spice up the Python prompt (courtesy of Danny Colligan). Put this in your ~/.bashrc:

export PYTHONSTARTUP="$HOME/.pythonrc.py"

And put this in your ~/.pythonrc.py:

#!/usr/bin/python

import sys
import os
import atexit

# color prompt
sys.ps1 = '\033[1;36m>>> \033[0m'

# tab completion
# from http://www.doughellmann.com/PyMOTW/rlcompleter/index.html
try:
    import readline
except ImportError:
    # Silently ignore missing readline module
    pass
else:
    import rlcompleter
    readline.parse_and_bind("tab: complete")

# history
# from http://dotfiles.org/~remote/.pythonrc.py
histfile = os.path.join(os.environ["HOME"], ".python_history")
try:
    readline.read_history_file(histfile)
except IOError:
    pass

atexit.register(readline.write_history_file, histfile)
del os, histfile
Adam Rosenfield
+1  A: 

Here is one of my favorite zsh snippets, taken from a forum post on osxhints. This allows you to complete with no case sensitivity. It also completes parts of words, I really can't live without this now.

# Completions
autoload -U compinit
compinit -C

# case-insensitive (all),partial-word and then substring completion
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'
claytron
A: 

I use the same set of confs across all the machines I use. Pretty much every machine has subversion installed, so one day I decided to keep my confs in subversion. This was nice, but I would always forget to update them.

Here is a little function I wrote that let's me delay the svn up, so that it doesn't run for every shell I open (works in bash and zsh)

# used for various OS checks
UNAME=$(uname)

weShouldUpdate() {
    local time_to_update
    local touchy_file
    local last_mod
    local current_time
    local time_diff
    # only update once a day
    time_to_update=86400
    # use a dot file as a marker
    touchy_file="$HOME/.dotfiles_updated"
    # if the file doesn't exist we'll end up touching it
    last_mod=0
    if [ -e $touchy_file ]; then
        # stat is different between flavas
        if [ $UNAME = "Linux" ] || isiPhone; then
            last_mod=$(stat -c %Y "$touchy_file")
        elif [ $UNAME = "FreeBSD" ] || [ $UNAME = "Darwin" ]; then
            last_mod=$(stat -f %a "$touchy_file")
        fi
    fi
    current_time=$(date +%s)
    # see what the difference is in seconds
    time_diff=$(( $current_time - $last_mod ))
    if [ $time_diff -gt $time_to_update ]; then
        touch $touchy_file
        echo "It's been a while since we updated the dotfiles..."
        return 0
    fi
    return 1
}

Then I put this in my profile (I use a .commonprofile and source that from either .zprofile or .bash_profile). First it checks to see if subversion is available on the machine, then checks to see that the .dotfiles directory is a svn checkout then finally runs the function to see if it has been at least a day since the last update. If all those checks pass then the .dotfiles are updated!

# only update once a day, according to the weShouldUpdate function
# update the confs so they are in sync, but only if under
# subversion control and subversion is available
if checkPath svn && [ -d $HOME/.dotfiles/.svn ] && weShouldUpdate; then
    svn up $HOME/.dotfiles
fi

I'll post the checkPath function in another answer.

claytron
A: 

I have a set of confs that can be used with bash or zsh on FreeBSD, Linux, OS X and my iPhone. This requires a lot of conditions in my confs to make sure everything works as expected. One such check is to see if the given command is available in your $PATH.

Here is the function that I ended up writing.

# helper function to search the $PATH for a given
# executable.  useful for checks across different
# systems.
checkPath() {
    local execpath
    local OIFS
    # the bash compatible version of this
    OIFS=$IFS
    IFS=':'
    for dir in $PATH
    do 
        execpath="$dir/$1" 
        if [ -x $execpath ]; then
            IFS=$OIFS
            return 0
        fi
    done

    # set the file separator back to normal
    IFS=$OIFS

    # the zsh compatible version
    for dir in $path
    do 
        execpath="$dir/$1"
        if [ -x $execpath ]; then
            return 0
        fi
    done

    return 1
}

Here is an example usage that checks to see if subversion is available

$ checkPath svn && echo "subversion is available"

This works in both bash and zsh. It's not the most elegant solution but it has been working for me. I tried to use built in tools like which but was never successful. If anyone knows a cross platform and cross shell way of doing this better I'd like to know.

claytron