How to Prepend a Number to Each Line Using AWK and Bash (Without Nested Loops)
When preparing datasets or tabular text files in Unix/Linux environments, a common task is prepending a unique value or sequence number to the beginning of each line. However, combining Bash loops with text-processing tools like awk often leads to unexpected Cartesian products (each value applied to every line) rather than line-by-line pairing.
The Problem: Why Nested Bash Loops Duplicate Lines
Consider the original code attempt:
for b in 0.1 0.15; do cat Edata | awk '{print '$b', $0;}'; doneIn this loop, Bash runs the entire cat Edata | awk ... pipeline once for 0.1 (printing every line in the file), and then runs it completely again for 0.15. Instead of updating line 1 with 0.1 and line 2 with 0.15, the whole file is processed repeatedly.
Here are several cleaner, faster, and more idiomatic ways to map one list of values to corresponding lines in a file.
Solution 1: Use paste (The Simplest Approach)
If you have an array or list of values to merge line-by-line with an existing file, the standard Unix utility paste is the most efficient choice.
paste -d ' ' <(printf "%s\n" 0.1 0.15) EdataHow it works:
<(printf "%s\n" 0.1 0.15)creates a temporary stream containing each number on a new line.paste -d ' 'merges the two streams side-by-side using a single space as the delimiter.
Solution 2: Pure awk with an Array
If you prefer an all-in-one awk command, you can pass your values directly into awk as an array and match each element to the current line number (FNR or NR):
awk 'BEGIN { split("0.1 0.15", nums) } { print nums[NR], $0 }' EdataHow it works:
- The
BEGINblock runs before processing the file and splits the string of numbers into thenumsarray. - For each line read from
Edata,NRrepresents the current line number.nums[NR]fetches the corresponding value and prints it before$0(the original line).
Solution 3: Processing Two Separate Files with awk
If your prefixes are stored in another file (e.g., numbers.txt), you can combine both files using awk without loading entire datasets into memory:
awk 'NR==FNR { a[NR]=$0; next } { print a[FNR], $0 }' numbers.txt EdataHow it works:
NR==FNRevaluates to true only while reading the first file (numbers.txt), storing each line in arraya.nextskips to the next line without executing the rest of the script.- Once reading
Edatabegins,FNRresets to 1, andprint a[FNR], $0outputs the matching line fromnumbers.txtfollowed by the line fromEdata.
Solution 4: Read Line-by-Line with a Bash while Loop
If you are already inside a Bash script and have an array of prefixes, you can step through the file line-by-line:
prefixes=(0.1 0.15)
i=0
while IFS= read -r line; do
printf "%s %s\n" "${prefixes[i]}" "$line"
((i++))
done < EdataSummary
Avoid executing awk inside a for loop over an entire file. For quick command-line operations, paste offers the shortest and fastest syntax. If more complex transformation logic is needed, use awk with split() or multi-file processing using NR==FNR.