BASH assigning command output into an array

Often find myself needing to do this but never remember how…

# flirble=(`ls -ltr ssh* | tail -3 | tr -s ' ' | cut -d ' ' -f 9` )
# echo ${flirble[*]}
ssh_host_key ssh_host_dsa_key.pub ssh_host_dsa_key
# echo ${flirble[0]}
ssh_host_key
# echo ${flirble[1]}
ssh_host_dsa_key.pub
# echo ${flirble[2]}
ssh_host_dsa_key
# echo ${flirble[3]}

# ls -ltr ssh* | tail -3
-rw-------  1 robin cm-users 539 Aug 11 12:40 ssh_host_key
-rw-r--r--  1 robin cm-users 614 Aug 11 12:40 ssh_host_dsa_key.pub
-rw-------  1 robin cm-users 668 Aug 11 12:40 ssh_host_dsa_key

This and more useful array manipulation can be found in Chapter 26 of the Advanced Bash Scripting Guide.

Where am I?

If you are writing a bash script and you need to know where you are you can use this:

#!/bin/sh

# always prints out the directory in which this script is lives
# no matter where it is run from

# doesnt cope with ../bin/script.sh, which should be ./script.sh anyway

bn=`basename $0`;
echo $0 | grep "^/" > /dev/null 2>&1

if [ $? -eq 1 ]; then
 echo $0 | grep "^\.\/$bn" > /dev/null 2>&1
 if [ $? -eq 1 ]; then
   echo case 1 - relative ref
   here=`dirname $PWD/$0 | sed 's/\.\///'`
 else
   echo case 2 - local ref
   here=$PWD
 fi
else
 echo case 3 - root ref
 here=`dirname $0`
fi

echo script home is $here

Thanks to Justin for this

return codes

When running a command in bash it will store the return code in the special variable $?, like this:

[robin@book robin]$ ls -ld tmp
drwxr-xr-x  3 robin  robin  1024 Aug  3 11:02 tmp
[robin@book robin]$ echo $?
0
[robin@book robin]$ ls -ld bob
ls: bob: No such file or directory
[robin@book robin]$ echo $?
1
[robin@book robin]$

When running a series of commands in a pipe however it is sometimes necerssary to find the return code of an individual command in the pipe, in this case bash stores the return codes in an array names $PIPESTATUS which you can access like any other bash array. The array can only be used once however, so if you want to use it more than once store it in some other temporary array.

[robin@book robin]$ echo "tmp"  | xargs ls -ld
drwxr-xr-x  3 robin  robin  1024 Aug  3 11:02 tmp
[robin@book robin]$ echo ${PIPESTATUS[@]}
0 0
[robin@book robin]$ echo "bob"  | xargs ls -ld
ls: bob: No such file or directory
[robin@book robin]$ echo ${PIPESTATUS[@]}
0 1
[robin@book robin]$ echo "bob"  | xargs ls -ld
ls: bob: No such file or directory
[robin@book robin]$ echo ${PIPESTATUS[1]}
1
[robin@book robin]$

Reading text files in bash

Reading a text file into bash with a for loop can produce unexpected results if a line in the text file has spaces in it.

If you had a text file with this line in it:

d/NON STUDIO/NON STUDIO105/Cavanaugh_M0228867.jpg

And you were to read it with this simple script:

for i in `cat image.list `;
do
echo $i
done

You would get the following output which is probably not what you wanted:

STUDIO105/Cavanaugh_M0228867.jpg
d/NON

If you use while instead of for and unstset the IFS like this:

while IFS= read line
do
: do whatever with $line
done < FILENAME

You will get the desired output of:

d/NON STUDIO105/Cavanaugh_M0228867.jpg