4

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.

0

5 Answers 5

5

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 
2
  • Or add the space before each character: sed -e 's/./ &/g' file | rs -Tng0. Commented Aug 2, 2017 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. Commented Aug 2, 2017 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
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.

1
  • This fails if you change the first line to less characters, say abc. Commented Aug 2, 2017 at 7:18
0

Using Raku (formerly known as Perl_6)

~$ raku -e 'my @a = lines.map: *.comb; my @b; my $max1 = @a.map(*.elems).max; for ^@a -> $i { for ^$max1 -> $j { @b.push: @a[$i][$j] // " " } }; .join.put for [Z] @b.rotor( $max1 );' file #OR: ~$ raku -e 'my @a = lines.map: *.comb; my @b; my $max1 = @a.map(*.elems).max; for ^@a -> $i { @b.push: @a[$i][$_] // " " for ^$max1 }; .join.put for [Z] @b.rotor( $max1 );' file 

Above are answers written in Raku, a member of the Perl-family of programming languages. Raku features high-level support for Unicode built-in, so that characters (except filenames) are normalized (NFC/NFG, see links at bottom). This makes decomposition of strings into characters a reliable process.

Sample Input:

abcde fghij klmn opqrs 

Sample Output:

afko bglp chmq dinr ej s 

NOTE 1: Users can think of the above code to proceed in two phases--first padding the array to insert a user-defined character (such as " " whitespace), and second transposing rows-to-columns. To simply pad (without transposing), omit the call to [Z] above.

NOTE 2: In the final line Raku will output whitespace-separated characters if the call to .join is omitted. Conversely, each line in the final output can be joined on user-defined character, such as .join(",").


https://github.com/MoarVM/MoarVM/blob/main/docs/strings.asciidoc#normalization
https://docs.raku.org/language/unicode#index-entry-Normalization
https://6guts.wordpress.com/2015/04/12/this-week-unicode-normalization-many-rts/
https://6guts.wordpress.com/2015/04/20/this-week-digging-into-nfg-fixing-use-fatal-and-more/
https://raku.org

-3

If lines with less characters, pad each line with some character and then remove the extra characters.

echo abc | sed 's/./&@@@@/' | sed -r 's/(.{4})./\1/'

abc@

echo "" | sed 's/./&@@@@/' | sed -r 's/(.{4})./\1/'

@@@@

1
  • 1
    (1) Your commands don't give the results that you say they do. (2) Even if the results that you show were correct, how would this answer the question? Commented Aug 9, 2020 at 21:53

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.