Bash - adding numbers from array matrix - bash

When adding numbers in a matrix, I get this error:
line 271: 1 2 3 4: syntax error in expression (error token is "2 3 4")
My add function:
add()
{
#Reading matrices into temp files
while read line1 <&3 && read line2 <&4
do
echo "$line1" | tr "\n" "\t" >> "temp50"
echo "$line2" | tr "\n" "\t" >> "temp60"
done 3<$temp1 4<$fileTwo
echo >>"temp50"
echo >>"temp60"
cat "temp60" >> "temp50"
i=1
x=1
while [ $i -le $totalNum ]
do
sum=0
cut -f $i "temp50" > "temp55"
while read num
do
sum=$(($sum + $num))
done <"temp55"
echo "$sum" | tr "\n" "\t" >> "temp65"
#try and remove hanging tab
if [[ "$x" -eq "$numcolOne" ]]
then
rev "temp65" > "temp222"
cat "temp222" | cut -c 1- >"temp333"
rev "temp333">"temp65"
x=0
fi
i=$((i+1))
x=$((x+1))
done
Matrix array (temp1):
1 2 3 4
5 6 7 8
Function is supposed to add the matrix array in the temp1 file; sample output:
2 4 6 8
10 12 14 16
Appreciate anyone's help!

Related

bash,shell, unexpected token done

here is my code:
echo -n "Enter 3 parameters in format file-num1-num2: "
str=""
while read param
do
echo $param >|temp
fname=$(cut -d"-" -f1 temp)
num1=$(cut -d"-" -f2 temp)
num2=$(cut -d"-" -f3 temp)
range=$num1"-"$num2
head -$num3 $fname >|temp
tail -$num1 temp >|temp
st=$fname"-"$(wc -c <temp)
str=$str" "$st"-"$range
echo $(wc -c <temp) >>temp2
echo -n "Enter 3 parameters in format file-num1-num2: "
done
sort temp2 >|temp
c=$(wc -l temp)
c2=0
while [ $c2 -lt $c ]
do
((c2++))
head -$c2 temp >|temp3
tail -1 temp3 >|temp3
num=$(cat temp3)
for x in $str
do
echo $x >| temp4
fname=$(cut -d"-" -f1 temp4)
ran=$(cut -d"-" -f2 temp4)
sh=$(cut -d"-" -f3 temp4)
if [ $ran -eq $num ]
then
echo "The size of file $fname in lines $sh is: $num"
done
done
rm temp
rm temp2
rm temp3
rm temp4
ok so the problem is when i run the script i get this error:
./P4.4: line 36: syntax error near unexpected token `done'
./P4.4: line 36: `done'
and when i run it for some reason the first loop gets stuck and i need to press ctrl^d to keep it going and do it two time to get out of the loop
and this is what the code is supposed to do:
input:
g-5-7
f-2-4
output:
The size of file f in lines 2-4 is: 16
The size of file g in lines 5-7 is: 10
file g:
12
34
56
78
910
ab
wz
file f:
aa
bb c
dd ef
gh x
ttt
if [ $ran -eq $num ]
then
echo "The size of file $fname in lines $sh is: $num"
done
The if there is missing a fi.

Flip multiple strings vertically on bash

I am trying to flip the contents of any sentence vertically. So each chars of any string will get printed vertically in same line. For Example:
Sample Text: This is an Example
Output expected: T i a E
h s n x
i a
s m
p
l
e
In following direction I am trying to achieve this but not able to yet.
echo "Input provided by user is $#"
for i in $(seq 1 $#); do
echo ${!i} | sed 's/./ &/g' | xargs |tr ' ' '\n'
done
Current output:
T
h
i
s
i
s
a
n
E
x
a
m
p
l
e
Also, This is also not helping
echo Print text vertically | fold -c -w1
T
h
i
s
i
s
a
n
E
x
a
m
p
l
e
More alternatives which did not worked :
#echo "Input provided by user is $#"
for i in $(seq 1 $#); do
content[i]=$(echo ${!i}|fold -c -w1)
#echo ${content[i]}
done
echo ${content[#]}
max variable holds the max length among all words. For your text it would be: length('Example') which is 7 (maximum out of lengths of all words)
Using an awk script file:
$ awk -f script.awk <<< "This is an Example"
TiaE
hsnx
i a
s m
p
l
e
And here is the script:
{
max=0
for(i=1;i<=NF;i++)
max=length($i)>max?length($i):max;
for(j=1;j<=max;j++)
{
for(i=1;i<=NF;i++)
{
temp=substr($i, j, 1);
printf temp==""?" ":temp
}
printf "\n"
}
}
#!/bin/bash
function abc(){
maxIteration=0;
for i in $(seq 1 $#); do
j=$(echo ${!i})
if [ $maxIteration -lt ${#j} ]
then
maxIteration=${#j};
fi
done
COUNTER=0;
while [ $COUNTER -lt $maxIteration ]; do
for i in $(seq 1 $#); do
j=$(echo ${!i})
if [ ${#j} -gt $COUNTER ]
then
echo ${j:$COUNTER:1} | tr '\n' ' ';
else
echo " " | tr '\n' ' ';
fi
done
echo -e "\n"
let COUNTER=COUNTER+1
done
}
abc $#| grep .
I had created some similar script before. A short but complete POC:
#!/bin/bash
count=0
max=0
#first determine the longest string so we can later pad shorter strings with spaces
for i in $(echo "$1" | xargs -d: -i echo {})
do
size=$(echo $i | wc -c)
if [[ $size > $max ]]
then
max=$size
fi
done
files=""
#then echo the strings vertically inside the tmp files
for i in $(echo "$1" | xargs -d: -i echo {})
do
res=$(echo $i | sed 's/./ &/g' | xargs |tr ' ' '\n' > /tmp/$count.out)
#and add spaces at the end
add_space=$((max-$(echo $i | wc -c)))
for space in $(seq 0 $add_space)
do
echo " " >> /tmp/$count.out
done
files=$files" $count.out"
count=$((count+1))
done
#and finally print them side by side
pr -t -J -m -w 70 -S" " $files
I create tmp files under /tmp, echo the string vertical and later use pr to print it out.
% ./s.sh "This is an Example"
T i a E
h s n x
i a
s m
p
l
e
Because Perl is fun:
perl -a script.pl <<< 'This is an Example'
T i a E
h s n x
i a
s m
p
l
e
And the script:
#F = map { [/./g] } #F;
while (grep #{$_}, #F) {
printf "%s ", shift #{$_} || ' ' for #F;
print "\n"
}
Alternative script:
perl -pe '$r.=$/while/\S/&&s/(\S)(\S*)|\B/$r.=($1||$").$";$2/ge}{$_=$r' \
<<< 'This is an Example'
T i a E
h s n x
i a
s m
p
l
e

how can i echo a line once , then the rest keep them the way they are in unix bash?

I have the following comment:
(for i in 'cut -d "," -f1 file.csv | uniq`; do var =`grep -c $i file.csv';if (($var > 1 )); then echo " you have the following repeated numbers" $i ; fi ; done)
The output that i get is : You have the following repeated numbers 455
You have the following repeated numbers 879
You have the following repeated numbers 741
what I want is the following output:
you have the following repeated numbers:
455
879
741
Try moving the echo of the header line before the for-loop :
(echo " you have the following repeated numbers"; for i in 'cut -d "," -f1 file.csv | uniq`; do var =`grep -c $i file.csv';if (($var > 1 )); then echo $i ; fi ; done)
Or only print the header once :
(header=" you have the following repeated numbers\n"; for i in 'cut -d "," -f1 file.csv | uniq`; do var =`grep -c $i file.csv';if (($var > 1 )); then echo -e $header$i ; header=""; fi ; done)
Well, here's what I came to:
1) generated input for testing
for x in {1..35},aa,bb ; do echo $x ; done > file.csv
for x in {21..48},aa,bb ; do echo $x ; done >> file.csv
for x in {32..63},aa,bb ; do echo $x ; done >> file.csv
unsort file.csv > new.txt ; mv new.txt file.csv
2) your line ( corrected syntax errors)
dtpwmbp:~ pwadas$ for i in $(cut -d "," -f1 file.csv | uniq);
do var=`grep -c $i file.csv`; if [ "$var" -ge 1 ] ;
then echo " you have the following repeated numbers" $i ; fi ; done | head -n 10
you have the following repeated numbers 8
you have the following repeated numbers 41
you have the following repeated numbers 18
you have the following repeated numbers 34
you have the following repeated numbers 3
you have the following repeated numbers 53
you have the following repeated numbers 32
you have the following repeated numbers 33
you have the following repeated numbers 19
you have the following repeated numbers 7
dtpwmbp:~ pwadas$
3) my line:
dtpwmbp:~ pwadas$ echo "you have the following repeated numbers:";
for i in $(cut -d "," -f1 file.csv | uniq); do var=`grep -c $i file.csv`;
if [ "$var" -ge 1 ] ; then echo $i ; fi ; done | head -n 10
you have the following repeated numbers:
8
41
18
34
3
53
32
33
19
7
dtpwmbp:~ pwadas$
I added quotes, changed if() to [..] expression, and finally moved description sentence out of loop. Number of occurences tested is digit near "-ge" condition. If it is "1", then numbers which appear once or more are printed. Note, that in this expression, if file contains e.g. numbers
8
12
48
then "8" is listed in output as appearing twice. with "-ge 2", if no digits appear more than once, no output (except heading) is printed.

Assigning variables to values in a text file with 3 columns, line by line

I've got a .txt file with three columns, each separated by a tab, and 264 rows called PowerCoords.txt. Each row contains an x (column 1), y (column2) and z (column3) coordinate. I want to go through this file, line by line, assign each value to X,Y, and Z, and then input those variables into another function.
I'm new to bash, and I don't understand how to specify that I want the value in Row 1, Column 2 to be the variable Y, and so on...
I know this is likely super simple and I could do it in a flash in Matlab, but I'm trying to keep everything in bash.
while read x y z; do
echo x=$x y=$y z=$z
done < input.txt
The above requires that none of your columns contain any whitespace.
EDIT:
In response to comments, here is one technique to handle numbering the lines:
nl -ba < input.txt | while read line x y z rest; do
~/data/standard/MNI152_T1_2mm -mul 0 \
-add 1 -roi $x 1 $y 1 $z 1 0 1 point -odt float > NewFile$line
done
William Pursell's answer is much smarter, but in my straight-forward beginners mind I tried following some time ago:
#!/bin/bash
data="data.dat"
datalength=`wc $data | awk '{print $1;}'`
for (( i=1; i<=$datalength; i++ )) ;do
x=`cat $data | awk '{print $1;}' | sed -n "$i"p | sed -e 's/[eE]+*/\\*10\\^/'` ; x=`echo "$x" | bc -l` ; echo "x$i=$x";
y=`cat $data | awk '{print $2;}' | sed -n "$i"p | sed -e 's/[eE]+*/\\*10\\^/'` ; y=`echo "$y" | bc -l` ; echo "y$i=$y";
z=`cat $data | awk '{print $3;}' | sed -n "$i"p | sed -e 's/[eE]+*/\\*10\\^/'` ; z=`echo "$z" | bc -l` ; echo "z$i=$z";
# do something with xyz:
fslmaths ~/data/standard/MNI152_T1_2mm -mul 0 -add 1 -roi $x 1 $y 1 $z 1 0 1 point -odt float > NewFile$i
done
The bc and the sed -e 's/[eE]+*/\\*10\\^/' have to be added if you like to use floating point numbers and for the case that input also uses exponential notation.
I had a similar problem but for lots of input data those bash scripts are very slow. I migrated to perl then. In perl it would look like this:
#!/usr/bin/perl -w
use strict;
open (IN, "data.dat") or die "Error opening";
my $i=0;
for my $line (<IN>){
$i++;
open(OUT, ">NewFile$i.out");
chomp $line;
(my $x,my $y,my $z) = split '\t',$line;
print "$x $y $z\n";
# do something with xyz:
my $f= fslmaths ~/data/standard/MNI152_T1_2mm -mul 0 -add 1 -roi $x 1 $y 1 $z 1 0 1 point -odt float
print OUT "f= $f\n";
close OUT;
}
close IN;

How to combine columns that have the same headers within 1 file using Awk or Bash

I would like to know how to combine columns with duplicate headers in a file using bash/sed/awk.
x y x y
s1 3 4 6 10
s2 3 9 10 7
s3 7 1 3 2
to :
x y
s1 9 14
s2 13 16
s3 10 3
$ cat file
x y x y
s1 3 4 6 10
s2 3 9 10 7
s3 7 1 3 2
$ cat tst.awk
NR==1 {
for (i=1;i<=NF;i++) {
flds[$i] = flds[$i] " " i+1
}
printf "%-3s",""
for (hdr in flds) {
printf "%3s",hdr
}
print ""
next
}
{
printf "%-3s",$1
for (hdr in flds) {
n = split(flds[hdr],fldNrs)
sum = 0
for (i=1; i<=n; i++) {
sum += $(fldNrs[i])
}
printf "%3d",sum
}
print ""
}
$ awk -f tst.awk file
x y
s1 9 14
s2 13 16
s3 10 3
$ time awk -f ./tst.awk file
x y
s1 9 14
s2 13 16
s3 10 3
real 0m0.265s
user 0m0.030s
sys 0m0.108s
Adjust the printf lines in the obvious ways for different output formatting if you like.
Here's the bash equivalent in response to the comments elsethread. Do NOT use this, the awk solution is the right one, this is just to show how you should write it in bash IF you wanted to do that for some inexplicable reason:
$ cat tst.sh
declare -A flds
while IFS= read -r rec
do
lineNr=$(( lineNr + 1 ))
set -- $rec
if (( lineNr == 1 ))
then
fldNr=1
for fld
do
fldNr=$(( fldNr + 1 ))
flds[$fld]+=" $fldNr"
done
printf "%-3s" ""
for hdr in "${!flds[#]}"
do
printf "%3s" "$hdr"
done
printf "\n"
else
printf "%-3s" "$1"
for hdr in "${!flds[#]}"
do
fldNrs=( ${flds[$hdr]} )
sum=0
for fldNr in "${fldNrs[#]}"
do
eval val="\$$fldNr"
sum=$(( sum + val ))
done
printf "%3d" "$sum"
done
printf "\n"
fi
done < "$1"
$
$ time ./tst.sh file
x y
s1 9 14
s2 13 16
s3 10 3
real 0m0.062s
user 0m0.031s
sys 0m0.046s
Note that it runs in roughly the same order of magnitude duration as the awk script (see comments elsethread). Caveat - I never write bash scripts for processing text files so I'm not claiming the above bash script is perfect, just an example of how to approach it in bash for comparison with the other script in this thread that I claimed should be rewritten!
This not a one line. You can do it using Bash v4, Bash's dictonaries, and some shell tools.
Execute the script below with the name of the file to process a parameter
bash script_below.sh your_file
Here is the script:
declare -A coltofield
headerdone=0
# Take the first line of the input file and extract all fields
# and their position. Start with position value 2 because of the
# format of the following lines
while read line; do
colnum=$(echo $line | cut -d "=" -f 1)
field=$(echo $line | cut -d "=" -f 2)
coltofield[$colnum]=$field
done < <(head -n 1 $1 | sed -e 's/^[[:space:]]*//;' -e 's/[[:space:]]*$//;' -e 's/[[:space:]]\+/\n/g;' | nl -v 2 -n ln | sed -e 's/[[:space:]]\+/=/g;')
# Read the rest of the file starting with the second line
while read line; do
declare -A computation
declare varname
# Turn the line in key value pair. The key is the position of
# the value in the line
while read value; do
vcolnum=$(echo $value | cut -d "=" -f 1)
vvalue=$(echo $value | cut -d "=" -f 2)
# The first value is the line variable name
# (s1, s2)
if [[ $vcolnum == "1" ]]; then
varname=$vvalue
continue
fi
# Get the name of the field by the column
# position
field=${coltofield[$vcolnum]}
# Add the value to the current sum for this field
computation[$field]=$((computation[$field]+${vvalue}))
done < <(echo $line | sed -e 's/^[[:space:]]*//;' -e 's/[[:space:]]*$//;' -e 's/[[:space:]]\+/\n/g;' | nl -n ln | sed -e 's/[[:space:]]\+/=/g;')
if [[ $headerdone == "0" ]]; then
echo -e -n "\t"
for key in ${!computation[#]}; do echo -n -e "$key\t" ; done; echo
headerdone=1
fi
echo -n -e "$varname\t"
for value in ${computation[#]}; do echo -n -e "$value\t"; done; echo
computation=()
done < <(tail -n +2 $1)
Yet another AWK alternative:
$ cat f
x y x y
s1 3 4 6 10
s2 3 9 10 7
s3 7 1 3 2
$ cat f.awk
BEGIN {
OFS="\t";
}
NR==1 {
#need header for 1st column
for(f=NF; f>=1; --f)
$(f+1) = $f;
$1="";
for(f=1; f<=NF; ++f)
fld2hdr[f]=$f;
}
{
for(f=1; f<=NF; ++f)
if($f ~ /^[0-9]/)
colValues[fld2hdr[f]]+=$f;
else
colValues[fld2hdr[f]]=$f;
for (i in colValues)
row = row colValues[i] OFS;
print row;
split("", colValues);
row=""
}
$ awk -f f.awk f
x y
s1 9 14
s2 13 16
s3 10 3
$ awk 'BEGIN{print " x y"} a=$2+$4, b=$3+$5 {print $1, a, b}' file
x y
s1 9 14
s2 13 16
s3 10 3
No doubt there is a better way to display the heading but my awk is a little sketchy.
Here's a Perl solution, just for fun:
cat table.txt | perl -e'#h=grep{$_}split/\s+/,<>;while(#l=grep{$_}split/\s+/,<>){for$i(1..$#l){$t{$l[0]}{$h[$i-1]}+=$l[$i]}};printf " %s\n",(join" ",sort keys%{$t{(keys%t)[0]}});for$h(sort keys%t){printf"$h %s\n",(join " ",map{sprintf"%2d",$_}#{$t{$h}}{sort keys%{$t{$h}}})};'

Resources