Redirection reminders

# stdout to file
$ foo  > file
# stderr to file
$ foo 2> file
# stderr+stdout to stdout (optionally piped through tee)
$ foo 2>&1 | tee all-output.txt
# stderr+stdout to file
$ foo  > file 2>&1
# NON-STANDARD shorthand for merging standard error and standard output
$ foo  >& file # NOT POSIX
# stdout to file, stderr to different file
$ foo  > file 2> errors
# output to stderr
$ echo foo 1>&2
# convenience function to output to stderr:
echoerr() { echo "$@" 1>&2; }

# stderr to stdout, but stdout to /dev/null, and pipe stderr (http://stackoverflow.com/a/2342841)
$ foo 2>&1 >/dev/null | grep 'something'

Default value for a variable

echo "-------------------------------------------------------------------------"
echo "Using ':=', 'one' and 'two' are empty, both end up with the default value"
unset one
unset two
one="${two:=default value}"
echo "one=$one"                    # one=default value
echo "two=$two"                    # two=default value
echo "-------------------------------------------------------------------------"
echo "Using ':-', 'one' and 'two' are empty, 'one' ends up with the default value, 'two' remains empty"
unset one
unset two
one="${two:-default value}"
echo "one=$one"                    # one=default value
echo "two=$two"                    # two=
echo "-------------------------------------------------------------------------"
echo "Using ':=', 'one' is empty, 'two' has a value, both end up with the value in 'two'"
unset one
two='two'
one="${two:=default value}"
echo "one=$one"                    # one=two
echo "two=$two"                    # two=two
echo "-------------------------------------------------------------------------"
echo "Using ':-', 'one' is empty, 'two' has a value, both end up with the value in 'two'"
unset one
two='two'
one="${two:-default value}"
echo "one=$one"                    # one=two
echo "two=$two"                    # two=two

The variable doesn’t have to be assigned. Just echoing, or using the : (which does nothing, like a comment, but still executes it, unlike a comment) is enough to be set to the default variable:

echo "Using program from ${program:-$HOME/local/program/bin}"
echo $program # Empty! The above printed the default, but the variable is not set

echo ${variable:=default value} # default value
echo $variable                  # default value
# This was set up correctly

More reading:

Splitting a string without bash arrays

The original solution comes from Stack Overflow, but I don’t like much the disguised if-else nesting short circuit:

var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
while [ "$var" ] ;do
    iter=${var%%;*}
    echo "> [$iter]"
    [ "$var" = "$iter" ] && \
        var='' || \
        var="${var#*;}"
done

Reworking it a bit to my taste:

INPUT="foo bar baz"
while [ "$INPUT" ]; do
    item=${INPUT%% *}
    echo "working on $item"
    if [ "$INPUT" = "$item" ]; then
        INPUT='' # done
    else
        INPUT="${INPUT#* }" # trim variable
    fi
done

Quotes, empty spaces, and other junk

Recommended:

if [ "$FOO" = "foo" ] ; then
    echo "expected output"
fi
Note
  • Quotes around the variable protect it from spaces, and empty strings.

  • Space before the equals makes it a comparison instead of an assignment.

  • Spaces around the brackets to make them the proper /usr/bin/[.

This is a fail:

FOO='' # empty string
[ -f $FOO ] && echo true
# Prints true, but there is no file with empty file name...
Caution

This is old fashioned, and has security risks:

if [ x$VARIABLE = x ] ; then
    echo "Empty variable \$VARIABLE"
fi

A counter

Recursively list files by time

find -type f -printf '%T+\t%p\n' | sort -n

Create a bunch of files

for i in `seq -w 0 999` ; do dd if=/dev/zero of=./file_$i.zr bs=4096 count=10 ; done