Quick Bash Trick: Looping through output lines

Lets say you want to add line numbers to a text file. For example, you want to see this file:

The quick brown fox
jumps over the
lazy dog

… displayed on your screen list this:

1  The quick brown fox
2  jumps over the
3  lazy dog

Of course, you could use cat -n if you’re using cat and want line numbers, but if you want to, say, add a 4 space indent to the output of iwconfig, the following technique will work.

First, we run the program / script and capture the output:

output=$(iwconfig 2>/dev/null)

Here we’re running iwconfig in a subshell and capturing the output. In this case, I’m redirecting stderr to /dev/null, otherwise iwconfig returns lines like lo no wireless extensions. up through the subshell to the shell thats calling the bash script. I don’t want this so I’m throwing it out.

Next, we loop through the output using a for loop:

for LINE in ${output} ; do

    echo ${LINE}

done

Running this will not get us the output we’re expecting, it will return each space-separated word/phrase on it’s own line. If we are still talking about the pangram above, we would see:

The
quick
brown
fox
jumps
over
the
lazy
dog

The problem is, the IFS (Internal Field Separator) contains the space character.

(Quick Aside)
To see what the IFS is, type:

echo -n "$IFS" | hexdump

My IFS contains 0×09, 0×20, 0×0a (tab, space and newline respectively).
(End Aside)

We can change the $IFS in our script (to only contain the newline character) which lets us iterate through the output where separated by newlines.

First, we save the original IFS and define the newline IFS:

OIFS="${IFS}"
NIFS=$'\n'

Next we set the IFS to our newline IFS:

IFS="${NIFS}"

Then, we loop through the output (in the loop we reset the IFS incase we expect it to include the space, etc. and then set it to newline IFS before our next iteration)

for LINE in ${output} ; do
    IFS="${OIFS}"

    echo "---- ${LINE}"

    IFS="${NIFS}"
done

Lastly, we reset the IFS:

IFS="${OIFS}"

The final code looks like this:

        output=$(iwconfig 2>/dev/null)

        OIFS="${IFS}"
        NIFS=$'\n'

        IFS="${NIFS}"

        for LINE in ${output} ; do
            IFS="${OIFS}"

            echo "---- ${LINE}"

            IFS="${NIFS}"
        done
        IFS="${OIFS}"

… and we get what we had hoped:

---- The quick brown fox
---- jumps over the
---- lazy dog

In closing, it should be noted that I’ve done this a few different ways (including splitting the output with awk / sed) but I like the simplicity of this method. I may try to find the other methods and write them up to if I get some time.

P.S. If you ever need to reset your IFS but don’t want to close the terminal, type this:

IFS=$'\n\t '
Posted Sunday, January 17th, 2010 under linux, programming, shell scripting.

One comment so far

  1. I’ve been looking everywhere for something like this. There are so many examples on the web that, for whatever reason, didn’t work for me. Thank You!!!

Leave a Reply

Powered by WP Hashcash