Perl
perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt
The idea here is that we use modulo operator %
with line number $.
variable, to figure out which one is every first, which one is every second, and which one is every 3rd line. For every 3rd line remainder is 0, while for every 1st and 2nd line it will have corresponding numbers.
Test:
$ cat input.txt
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
$ perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
Minor improvement
The approach with storing second line into a variable has a flaw. What if the last line is the "second" one , i.e. for that line number remainder is 2 ? The original code in my and DopeGhoti's answer won't print My dog is orange
if we leave out the last line. The fix for that in both cases is to use END{}
code block, with unsetting the temporary variable after printing. In other words:
$ awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay;delay=""}END{print delay}' input.txt
and
$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s}' input.txt
This way, the code will work for arbitrary number of lines in a file, not just those divisible by 3.
Additional fix for issue mentioned in comments
In awk's case, if the last line in the file produces output of 1 for $. % 3, the previous code has issue of outputting blank newline because of unconditional printing of END{print delay}
, since print
function mentioned in the comments always appends newline to whatever variable it is operating on. In case of perl
version this issue doesn't occur, since with -ne
flags print
function doesn't append the newline.
Nonetheless, the fix in awk's case is to make conditional, as mentioned by Dope Ghoti in the comments is to verify the length of the temporary variable. The perl version of the same fix would be:
$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt