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.

2

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.

  • 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. – Stéphane Chazelas May 4 '17 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 May 4 '17 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

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//'
  • 1
    Or sed -n 'h;n;G;s/\n//p' – Stéphane Chazelas May 4 '17 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//'. – Satō Katsura May 4 '17 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 – Stéphane Chazelas May 4 '17 at 10:25
  • sed: -e expression #1, char 2: unknown command: ~ GNU sed only? – Philippos May 4 '17 at 10:42
  • @Philippos Yup, as I pointed out. – Satō Katsura May 4 '17 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
  • Fixed both your concerns. – user218374 May 4 '17 at 10:53
  • NL='<real-newline>' is a lot simpler (and doesn't assume an ASCII based system). Or eval "$(printf "NL='\n'")". – Stéphane Chazelas May 4 '17 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 May 4 '17 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 $(...)). – Stéphane Chazelas May 4 '17 at 11:05
  • printf can very well be non-builtin and we can still be in bourne. – user218374 May 4 '17 at 11:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.