LinuxQuestions.org
Visit Jeremy's Blog.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 09-17-2012, 01:25 AM   #1
w1k0
Senior Member
 
Registered: May 2008
Location: Poland
Distribution: Slackware (personalized Window Maker), Mint (customized MATE)
Posts: 1,309

Rep: Reputation: 234Reputation: 234Reputation: 234
How to convert the rows of data into the columns?


In the shell script I have some data stored in a few variables:

VAR1="ABCD"
VAR2="EFGH"
VAR3="IJKL"

These variables displayed with the command:

echo -e "$VAR1\n$VAR2\n$VAR3"

give the result:

Code:
ABCD
EFGH
IJKL
I’d like to display the above strings as follows:

Code:
AEI
BFJ
CGK
DHL
So I’d like to change the rows of the characters into the columns.

It’s relatively easy to convert such a file:

Code:
A B C D
E F G H
I J K L
into the file:

Code:
AEI
BFJ
CGK
DHL
using the following script:

Code:
    awk '{
          if (max_nf < NF)
               max_nf = NF
          max_nr = NR
          for (x = 1; x <= NF; x++)
               vector[x, NR] = $x
     }
     END {
          for (x = 1; x <= max_nf; x++) {
               for (y = 1; y <= max_nr; y++)
                    printf("%s", vector[x, y])
               printf("\n")
          }
     }'
Unfortunately the above solution assumes that the data is stored in a file.

I don’t want to use the temporary file because the data stored in the mentioned variables changes frequently and the continuous writing and reading of the temporary file increases the CPU usage.

I believe it’s easy to achieve that result with awk. Unfortunately I don’t know it enough to implement such a function in the shell script. Any assistance will be welcomed.
 
Old 09-17-2012, 02:21 AM   #2
H_TeXMeX_H
LQ Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928
Blog Entries: 2

Rep: Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301
Well, why not pipe the data as you display it to the awk command ?

Code:
bash-4.1$  echo -e "$VAR1\n$VAR2\n$VAR3" | ./script
AEI
BFJ
CGK
DHL
bash-4.1$ cat script
#!/bin/sh
awk '
	BEGIN {
		FS=""
	}
	{
          if (max_nf < NF)
               max_nf = NF
          max_nr = NR
          for (x = 1; x <= NF; x++)
               vector[x, NR] = $x
     }
     END {
          for (x = 1; x <= max_nf; x++) {
               for (y = 1; y <= max_nr; y++)
                    printf("%s", vector[x, y])
               printf("\n")
          }
     }'
exit 0
Note that I changed the FS="" so that each character is taken as a field.
 
1 members found this post helpful.
Old 09-17-2012, 06:32 AM   #3
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
How about this?

