Arrays and functions

Hi Guys! I need to solve this.
I want an array to be created by a certain calculation for which I created a function. Now this array is not getting created. See script below I want array b to be the factorial value of array element a. Help is needed. Thanks!

#!/bin/bash

echo "Number of factorials:"
read h

for (( i = 0; $i < h; i++ ))
do
echo "Enter the factor $i:"
read d
a[$i]=$d
b[$i]=$(fun $d)
done
echo ${a[*]}
echo ${b[*]}


fun()
{
j=`expr 1 + $1`
z=1
f=0

for (( i = 1; $i < $j; i++ ))
do
f=`expr $j - $i`
z=`expr $z \* $f`
done
return $z
}

Couple of things:

  1. define function before you use it (probably best at top of script)
  2. result should be echoed not returned (ie change return $z to echo $z )

also, you are using bash. no need for ancient var=`expr $i - $j` etc. There is now var=$(( i - j )) . $ is not required in arithmetic context (your for loop, a=$d ...).

Well I take that as good practice but I really did not find program behaving any different.
My problem is with this part below.
b[$i]=$(fun $d)
I want that function to be used for creating values that is fed into array. It simply does not work.
Any help!

There was help. Did you try the suggestions? If you did, it'd look something like this:

#!/bin/bash
fun() {
        # makes these local to the function.
        declare -i j z f i n=$1

        j=$(( 1 + n ))
        z=1
        f=0

        for (( i = 1; i < j; i++ ))
        do
                f=$(( j - i ))
                z=$(( z * f ))
        done

        echo "$z" # THIS WAS YOUR PRIMARY FAULT!
}

echo "Number of factorials:"
read h

for (( i = 0; i < h; i++ ))
do
        echo "Enter the factor $i:"
        read d
        a=$d
        b=$(fun $d)
done

echo ${a[*]}
echo ${b[*]}

Well I re-wrote the whole thing as follows and it neatly gives me the required results. I look forward to improvements from you. Thanks.

#!/bin/bash

fun()
{
j=`expr 1 + $1`
z=1
f=0
for (( i = 1; $i < $j; i++ ))
do
f=`expr $j - $i`
z=`expr $z \* $f`
done
echo $z
}

echo -n "Enter the number of factors: "
read b
i=0

while [ $i -lt $b ]
do
echo -n "Enter the factor: "
read d
a=("${a[@]}" "$(fun $d)") #a=($(fun $d)). the previous wrong code, now corrected.
echo "The factorial for $d is  ${a[$i]}"
i=$(( i + 1 ))
done

echo ""
echo "Total elements in the arrary A are ${#a[*]}"
echo ""

for (( i = 0; i < ${#a[*]}; i++ ))
do
echo "a[$i] = ${a[$i]}"
done
echo ""

Hello friends! this is again some work on creating factorial. I am unable to locate the mistake, it gives very unique output. Kindly, have a look and sort it for me.

#!/bin/bash

fator(){
j=$(( $1 + 1 ))
z=1
f=0
# echo $j
for (( p = 1; $p < $j; p++ ))
do
f=`expr $j - $p`
z=`expr $z \* $f`
done
echo "$z"
return
}


k=("$@")
echo ${k[*]}
echo "No of arguments: $#"
echo "length of array is: ${#k[*]}"

i=0

while [ $i -lt ${#k[*]} ]
do
echo "k[$i] = ${k[$i]}"
a=("${a[$i]}" "$(fator ${k[$i]})")
echo "a[$i] = ${a[$i]}"

i=$(( $i + 1 ))

done
echo ""

I used the command

./fac3b 7 6 8 9

Just try yourself. It gives output for only one argument. I want for all of them. fac3b is a kind of filename, I give to revisions that I perform. Help is most solicited.

I get this.

$ ./fator.sh 1 2 3 4
1 2 3 4
No of arguments: 4
length of array is: 4
k[0] = 1
a[0] =
k[1] = 2
a[1] = 2
k[2] = 3
a[2] =
k[3] = 4
a[3] =

$

What am I supposed to be seeing here?

You didn't need arrays to do any of that, fyi.

1 Like

Your problem was here:

a=("${a[$i]}" "$(fator ${k[$i]})")

Should be a=(${a[@]} "$(fator ${k[$i]})") ... or why not a=$(fator ${k})

Here is a bit of a cleanup to use bash expressions and for loops everywhere instead of while and expr:

#!/bin/bash
fator() {
  ((j=$1+1))
  z=1
  f=0
  for (( p = 1; $p < $j; p++ )) {
    ((f=j-p))
    ((z=z*f))
  }
  echo "$z"
}
 
k=("$@")
echo ${k
[*]}
echo "No of arguments: $#"
echo "length of array is: ${#k
[*]}"
for ((i=0;i<${#k
[*]};i++)) {
   echo "k[$i] = ${k}"
   a="$(fator ${k})"
   echo "a[$i] = ${a}"
}
echo ""

Again as as Corona688 said there is no real need for arrays here unless you plan to use them later on in the script.

1 Like

Array is needed for further using as input values.

---------- Post updated at 01:27 PM ---------- Previous update was at 01:24 PM ----------

what is the technical difference in arrays, a[@] and a[*].
besides you have omitted the $ signs to make things tidy, however, I am lacking adequate knowledge on rules. Any links!
Thanks!

Hi! guys! again with some problem solving issue. The problem is that I have few files in a folder some are with extension .txt and some are not so how to put them all with .txt extension minus the shell script which i usually put in the same folder. I tried making it out, but there is some problem. Here is the code and details below.

#!/bin/bash
# program for file renaming
echo "Enter path: "
read p # pattern should be *.txt, *.pdf or simply *
a=($p)

for (( i = 0; i < ${#a[*]}; i++ ))
do
echo "a[$i] = ${a[$i]}"
done

echo "Enter the file format: "
read f   # can be given as txt or pdf

for (( i = 0; i < ${#a[*]}; i++ ))
do
for (( ${a[$i]} != "frn2c" ))
do
v[$i]=("$(cat ${a[$i]} | sed 's/\(.\)/\1 \n/g')")
echo ${v[$i]}
c[$i]=("$(mv ${v[$i]} ${v[$i]}.$f)")
echo ${c[$i]}
done
done

And, the problem is denoted with this line

for (( ${a[$i]} != "frn2c" ))

The error is

./frn2c: line 17: syntax error: arithmetic expression required
./frn2c: line 17: syntax error: `(( ${a[$i]} != "frn2c" ))

It means what it says. (( )) brackets are used for arithmetic expressions. If you want to do things like comparing strings, use [[ ]].

[[ ${a[$i]} != "frn2c" ]]

1 Like

You can't use for [[ ... ]] , can you? The requestor should use if / while [[ ... ]] to run the conditional branch.

1 Like

Thanks! the square braces did the trick, now here is one more thing!
Compare the two scripts, the first one gives the results, the second one does not.

#!/bin/bash
# program for data mining

echo "This programs read text files and brings out the"
echo "word count of first 50 most used words in a file."

a=(*.txt)

for (( i = 0; i < ${#a[*]}; i++ ))
do
echo "a[$i] = ${a[$i]}"
done
echo ""


for (( i = 0; i < ${#a[*]}; i++ ))
do
printf "=======================" >> lsf2dout.txt
printf "\n" >> lsf2dout.txt
printf "Name of the file: ${a[$i]}" >> lsf2dout.txt
printf "\n" >> lsf2dout.txt

cat ${a[$i]} | sed 's/\([[:space:]]\)/\1 \n/g' | sed 's/^[ \t]*//' | sed \
's/[ \t]*$//' | sed '/^$/d' | sed 's/^to$//g' | sed 's/^of$//g' | sed \
's/^and$//g' | sed 's/^a$//g' | sed 's/^the$//g' | sed \
's/^The$//g' | sed 's/^is$//g' | sed 's/^in$//g' | sed 's/^as$//g' | sed \
's/^for$//g' | sed 's/^by$//g' | sed 's/^on$//g' | sed 's/^at$//g' | sed \
's/^be$//g' | sed 's/^an$//g'|sed 's/^it$//g' | sed 's/^no$//g' | sed 's/^It$//g' | sed \
's/^us$//g' | sed 's/^are$//g' | sed 's/^was$//g' | sed 's/^up$//g' | tr \
-cd '[:alnum:] [:space:]'| sort | uniq -c | sort \
-nr | head -n 50 >> lsf2dout.txt

printf "\n\n" >> lsf2dout.txt
done

and the modified one below

#!/bin/bash
# program for data mining

echo "This programs read text files and brings out the"
echo "word count of first 50 most used words in a file."

a=(*.txt)

for (( i = 0; i < ${#a[*]}; i++ ))
do
echo "a[$i] = ${a[$i]}"
done
echo ""


for (( i = 0; i < ${#a[*]}; i++ ))
do
printf "=======================" >> lsf2dout.txt
printf "\n" >> lsf2dout.txt
printf "Name of the file: ${a[$i]}" >> lsf2dout.txt
printf "\n" >> lsf2dout.txt

cat ${a[$i]} | sed 's/\([[:space:]]\)/\1 \n/g' | sed 's/^[ \t]*//' | sed \
's/[ \t]*$//' | sed '/^$/d' | 
for (( i = 0; $i <= ${#g[*]}; i++ )) 
do 
sed 's/^${g[$i]}$//g'
done | tr -cd '[:alnum:] [:space:]'| sort | uniq -c | sort -nr | head -n 50 >> lsf2dout.txt

printf "\n\n" >> lsf2dout.txt
done


declare -a g=('and' 'us' 'the' 'The' 'we' 'is' 'a' 'at' 'to' 'of' 'is' 'in' 'as' 'be' 'for' 'by' 'on' 'us' 'are' 'was' 'up')

CANT LOCATE WHERE I HAVE GONE WRONG.

I'd go about that in a slightly different way.

1) Strip out all non-alphanumeric and whitespace characters
2) Change all whitespace into newlines
3) Sort
4) uniq -c
5) Sort again to get the top 50
6) head -n 50

for FILE in *.txt
do
        sed 's/[^a-zA-Z \r\t]//g' "$FILE" | 
                tr -s ' \r\t' '\n' | 
                sort |
                uniq -c |
                sort -n |
                head -n 50
done
1 Like

My problem is that I need specific array of words that should get omitted and that array you see at the bottom of the modified one. It executes for one file at a time and it should do before sorting so that I get a more productive list. So, that should be chipped in before step 5. How to do that?

Put them in exclude.txt and use grep -f -F.

for FILE in *.txt
do
        sed 's/[^a-zA-Z \r\t]//g' "$FILE" | 
                tr -s ' \r\t' '\n' | 
                grep -v -F -f exclude.txt |
                sort |
                uniq -c |
                sort -n |
                head -n 50
done
1 Like
cat ${a[$i]} | sed 's/\([[:space:]]\)/\1 \n/g' | sed 's/^[ \t]*//' | sed \
's/[ \t]*$//' | sed '/^$/d' | grep -v -F -f exclude.lst | tr -cd '[:alnum:] [:space:]'| sort \
| uniq -c | sort -nr | head -n 50 >> lsf2dout.txt

I tried out but I think only grep worked for me. The $FILE would modify original file, I think so, but I want the results to be stored separate without affecting the source txt files in the folder. I think it is good.

---------- Post updated 08-14-12 at 12:19 AM ---------- Previous update was 08-13-12 at 11:43 PM ----------

I think the code below gives exactly what I need. Thanks!

#!/bin/bash
# program for data mining
#this program needs a file named exclude.lst

echo "This programs read text files and brings out the"
echo "word count of first 50 most used words in a file."

a=(*.txt)

for (( i = 0; i < ${#a
[*]}; i++ ))
do
echo "a[$i] = ${a[$i]}"
done
echo ""

for (( i = 0; i < ${#a
[*]}; i++ ))
do
printf "=======================" >> lsf2dout.txt
printf "\n" >> lsf2dout.txt
printf "Name of the file: ${a[$i]}" >> lsf2dout.txt
printf "\n" >> lsf2dout.txt

cat ${a[$i]} | sed 's/\([[:space:]]\)/\1 \n/g' | sed 's/^[ \t]*//' | sed \
's/[ \t]*$//' | sed '/^$/d' | grep -w -v -F -f exclude.lst | tr -cd '[:alnum:] [:space:]'| sort \
| uniq -c | sort -nr | head -n 100 >> lsf2dout.txt

printf "\n\n" >> lsf2dout.txt
done