1

I am trying to figure out how to move all even lines to the beginning of all odd lines as follows:

before:

AAA
BBB
CCC
DDD

after:

BBBAAA
DDDCCC

Something I can run on multiple files at once would be great since combining all the files using cat results in a file that is too big to deal with. Something similar to the following command for doing text swapping so that I can add it to a bash file:

  find /directory/. -type f -exec sed -i 's/oldtext/newtext/g' {} \;

I have searched but I haven't found something suitable.

To make things easier I can add 'string1' to the beginning of all odd lines and 'string2' to the beginning of all even lines as follows:

  before:
  string1AAA
  string2BBB
  string1CCC
  string2DDD

  after:
  string2BBBstring1AAA
  string2DDDstring1CCC 

Big thanks.

5 Answers 5

3

Forget about the idea with the prefix. The script you are looking for is

sed 'N;s/\(.*\)\n\(.*\)/\2\1/'

That is, read the next line with N, then change order and remove the newline.

And you can combine this with the find as you wrote. Try without option -i first to test whether this is what you want.

2
  • 1
    As a note: if the file contains an odd number of lines, the behaviour will vary depending on the sed implementation and with GNU sed, with whether POSIXLY_CORRECT is in the environment or not. Commented May 4, 2017 at 10:07
  • If this can happen, sed '$! N;s/\(.*\)\n\(.*\)/\2\1/' would be needed if the requirement is to print the last line without a change
    – Philippos
    Commented May 4, 2017 at 10:36
1

gawk solution:

for f in /directory/*; do gawk -i inplace '{getline nl; print nl$0}' "$f"; done

for f in /directory/* - for each file in specified path

gawk -i inplace - enable in place file editing

getline nl - read the next record from awk's input into variable nl (next line)

print nl$0 - print the current and the next record swapped

0
1

With GNU sed:

sed '1~2 { h; d; }; G; s/\n//'

With awk:

awk '{ if(NR%2) save = $0; else print $0 save }'

With perl:

perl -pe '$_ = <> . $_; s/\n//'
5
  • 1
    Or sed -n 'h;n;G;s/\n//p' Commented May 4, 2017 at 10:09
  • @StéphaneChazelas That works too, of course. But if the input file has an odd number of lines, my version is much easier to fix to print the last line unchanged: sed '1~2 { h; $p; d; }; G; s/\n//'. Commented May 4, 2017 at 10:16
  • True (assuming one does want to print that extra line). Mine would have to become sed -ne '$!{h;n;G;s/\n//;}' -e p Commented May 4, 2017 at 10:25
  • sed: -e expression #1, char 2: unknown command: ~ GNU sed only?
    – Philippos
    Commented May 4, 2017 at 10:42
  • @Philippos Yup, as I pointed out. Commented May 4, 2017 at 10:43
0

To complete RomanPrekhrest's answer, if you have and old version of gawk like me that does not support inplace edition, or have the getline instruction:

find /path/to/directory/ -exec awk 'NR%2!=0{line=$0; next}{print line $0}END{if(NR%2!=0) print line}' {} > /tmp/mytmpfile.txt \; -exec mv /tmp/mytmpfile.txt {} \;

Also added something to handle files with an odd number of lines.

0
eval "`echo 'NL=qsq' | tr 'qs' '\047\012'`"; # newline
set -- 
while IFS= read -r l; do
   case $# in
      0 ) set -- "$l" ;;
      * ) printf '%s%s\n' "$l" "$1"; set -- ;;
   esac
done < input.data
printf '%s' ${1+"$1$NL"} # to take care of dangling line

awk '{ if ( NR % 2 ) ORS = $0 RS; else print }' input.data

perl -lpe '$_ = (<>.$_) =~ s/\n//r' input.data

sed -ne '
   ${p;d;}
   h;n;G;s/\n//p
' input.data

Result

BBBAAA
DDDCCC
6
  • Fixed both your concerns.
    – user218374
    Commented May 4, 2017 at 10:53
  • NL='<real-newline>' is a lot simpler (and doesn't assume an ASCII based system). Or eval "$(printf "NL='\n'")". Commented May 4, 2017 at 10:56
  • I don't like literal whitespaces in variables as they can be cleaned up by linting or other such tools, and besides this way the intent is clear and it enhances portability.
    – user218374
    Commented May 4, 2017 at 11:00
  • Still, eval "$(printf "NL='\n'")" is a lot clearer than eval "`echo 'NL=qsq' | tr 'qs' '\047\012'`", and you're already using printf and read -r (which excludes the Bourne shell, the only Bourne-like shell that doesn't support $(...)). Commented May 4, 2017 at 11:05
  • printf can very well be non-builtin and we can still be in bourne.
    – user218374
    Commented May 4, 2017 at 11:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .