1

There are some tools like datamesh to transpose a matrix in a csv file, but I want to exchange rows and columns character based. So a file

abcde
fghij
klmn
opqrs

should become

afko
bglq
chmq
dinr
ej s

Note that as line 3 is shorter, a whitespace has to be inserted in the last row.

Of source I could write some C program to do it, but I thought I once met a tool to do this, but my search engine doesn't help me find it.

3

You can do it with the rs utility in pure transpose (-T) mode - if you space the input appropriately first i.e.

$ sed -e 's/./& /g' -e 's/ $//' file
a b c d e
f g h i j
k l m n
o p q r s

(or, if you have GNU sed, you can use sed 's/./ &/2g'; another option is using a loop sed -E ':a; s/([^ ])([^ ])/\1 \2/; ta'); then

$ sed -e 's/./& /g' -e 's/ $//' file | rs -Tng0
afko
bglp
chmq
dinr
ej s

The important options are:

  • -T pure transpose
  • -n pad null entries
  • -g0 set the output gutter width (inter-column spacing) to zero

Alternatively, doing the input splitting using awk with an empty input field separator and default output field separator:

awk '{$1=$1} 1' FS= file | rs -Tng0
  • Or add the space before each character: sed -e 's/./ &/g' file | rs -Tng0. – Arrow Aug 2 '17 at 7:04
  • The only problem is that this depends on the count of characters of the first line. Try changing the first line (only) to abc. – Arrow Aug 2 '17 at 7:08
2

A general solution for transposing with awk follows.

To work correctly we need the number of columns.
That could be found while reading the file into an array of values:

#!/bin/bash
file=i4
delimiter=""
sep=""

transpose() { : # comment sed for newer awks.
              # Do this to separate characters in quite old awk
              # very old wak does not allow that the FS could be Null.
              #sed -e 's/./ &/g' "$file" |
              awk ' 
                   { for(i=1;i<=NF;i++){a[NR,i]=$i};{(NF>m)?m=NF:0} }
                   END { for(j=1; j<=m; j++)
                         { for(i=1; i<=NR; i++)
                           { b=((a[i,j]=="")?" ":a[i,j])
                             printf("%s%s",(i==1)?"":sep,b)
                           }
                           printf("\n")
                         }
                       }
                   ' FS="$delimiter" sep="$sep" cc="$countcols" <"$file"
             }

transpose

With this file:

abc
fghij
klmn
opqrs

Will print:

afko
bglp
chmq
 inr
 j s

Awk takes care of separating the characters if the "field separator" is null.
The characters are printed in one line if the variable sep is also null.


If the awk available is an older one, a null FS is not valid. Use the following two commands.

To count the number of characters, use this in older awks:

# Work with any POSIX awk to find the max character count in all rows.
countcols=$(awk '{l=length($0);(l>max)?max=l:0}END{print max}' < "$file")

To do the transposition, an space could be added in front of each character and use an space as a "field separator" and avoid the empty FS:

sed -e 's/./ &/g' < "$file" |
awk ' {for(i=1;i<=cc;i++){if($i==""){$i=" "};r[i]=r[i]sep$i;};sep=""};
      END{for(i=1;i<=cc;i++)print(r[i])}
    ' cc="$countcols"

Comment the sed line for newer awks.

0

Here is a solution with cut and paste. As you don't have any delimiter like space or tabulator it need some fixup with sed:

for COL in {1..5}; do cut -c $COL < infile | paste -s -d_ ; done | sed -e 's/__/_ /g' -e 's/_//g'

Here broken up in multiline:

for COL in {1..5}; do
  cut -c $COL < infile | paste -s -d_
done | sed -e 's/__/_ /g' -e 's/_//g'

The output of the first part looks like:

for COL in {1..5}; do cut -c $COL < infile | paste -s -d_ ; done

a_f_k_o
b_g_l_p
c_h_m_q
d_i_n_r
e_j__s

One annoying thing is that you have to know how many columns there are before start.

  • This fails if you change the first line to less characters, say abc. – Arrow Aug 2 '17 at 7:18

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.