Bash reading a txt file and saving to an array

I am writing my first Bash script, I have some experience with C and C #, so I believe that the program logic is correct, just the syntax is so complicated that, apparently, there are many different ways. Write the same thing!

Here is a script, it just checks if the argument (string) is contained in a specific file. If so, it saves each line of the file in the array and writes the element of the array to the file. I'm sure there should be simpler ways for this, but I want to practice a little with Bash loops

#!/bin/bash NOME=$1 c=0 #IF NAME IS FOUND IN THE PHONEBOOK THEN STORE EACH LINE OF THE FILE INTO ARRAY #ONCE THE ARRAY IS DONE GET THE INDEX OF MATCHING NAME AND RETURN ARRAY[INDEX+1] if grep "$NOME" /root/phonebook.txt ; then echo "CREATING ARRAY" while read line do myArray[$c]=$line # store line c=$(expr $c + 1) # increase counter by 1 done < /root/phonebook.txt else echo "Name not found" fi c=0 for i in myArray; do if myArray[$i]="$NOME" ; then echo ${myArray[i+1]} >> /root/numbertocall.txt fi done 

This code only returns the second element of myArray ( myArray[2] ) or the second line of the file, why?

+4
source share
2 answers

The first part (where you are building the array) looks fine, but the second part has a couple of serious errors:

  • for i in myArray; - this loop is executed once, and $ i is set to "myArray". In this case, you want $ i to iterate over the myArray indices, so you need to use

     for i in "${!myArray[@]}" 

    or

     for ((i=0; i<${#a[@]}; i++)) 

    (although I usually prefer the former, as it will work with non-contiguous and associative arrays).

    In addition, you do not need ; unless do is on the same line (in the shell ; basically equivalent to a line break, so the semicolon at the end of the line is redundant).

  • if myArray[$i]="$NOME" ; then if myArray[$i]="$NOME" ; then - the if accepts the command and therefore will treat myArray[$i]="$NOME" as the destination command, which is not at all what you wanted. To compare strings, you can use the test command or its synonym [

     if [ "${myArray[i]}" = "$NOME" ]; then 

    or conditional expression bash

     if [[ "${myArray[i]}" = "$NOME" ]]; then 

    Both are very similar, but the conditional expression has a cleaner syntax (for example, in a test command, > redirects the output, and \> compares strings; in [[ ]] simple > means comparing).

    In either case, you need to use the appropriate $ expression for myArray, or it will be interpreted as a literal. On the other hand, you do not need $ before i in "$ {myArray [i]}" because it is in the context of a numeric expression and therefore will automatically expand.

    Finally, note that the spaces between the elements are absolutely necessary - in the shell, spaces are very important separators, and not just for readability, as is usually the case in c.

+2
source

1. This is what you wrote with a little tweaking.

 #!/bin/bash NOME=$1 #IF NAME IS FOUND IN THE PHONE-BOOK **THEN** READ THE PHONE BOOK LINES INTO AN ARRAY VARIABLE #ONCE THE ARRAY IS COMPLETED, GET THE INDEX OF MATCHING LINE AND RETURN ARRAY[INDEX+1] c=0 if grep "$NOME" /root/phonebook.txt ; then echo "CREATING ARRAY...." IFS= while read -r line #IFS= in case you want to preserve leading and trailing spaces do myArray[c]=$line # put line in the array c=$((c+1)) # increase counter by 1 done < /root/phonebook.txt for i in ${!myArray[@]}; do if myArray[i]="$NOME" ; then echo ${myArray[i+1]} >> /root/numbertocall.txt fi done else echo "Name not found" fi 

2. But you can also read the array and stop the loop this way:

 #!/bin/bash NOME=$1 c=0 if grep "$NOME" /root/phonebook.txt ; then echo "CREATING ARRAY...." readarray myArray < /root/phonebook.txt for i in ${!myArray[@]}; do if myArray[i]="$NOME" ; then echo ${myArray[i+1]} >> /root/numbertocall.txt break # stop looping fi done else echo "Name not found" fi exit 0 

3.- The following improves the situation. Suppose a) $ NAME matches the entire line containing it, and b) there is always one line after the found $ NOME, this will work; if not (if $ NOME might be the last line in the phone book), then you need to make small adjustments.

 !/bin/bash PHONEBOOK="/root/phonebook.txt" NUMBERTOCALL="/root/numbertocall.txt" NOME="$1" myline="" myline=$(grep -A1 "$NOME" "$PHONEBOOK" | sed '1d') if [ -z "$myline" ]; then echo "Name not found :-(" else echo -n "$NOME FOUND.... " echo "$myline" >> "$NUMBERTOCALL" echo " .... AND SAVED! :-)" fi exit 0 
+2
source

All Articles