Using arrays in the shell

This started as a post about shell arrays as means of maintaining filenames with spaces in them. Sadly, it fails at that. Still, compare two shell constructs:

$ egrep -li 'icon' $(find ~/src -type f -name '*.py' | egrep -v venv)

Against:

$ set -A python_files $(find ~/src -type f -name '*.py' | egrep -v venv)
$ for f in ${python_files[@]}; do egrep -li icon "${f}"; done

Note well that the second construct preserves whitespacing in filenames even if one where foolish enough to create a python program with whitespace in it’s filename.

I find myself doing the former a lot and there are a few places in my source code tree where there are embedded spaces in a directory. It’s probably time to retrain my finger memory to use the latter construct. This post comes because I find myself revisiting things occasionally. When people start with Unix, a common question is what’s the difference between $*, $@, "$*", and "$@"? This isn’t original work but here’s a shell script that answers the question.

$ cat foo.sh
#! /bin/bash

echo "## "'$*' -- expands elements
for arg in $*; do
    echo "\"${arg}\""
done
echo 

echo "## "'$@' -- expands elements
for arg in $@; do
    echo "\"${arg}\""
done
echo 

echo "## "'"$*"' -- all elements
for arg in "$*"; do
    echo "\"${arg}\""
done
echo 

echo "## "'"$@"'-- expands elements maintaining whitespace
for arg in "$@"; do
    echo "\"${arg}\""
done
echo 

exit 0

here’s what you get when you run the code:

$ /bin/bash foo.sh one two 'three four' five
## $* -- expands elements
"one"
"two"
"three"
"four"
"five"

## $@ -- expands elements
"one"
"two"
"three"
"four"
"five"

## "$*" -- all elements
"one two three four five"

## "$@"-- expands elements maintaining whitespace
"one"
"two"
"three four"
"five"

$ 

With arrays things are slightly different:

#! /bin/bash

## If Korn Shell -- set -A list "$@" also works

list=( "$@" )

echo "length: ........... ${#list[@]}"
echo "all elements: ..... ${list[@]}"
echo

for arg in "${list[@]}"; do
    echo "\"${arg}\""
done
echo

exit 0

And the output when you run it:

$ /bin/ksh foo.sh one two 'three four' five
length: ........... 4
all elements: ..... one two three four five

"one"
"two"
"three four"
"five"

$ /bin/bash foo.sh one two 'three four' five
length: ........... 4
all elements: ..... one two three four five

"one"
"two"
"three four"
"five"

(venv) $ output

As above, sadly once you pipe the output of this into another program, in this case egrep -v ... to throw away the python that was pulled into your virtual environment, the spacis seems to get lost. Maybe some python can fix this?