There are better ways to sort than to do it purely in bash. This is not a good answer to your question -- it's not simple (because it uses several features of bash that aren't common-place), and it doesn't do things "The Unix Way", which is to use tools that are pre-built for doing one thing and doing it well (such as sorting).
I decided to write this Answer up to help make a larger point that your account's default shell is built to run commands and redirect I/O. Just because a shell has a multitude of features, like Bash does doesn't mean it's the best tool for a particular job. You'll very often see answers here that suggest using awk or perl (or jq or sort ...) instead of trying to hack it into a shell-only script.
That being said, bash can sort -- it's just not built-in. I'll repeat myself: it's still not a good idea. But you can do it. Below are four functions, implemented in bash, that sort two different ways on each of the two fields.
The functions use:
The insertion sort is not efficient (O(n)2), but certainly reasonable for small datasets, such as the 11-line example. The four functions ran in sub-second time for the sample data, but for a randomly-generated 1,000 line input file, the "separate array" sorts took ~15 seconds while the "in-place" versions took ~60 seconds because of all of the re-processing of the values. Compare this to the standard sort utility which sorted the 1,000 line file on either column in sub-thousandths-of-a-second time.
The two "inplace" functions attempt to save a few bytes by creating only one array (and some one-off variables for looping and swapping values); on the plus side, it uses a neat bash function to map file contents into arrays. The "keyed" functions throw caution to the wind and create two separate arrays, one for the desired keys to sort on and the other of the actual values.
function sort_inplace_f1 {
local array
mapfile -t array < "$1"
local i j tmp
for ((i=0; i <= ${#array[@]} - 2; i++))
do
for ((j=i + 1; j <= ${#array[@]} - 1; j++))
do
local ivalue jvalue
[[ ${array[i]} =~ ([^[:space:]]+)[[:space:]]+(.*) ]]
ivalue="${BASH_REMATCH[1]}"
[[ ${array[j]} =~ ([^[:space:]]+)[[:space:]]+(.*) ]]
jvalue=${BASH_REMATCH[1]}
if [[ $ivalue > $jvalue ]]
then
tmp=${array[i]}
array[i]=${array[j]}
array[j]=$tmp
fi
done
done
printf "%s\n" "${array[@]}"
}
function sort_inplace_f2 {
local array
mapfile -t array < "$1"
local i j tmp
for ((i=0; i <= ${#array[@]} - 2; i++))
do
for ((j=i + 1; j <= ${#array[@]} - 1; j++))
do
local ivalue jvalue
[[ ${array[i]} =~ ([^[:space:]]+)[[:space:]]+(.*) ]]
ivalue="${BASH_REMATCH[2]}"
[[ ${array[j]} =~ ([^[:space:]]+)[[:space:]]+(.*) ]]
jvalue=${BASH_REMATCH[2]}
if [[ $ivalue > $jvalue ]]
then
tmp=${array[i]}
array[i]=${array[j]}
array[j]=$tmp
fi
done
done
printf "%s\n" "${array[@]}"
}
function sort_keyed_f1 {
local c1 c2 keys values
while IFS=' ' read -r c1 c2
do
keys+=("$c1")
values+=("$c1 $c2")
done < "$1"
local i j tmpk tmpv
for ((i=0; i <= ${#keys[@]} - 2; i++))
do
for ((j=i + 1; j <= ${#keys[@]} - 1; j++))
do
if [[ ${keys[i]} > ${keys[j]} ]]
then
# swap keys
tmpk=${keys[i]}
keys[i]=${keys[j]}
keys[j]=$tmpk
# swap values
tmpv=${values[i]}
values[i]=${values[j]}
values[j]=$tmpv
fi
done
done
printf "%s\n" "${values[@]}"
}
function sort_keyed_f2 {
local c1 c2 keys values
while IFS=' ' read -r c1 c2
do
keys+=("$c2")
values+=("$c1 $c2")
done < "$1"
local i j tmpk tmpv
for ((i=0; i <= ${#keys[@]} - 2; i++))
do
for ((j=i + 1; j <= ${#keys[@]} - 1; j++))
do
if [[ ${keys[i]} -gt ${keys[j]} ]]
then
# swap keys
tmpk=${keys[i]}
keys[i]=${keys[j]}
keys[j]=$tmpk
# swap values
tmpv=${values[i]}
values[i]=${values[j]}
values[j]=$tmpv
fi
done
done
printf "%s\n" "${values[@]}"
}
Even after all of that, you still need one of your shell's core "functions", that is -- to redirect the output to a file:
sort_keyed_f1 input-file > alpha_sorted.txt
sort_keyed_f2 input-file > numbers_sorted.txt
sort
? As in: it is possible to write a function doing the sorting in pure bash – but in most cases one would use tools made for the task. The former is far more complex. – Runium Jun 12 '16 at 01:21