Tag Archives: shell

bash: for-loop with glob patterns

It is common to use a for-loop with glob patterns:

for i in *.txt; do
    mv $i $i.old
done

But if the glob pattern does not match anything it will be preserved unchanged in the command. This results in command execution of mv *.txt *.txt.old which fails because no file named *.txt (literally!) exists.

As this is not the desired behavior, here is a way how to do this as expected without forking using the nullglob bash shell option.

oldnullglob=$(shopt -p nullglob)
shopt -s nullglob

for i in *.txt; do
    mv $i $i.old
done

eval "$oldnullglob" 2>/dev/null
unset oldnullglob

This will silently prevent the execution of the mv command. If you use failglob instead of nullglob bash will interrupt the evaluation of any command if the glob pattern did not match anything.

Disclaimer: Be careful with this option, as this will not be the expected behavior in all cases. Most (in)famously it breaks bash-completion if you set it in your interactive bash session. I suggest to use it temporary only.

pastie.org shell script

I wrote a bash script to create new pastes on pastie.org. It features automatic source language selection based on the file extension and has a switch to make a private paste.

$ pastie --help
Usage: pastie [options] [files...]

Options:
    -h, --help          display this help
    -l, --lang    set language of the paste
    -p, --private       make paste private

If --lang is not specified, this script will try to determine the type of each
file automatically based on the extension. If no files are given on the
command line it reads from standard input.

You can download it here:
http://pastie.org/904797

The script is public domain, so do whatever you want with it.

Although this is the initial release, I bumped the version number to 1.6 already. During testing the script I pasted itself several times to pastie.org. I set a arbitrary higher version number to avoid confusion in case the previous pastes ever turn up in Google or wherever.

Update:
Seems like their parser for shell is a bit broken and doubles the heredoc starting and ending sequences in the output. For whatever reason it appears as “<<END<<END”. Please use the raw version for download instead of copy & paste.

Calculating sum of numbers in the shell

More than one time I wanted to sum up numbers in the shell.

Imagine a command producing a list of numbers like this:


$ ...
42
23
966
1764
529
4711

Now calculating the sum of these numbers is not trivial. Using tools like bc or calc directly is not possible because you would need to put plus signs between the numbers first. So you could probably replace all ‘\n’ with ‘+’ with tr — but not the last one as that would lead to a syntax error later. Or as an alternative you could write a long shell construct with while read ... etc. In short this is just getting way too complicated for such a simple task.

Here is the simple solution in awk:


$ ... | awk 'BEGIN {total = 0} {total += $1} END {print total}'
8035

Hiding MOTD on bash startup

Usually bash displays /etc/motd at the time of last login on opening a new shell. I do not find that information very useful, as I am opening new shells a lot, so the time does not really mean anything for me. Also, the MOTD rarely changes.

There is a feature in bash to hide these messages by creating a file ~/.hushlogin. This will make bash jump right to the first prompt without any output before.

But just in case the MOTD changes and includes important messages, I enhanced this setup a little bit. Instead of just touching the .hushlogin file, I am storing the old MOTD in it. At startup the old and current MOTD is compared against each other and will be displayed only if it differs. To avoid accidentally missing the MOTD, bash will also ask for my confirmation that I have read it.

Here is the snippet from my ~/.bashrc:

# Show motd only if necessary
cmp -s $HOME/.hushlogin /etc/motd
if [ $? != 0 ]; then
    echo -e "\n==> !!! IMPORTANT: /etc/motd changed !!! <==\n"
    cat /etc/motd
    echo -e "\n==> !!! IMPORTANT: /etc/motd changed !!! <==\n"
    read -e -n 1 -p "Show again? (Y/n) " ans
    if [ "$ans" == "n" ]; then
        cat /etc/motd > $HOME/.hushlogin
    fi
fi

Please note: If you are going to use this, make sure it will not get executed on non-interactive shells. Otherwise it can break tools like scp, sftp or rsync. To ensure this will not be used on non-interactive shells, I am using this conditional at the top of my ~/.bashrc:

if [[ $- != *i* ]] ; then
    # Shell is non-interactive.  Be done now!
    return
fi

Subversion diff commands

Subversion allows to use a custom command for displaying diffs using svn diff --diff-cmd <cmd>. I have been using diff-cmd=colordiff in my ~/.subversion/config for quite some time now. This is really useful, but occasionally I would also like to use vimdiff to get a nice side-by-side diff.

Although this sounds quite easy at first, there are some hurdles. Subversion expects the given command to adhere to the GNU diff parameters, that means it expects it to understand and parse the labels it passes before the actual filenames. This works fine for colordiff, but not vimdiff which only wants the old and new filenames.

For a better diff experience with svn, I set up the following shell function which let’s me choose the program I want to use for diffing.

First, I need a new wrapper script at ~/libexec/svndiff which takes the actual diff program as first option, ignores the GNU diff labels and calls that program passing old and new filename.

#!/bin/bash
BIN=$1
shift 5
$BIN "$@"

Then I define this shell alias in my ~/.bashrc to extend the functionality of the svn command:

function svn() {
    case "$1" in
        diff-plain)
            shift;
            `which svn` diff --diff-cmd diff $@
            ;;
        diff-color)
            shift;
            `which svn` diff --diff-cmd colordiff $@
            ;;
        diff-vim)
            shift;
            `which svn` diff --diff-cmd $HOME/libexec/svndiff -x vimdiff $@
            ;;
        diff-filemerge)
            shift;
            `which svn` diff --diff-cmd $HOME/libexec/svndiff -x opendiff $@
            ;;
        *)
            `which svn` $@
            ;;
    esac
}

Now I can choose the program I find best suited for the current task in a very simplified manner, for example svn diff-vim -c1337.