Code:
for (( i=0 ; i<${#VAR1} ; i++ )); do

	printf '%s%s%s\n' "${VAR1:i:1}" "${VAR2:i:1}" "${VAR3:i:1}"

done
 
1 members found this post helpful.
Old 09-17-2012, 07:01 AM   #4
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Mint 17.3
Posts: 1,881

Rep: Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660
Quote:
Originally Posted by w1k0 View Post
Have:
Code:
VAR1="ABCD"
VAR2="EFGH"
VAR3="IJKL"
Want:
Code:
AEI
BFJ
CGK
DHL
Consider this:
Code:
echo -e "$VAR1\n$VAR2\n$VAR3"  \
|awk -F "" '{for (i=1; i<=NF; i++) a[i]=a[i]$i} 
  END {i=1; while (i in a) {print a[i]; i++;}}
Daniel B. Martin
 
1 members found this post helpful.
Old 09-17-2012, 02:50 PM   #5
w1k0
Senior Member
 
Registered: May 2008
Location: Poland
Distribution: Slackware (personalized Window Maker), Mint (customized MATE)
Posts: 1,309

Original Poster
Rep: Reputation: 234Reputation: 234Reputation: 234
@H_TeXMeX_H,

Thank you for that tip. As I mentioned I don’t know awk. I found the above script in Internet. After the change you made it’s possible to pass the variables through the script without using the temporary file. It’s great improvement.

David the H. and danielbmartin,

I like your both solutions very much. The method by David the H. using the printf command assumes one have to define all the variables in the command line. It could be tiresome when there’s a lot of such a variables. Fortunately I need to convert just six variables. The method by danielbmartin using awk is more universal and very elegant in the comparison to the script that I found in Internet.

The following script:

Code:
#!/bin/bash

VAR1="0111"
VAR2="1100"
VAR3="1010"

rotate1() {
    for (( i=0 ; i<${#VAR1} ; i++ )); do
        printf '%s%s%s\n' "${VAR1:i:1}" "${VAR2:i:1}" "${VAR3:i:1}"
    done
}

rotate2() {
    awk -F "" '{for (i=1; i<=NF; i++) a[i]=a[i]$i}
        END {i=1; while (i in a) {print a[i]; i++;}}'
}

echo
echo -e "$VAR1\n$VAR2\n$VAR3"

echo
echo -e "$VAR1\n$VAR2\n$VAR3" | rotate1

echo
echo -e "$VAR1\n$VAR2\n$VAR3" | rotate2
produces the expected results:

Code:
0111
1100
1010

011
110
101
100

011
110
101
100
I decided to compare both these methods so I prepared two scripts – the first uses rotate1 function (printf) and the other uses rotate2 function (awk). Both these scripts perform 25,000 iterations and both of them send the output to /dev/null in order to test the defined functions performance only.

The script using rotate1 function (printf) worked significantly shorter:

Code:
real    1m5.928s
user    0m38.734s
sys     0m42.828s
and used from 30.4% to 47.7% of CPU power (the median was equal 45.5%).

The script using rotate2 function (awk) worked significantly longer:

Code:
real    3m27.683s
user    2m14.988s
sys     1m11.728s
and used from 36.7% to 43.4% of CPU power (the median was equal 41.0%).

From my purposes point of view the above differences aren’t crucial because my final script will run once a second.

***

Now I have the next question.

The output of the script is:

Code:
011
110
101
100
but I need to present the data in the other way. Instead of “0” I need “A” and instead of “1” I need “B”.

Now I pass the string of the variables through the rotate function and then pass the result through sed:

echo -e "$VAR1\n$VAR2\n$VAR3" | rotate_1_or_2 | sed -E 's/0/A/g;s/1/B/g'

I believe it’s possible to add to rotate1 or rotate2 functions the procedure that performs such a substitution. I’ll be grateful for some advice.
 
Old 09-17-2012, 04:19 PM   #6
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Mint 17.3
Posts: 1,881

Rep: Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660
Quote:
Originally Posted by w1k0 View Post
Now I have the next question.

The output of the script is:

Code:
011
110
101
100
but I need to present the data in the other way. Instead of “0” I need “A” and instead of “1” I need “B”.

Now I pass the string of the variables through the rotate function and then pass the result through sed:

echo -e "$VAR1\n$VAR2\n$VAR3" | rotate_1_or_2 | sed -E 's/0/A/g;s/1/B/g'

I believe it’s possible to add to rotate1 or rotate2 functions the procedure that performs such a substitution. I’ll be grateful for some advice.
Words are good; words with examples are better. Please post "Have" and "Want" examples, similar to what I put in a previous post.

Daniel B. Martin
 
Old 09-17-2012, 05:00 PM   #7
w1k0
Senior Member
 
Registered: May 2008
Location: Poland
Distribution: Slackware (personalized Window Maker), Mint (customized MATE)
Posts: 1,309

Original Poster
Rep: Reputation: 234Reputation: 234Reputation: 234
Here’s my script (binary clock):

Code:
#!/bin/bash

H=`date +"%H"`
M=`date +"%M"`
S=`date +"%S"`

Hl=`echo $H | sed -E 's/(.)./\1/'`
Hr=`echo $H | sed -E 's/.(.)/\1/'`
Ml=`echo $M | sed -E 's/(.)./\1/'`
Mr=`echo $M | sed -E 's/.(.)/\1/'`
Sl=`echo $S | sed -E 's/(.)./\1/'`
Sr=`echo $S | sed -E 's/.(.)/\1/'`

dectobin() {
    echo $1 | perl -e "printf(\"%04b\n\", <STDIN>)"
}

rotate1() {
    for (( i=0 ; i<${#Hl} ; i++ )); do
        printf '%s%s%s%s%s%s\n' "${Hl:i:1}" "${Hr:i:1}" "${Ml:i:1}" "${Mr:i:1}" "${Sl:i:1}" "${Sr:i:1}"
    done
}

rotate2() {
    awk -F "" '{for (i=1; i<=NF; i++) a[i]=a[i]$i}
        END {i=1; while (i in a) {print a[i]; i++;}}'
}

Hl=`dectobin $Hl`
Hr=`dectobin $Hr`
Ml=`dectobin $Ml`
Mr=`dectobin $Mr`
Sl=`dectobin $Sl`
Sr=`dectobin $Sr`

echo "HH MM SS"

echo -e "$Hl\n$Hr\n$Ml\n$Mr\n$Sl\n$Sr" | rotate1 | sed -E 's/0/ /g;s/1/*/g;s/(..)(..)(..)/\1|\2|\3|/'
The above script gets the hours (H), minutes (M), and seconds (S). Splits them into left and right values (Hl, Hr, Ml, Mr, Sl, and Sr). Converts these decimal numbers into binary ones using dectobin function. Rotates the rows into columns using rotate1 or rotate2 function. Finally it filters the oputput through sed.

The data after rotating the rows into the columns but before filtering through sed looks like:

Code:
000101
000000
111000
011111
The same data filtered through sed and with the label looks like:

Code:
HH MM SS
  | *| *|
  |  |  |
**|* |  |
 *|**|**|
It’s easy to add the vertical lines to the output modifying printf command from:

Code:
printf '%s%s%s%s%s%s\n'
to:

Code:
printf '%s%s|%s%s|%s%s|\n'
The harder part is to convert all “0s” to spaces and all “1s” to asterisks. As you can see now I filter the output through the following command:

Code:
sed -E 's/0/ /g;s/1/*/g;s/(..)(..)(..)/\1|\2|\3|/'
But I wonder if it’s possible to perform the above substitutions using printf from rotate1 function or awk from rotate2 function in order to avoid the additional filtering through sed.
 
2 members found this post helpful.
Old 09-17-2012, 11:22 PM   #8
PTrenholme
Senior Member
 
Registered: Dec 2004
Location: Olympia, WA, USA
Distribution: Fedora, (K)Ubuntu
Posts: 4,187

Rep: Reputation: 354Reputation: 354Reputation: 354Reputation: 354
If you pass some arguments to you rotate function, it might handle more general cases.

Here's a slight re-write of the code you posted:
Code:
#!/bin/bash

H=$(date +%H)
M=$(date +%M)
S=$(date +%S)

Hl=$(echo $H | sed -E 's/(.)./\1/')
Hr=$(echo $H | sed -E 's/.(.)/\1/')
Ml=$(echo $M | sed -E 's/(.)./\1/')
Mr=$(echo $M | sed -E 's/.(.)/\1/')
Sl=$(echo $S | sed -E 's/(.)./\1/')
Sr=$(echo $S | sed -E 's/.(.)/\1/')

dectobin() {
  printf -v $1 "%s" $(echo "${!1}" | perl -e "printf(\"%04b\n\", <STDIN>)")
}

rotate1()
{
  local i j max
  max=0
  for i in $*
  do
    [ ${max} -lt ${#i} ] && max=${#i}
  done
  for (( i=0 ; i<${max} ; i++ ))
  do
    for j in $*
    do
      printf '%s' "${j:${i}:1}"
      done
    printf '\n'
  done
}
      
rotate2() {
  awk -F "" '{for (i=1; i<=NF; i++) a[i]=a[i]$i}END {i=1; while (i in a) {print a[i]; i++;}}'
}

dectobin Hl
dectobin Hr
dectobin Ml
dectobin Mr
dectobin Sl
dectobin Sr

echo "HH MM SS"

rotate1 $Hl $Hr $Ml $Mr $Sl $Sr | sed -E 's/0/ /g;s/1/*/g;s/(..)(..)(..)/\1|\2|\3|/'
which produces this:
Code:
$ sh rotate.sh 
HH MM SS
  |  |  |
  | *|  |
* | *| *|
 *|**|  |
 
3 members found this post helpful.
Old 09-18-2012, 02:01 AM   #9
H_TeXMeX_H
LQ Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928
Blog Entries: 2

Rep: Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301
Interesting script.
 
Old 09-18-2012, 12:03 PM   #10
w1k0
Senior Member
 
Registered: May 2008
Location: Poland
Distribution: Slackware (personalized Window Maker), Mint (customized MATE)
Posts: 1,309

Original Poster
Rep: Reputation: 234Reputation: 234Reputation: 234
PTrenholme,

I compared your rotate1 function to the rotate1 function suggested by David the H.

Benchmark for David the H. function:
Code:
#!/bin/bash

VAR1="0111"
VAR2="1100"
VAR3="1010"

rotate1() {
    for (( i=0 ; i<${#VAR1} ; i++ )); do
        printf '%s%s%s\n' "${VAR1:i:1}" "${VAR2:i:1}" "${VAR3:i:1}"
    done
}

time for n in `seq 1 25000`
do
    echo -e "$VAR1\n$VAR2\n$VAR3" | rotate1 > /dev/null
done
Benchmark for PTrenholme function:
Code:
#!/bin/bash

VAR1="0111"
VAR2="1100"
VAR3="1010"

rotate1()
{
  local i j max
  max=0
  for i in $*
  do
    [ ${max} -lt ${#i} ] && max=${#i}
  done
  for (( i=0 ; i<${max} ; i++ ))
  do
    for j in $*
    do
      printf '%s' "${j:${i}:1}"
      done
    printf '\n'
  done
}

time for n in `seq 1 25000`
do
    rotate1 $VAR1 $VAR2 $VAR3 > /dev/null
done
The previous benchmarks mentioned in the post #5 I made after closing all applications and Internet connection because I wanted to compare not only times of the execution but also the CPU usages. The present benchmarks I made on the system running different applications and using Internet connection testing the times of the execution only. That explains the difference between the previous and the present results for David the H. function. Previously, on the idle system, the real time was 1m5.928s – at present, on the busy system, the real time is 1m55.536s.

The result for David the H. function:
Code:
real    1m55.536s
user    0m38.895s
sys     0m46.289s
The result for PTrenholme function:
Code:
real    0m28.595s
user    0m21.478s
sys     0m0.655s
So your rotate1 function works four times faster than David the H. function. It’s incredible improvement.

Of course your function is also more general so one doesn’t have to define in that function all the variables.

I’m really impressed by your work.

H_TeXMeX_H,

Quote:
Originally Posted by H_TeXMeX_H
Interesting script.
I’m not sure it’s a serious opinion or an ironic one.

I put together in my script the shell commands, sed, Perl, and AWK. My script is a melting pot of different programming techniques. I’m aware it’s possible to get the same result in a more elegant form using Perl or AWK only. Unfortunately I don’t know neither of them enough to code that.

So if it was irony it was justified.

***

My question from post #7 is still open: is it possible to avoid the substitution performed with sed:

Code:
sed -E 's/0/ /g;s/1/*/g;s/(..)(..)(..)/\1|\2|\3|/'
by implementing such a substitution into rotate1 function in order to change the rotated data such as:

Code:
000100
001001
110001
011111
into:

Code:
  | *|  |
  |* | *|
**|  | *|
 *|**|**|
(Both above outputs show the time 23:59:17.)
 
Old 09-18-2012, 12:20 PM   #11
w1k0
Senior Member
 
Registered: May 2008
Location: Poland
Distribution: Slackware (personalized Window Maker), Mint (customized MATE)
Posts: 1,309

Original Poster
Rep: Reputation: 234Reputation: 234Reputation: 234
@H_TeXMeX_H,

I just realized you found my post including the binary clock script helpful. So your statement wasn't an irony.
 
Old 09-19-2012, 03:10 AM   #12
H_TeXMeX_H
LQ Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928
Blog Entries: 2

Rep: Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301
Well, I mean it is interesting in terms of being a binary clock written in bash, and PTrenholme's frankenstein script written in many different languages, and yet has good performance. I meant no irony.
 
Old 09-19-2012, 09:46 AM   #13
w1k0
Senior Member
 
Registered: May 2008
Location: Poland
Distribution: Slackware (personalized Window Maker), Mint (customized MATE)
Posts: 1,309

Original Poster
Rep: Reputation: 234Reputation: 234Reputation: 234
I prepared binary clock among the other scripts for the next release of wminfo (see: http://dockapps.windowmaker.org, or http://freecode.com, or http://slackbuilds.org). The wminfo program is a dockable application for Window Maker that displays different information using the plugins. The binary clock is such a plugin. The performance of the plugins becomes crucial when you run a dozen or so of instances of wminfo using different plugins. One poorly designed plugin isn’t a problem. Twelve poorly designed plugins can consume a lot of system resources. So I spend a lot of time testing and optimizing the plugins or writing the alternative versions of the most useful and the most demanding plugins.
 
Old 09-19-2012, 10:50 AM   #14
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,005

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
Not sure if it had to be done in bash ... but thought it was a nice little challenge in ruby
Code:
#!/usr/bin/env ruby

cnt = 0
array = Array.new(6) { Array.new(4) }
t = Time.now.strftime("%H%M%S")

puts "HH MM SS"

t.each_char do |c|
		0.upto(3) do |n|
				array[cnt][n] = c.to_i[n]
		end

		array[cnt].reverse!
		cnt+=1
end

transpose_array = array.transpose

transpose_array.each{ |x| x.map!{ |y| y == 0 ? ' ' : '*' } }


transpose_array.each do |x|
	6.step(2, -2) { |n| x.insert(n, '|') }
	print "#{x.join}\n"
end
This actually covers the whole process.
 
1 members found this post helpful.
Old 09-19-2012, 11:44 AM   #15
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,780

Rep: Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081
GNU awk (uses nonstandard bitwise and function), avoids both substitution and rotation:
Code:
#!/bin/sh
echo "HH MM SS"
date '+%H|%M|%S' | gawk -vFS= '
{for (bit = 8; bit >= 1; bit = bit/2) {
    for (i=1; i <= NF; i++)
        printf($i=="|"?$i: and($i,bit)?"*":" ");
    print("")}}'

Last edited by ntubski; 09-19-2012 at 11:46 AM. Reason: reformat code to make it less wide
 
1 members found this post helpful.
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Convert columns of data into rows jaufer Linux - Software 3 06-29-2010 05:19 PM
convert columns to rows (tab separated file to csv) doug23 Programming 16 08-16-2009 09:14 PM
Script to convert logs columns to rows fono Linux - Software 10 05-19-2009 08:29 PM
How to print data in rows and columns suran Linux - General 3 03-15-2009 02:53 PM
text data conversion: rows into columns frankie_DJ Programming 6 06-03-2006 06:43 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 02:04 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration