The file is as followed (all the spaces are "single-space"):

A S1 0 0 0 -9 C C A G C C A G A A
B S2 0 0 0 -9 C C A G C C A G A A
C S3 0 0 0 -9 C C A G C C A G A A
D S4 0 0 0 -9 C C A G C C A G A A

What I need is to replace space coming after every second columns (even numbered fields) into tabs (\t). The expected result is as followed:

A S1"\t"0 0"\t"0 -9"\t"C C"\t"A G"\t"C C
B S2"\t"0 0"\t"0 -9"\t"C C"\t"A G"\t"C C
C S3"\t"0 0"\t"0 -9"\t"C C"\t"A G"\t"C C
D S4"\t"0 0"\t"0 -9"\t"C C"\t"A G"\t"C C

Since the original file I have has much more number of columns so the command line should not be manual (specifying certain number of fields).

I would like to kindly ask about this issue to all of you.

Thank you for your help beforehand.

  • Do you want to remove A G A A from the end of each line?
    – rowboat
    Jul 23 at 9:29

Match two spaces, keep one and replace the second:

sed -E 's/( [^ ]*) /\1\t/g'

Mandatory awk-based solution ;) :

awk '{for (i=1;i<=NF;i++) printf("%s%s",$i,i==NF?ORS:((i%2)?" ":"\t"))}' input.txt

This will iterate over all fields and print them via printf, where the field content is followed either by

  • the "output record separator" (defaults to newline) when the last field is reached
  • otherwise, a space if the field number is odd,
  • or a \t if the field number is even

Short and silly way, but totally legit assuming that we know we're dealing with 16 columns:

$ tr ' ' '\n' <file | paste -d ' \t' - - - - - - - - - - - - - - - -
A S1    0 0     0 -9    C C     A G     C C     A G     A A
B S2    0 0     0 -9    C C     A G     C C     A G     A A
C S3    0 0     0 -9    C C     A G     C C     A G     A A
D S4    0 0     0 -9    C C     A G     C C     A G     A A

This replaces each of the original space delimiters by newlines. It then reads the resulting stream (one field per line) into paste which will create 16 columns with alternating space and tab delimiters.

Using awk to print the fields in tab-delimited pairs:

$ awk -v OFS='\t' '{ nf = 0; delete a; for (i = 1; i < NF; i += 2) a[++nf]=sprintf("%s %s", $i, $(i+1)); $0 = ""; for (i = 1; i <= nf; ++i) $i = a[i]; print }' file
A S1    0 0     0 -9    C C     A G     C C     A G     A A
B S2    0 0     0 -9    C C     A G     C C     A G     A A
C S3    0 0     0 -9    C C     A G     C C     A G     A A
D S4    0 0     0 -9    C C     A G     C C     A G     A A

The code temporarily stores space-delimited pairs of fields in the array a. This array's elements are then used to replace the fields of the current record. The new record is printed with tab as the delimiter, giving the wanted effect.

The stand-alone awk code:

BEGIN { OFS = "\t" }

    nf = 0; delete a
    for (i = 1; i < NF; i += 2)
        a[++nf] = sprintf("%s %s", $i, $(i+1))

    $0 = ""
    for (i = 1; i <= nf; ++i)
        $i = a[i]

gawk '{$1=$1}1' FPAT='[^ ]+ *[^ ]*' OFS='\t' file

FPAT - A regular expression describing the contents of the fields in a record.


Using the itertools module from the python3 standard library.

python3 -c 'import sys
from itertools import zip_longest

ifile = sys.argv[1]
fs,rs,ofs = " ","\n","\t"

with open(ifile) as f:
  for l in f:
    L = l.rstrip(rs).split(fs)
      for t in zip_longest(L[::2],L[1::2])],sep=ofs)
' your_file.input

The zip function in Python parallelizes two or more iterators, in this case lists. Python has a compact slice notation [::2] and [1::2] to refer to even numbered elements and odd numbered elements from a given list. Here we pick one one element each from the even n odd lists and join them with spaces to form a pair and then join these pairs (aka, tuples in Python speak) with TABs.

Using the gensub function in GNU awk which allows us to target a particular match number in the input string.

awk '
  for (i=2; i<NF; i++)
    t = gensub(FS, "\t", i, t)
  print t
' your_file.input

Using the sed utility we first turn all spaces into newlines,a character guaranteed not to be present in the pattern space, having been chopped away before being placed in the pattern space. Then we progressively and alternately, change a newline to space and next newline to a tab. Repeat till the newlines run out.

sed -e '
  y/ /\n/
    s/\n/ /
  t a
' your_file.input

Same thing in perl.

perl -lpe '
  s/ /$|--?"\t":$&/eg;
  $|-- unless $|--;
' your_file.input

perl -sF'\x20' -lane '
  splice @F, $_, 2, "@F[$_,$_+1]" for 0 .. (@F>>1)-1;
  print @F;
' -- -,=$'\t' your_file.input

With a single space for the record separator:

awk '
    print sep $0
    f = ! f || /\n/
    sep = (f ? OFS : "\t")
' RS=' ' ORS= file

f is flipped between 1 and 0 (always back to 1 if a newline is matched), and a separator to print before the next record is chosen based on the value of f.

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.