OVERHEAD

Line-by-line parsing and indexing in POSIX shell

Scripting

Before we begin, we’ll assign a variable with a multi-line value for demonstration purposes:

POSIX shell example of a variable named $multiline being assigned a value containing newline (\n) characters in it.

multiline="$(printf "%b" "this is a line\nthis is also a line\nyep, still a line\nokay, last line.")"

Here’s how to isolate any arbitrary line from $multiline (where $counter can be any non-zero positive integer)…

POSIX shell sample showing how to use the sed command to print a specific line.

line="$(printf "%b" "$multiline" | sed "${counter}p;d")"

And finally, here’s how to write a while-loop that parses each line and prints that line’s index alongside it…

POSIX shell script which uses a while-loop, and the previously shown sed command, to prefix each line with its index.

#!/bin/sh

multiline="$(printf "%b" "this is a line\nthis is also a line\nyep, still a line\nokay, last line.")"

max="$(printf "%b\n" "$multiline" | wc -l)"
index="1"
while [ "$index" -le "$max" ]; do
	line="$(printf "%b" "$multiline" | sed "${index}p;d")"

	printf "%d: %s\n" "$index" "$line"

	index="$(( "$index" + 1 ))"
done

The while-loop will print empty lines with their index too, so you’ll need to check if the value of $line is non-empty before printing it if you want to skip empty lines.

The wc -l command counts the number of newline characters in a string, and not necessarily the number of lines, so a string like test\nline would be counted as one “line” and not two. Thankfully, the above script works regardless of if the value of $multiline ends with a newline character or not (and it ignores extraneous newlines at the end… for some reason).