Oleg
Oleg

Reputation: 43

How to swap the even and odd lines via bash script?

In the incoming string stream from the standard input, swap the even and odd lines. I've tried to do it like this, but reading from file and $i -lt $a.count aren't working:

$a= gc test.txt
for($i=0;$i -lt $a.count;$i++)
{
if($i%2)
{
$a[$i-1]
}
else
{
$a[$i+1]
}
}

Please, help me to get this working

Upvotes: 1

Views: 659

Answers (2)

pjh
pjh

Reputation: 8134

There's already a good awk-based answer to the question here, and there are a few decent-looking sed and awk solutions in Swap every 2 lines in a file. However, the shell solution in Swap every 2 lines in a file is almost comically incompetent. The code below is an attempt at a functionally correct pure Bash solution. Bash is very slow, so it is practical to use only on small files (maybe up to 10 thousand lines).

#! /bin/bash -p

idx=0 lines=()
while IFS= read -r 'lines[idx]' || [[ -n ${lines[idx]} ]]; do
    (( idx == 1 )) && printf '%s\n%s\n' "${lines[1]}" "${lines[0]}"
    idx=$(( (idx+1)%2 ))
done
(( idx == 1 )) && printf '%s\n' "${lines[0]}"
  • The lines array is used to hold two consecutive lines.
  • IFS= prevents whitespace being stripped as lines are read.
  • The idx variable cycles through 0, 1, 0, 1, ... (idx=$(( (idx+1)%2 ))) so reading to lines[idx] cycles through putting input lines at indexes 0 and 1 in the lines array.
  • The read function returns non-zero status immediately if it encounters an unterminated final line in the input. That could cause the loop to terminate before processing the last line, thus losing it in the output. The || [[ -n ${lines[idx]} ]] avoids that by checking if the read actually read some input. (Fortunately, there's no such thing as an unterminated empty line at the end of a file.)
  • printf is used instead of echo to output the lines because echo doesn't work reliably for arbitrary strings. (For instance, a line containing just -n would get lost completely by echo "$line".) See Why is printf better than echo?.
  • The question doesn't say what to do if the input has an odd number of lines. This code ((( idx == 1 )) && printf '%s\n' "${lines[0]}") just passes the last line through, after the swapped lines. Other reasonable options would be to drop the last line or print it preceded by an extra blank line. The code can easily be modified to do one of those if desired.
  • The code is Shellcheck-clean.

Upvotes: 0

Dudi Boy
Dudi Boy

Reputation: 4890

Suggesting one line awk script:

awk '!(NR%2){print$0;print r}NR%2{r=$0}' input.txt

awk script explanation

!(NR % 2){ # if row number divide by 2 witout reminder
  print $0; # print current row
  print evenRow; # print saved row
}
(NR % 2){ # if row number divided by 2 with reminder
  evenRow = $0; # save current row in variable
}

Upvotes: 2

Related Questions