8

I'm writing my first BASH script , I have some experience with c and c# so I think the logic of the program is correct..it's just the syntax is so complicated because apparently there are billions of ways to write the same thing!

Anyway here is the script: it simply checks if the argument (string) is contained in a certain file . If so it stores each line of the file in an array and writes an item of the array in a file. I'm sure there must be easier ways to achieve that but I want to do some practice with bash loops

    #!/bin/bash

NOME=$1
c=0


#IF NAME IS FOUND IN THE PHONEBOOK THANK 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 does returns the only the second item of myArray (myArray[2] or the second line of the file)..why?

3
  • What relevance has the \t? Both the text and the rest of the code just refer to lines. Apr 2, 2013 at 21:26
  • Not a direct answer, so it's just a comment, but I'd do this with one a one liner grep; grep -P -o "(?<=$NOME\t).+"
    – phemmer
    Apr 2, 2013 at 21:34
  • @Patrick Classic X-Y problem, so I think your comment should be an answer. It does the right thing after all.
    – l0b0
    Apr 3, 2013 at 8:50

3 Answers 3

11
IFS=$'\n' a=($(cat phonebook.txt))
for i in $(seq ${#a[*]}); do
    [[ ${a[$i-1]} = $name ]] && echo "${a[$i]}"
done

In Bash 4 IFS=$'\n' a=($(cat phonebook.txt)) can be replaced with mapfile -t a < phonebook.txt.

grep -A1 prints one line after the match. -x disables regex like -F but it only matches complete lines.

grep -x "$name" -A1 phonebook.txt | tail -n1
6
  • You may want to validate the contents of the file first. Up to you.
    – Wildcard
    Apr 20, 2017 at 3:33
  • -x does not disable regex. Jun 8, 2020 at 9:55
  • Leaving that $(cat phonebook.txt) unquoted in that list context invokes split+glob on the expansion. Here, you want the split part, but not the glob part, so it should be disabled (set -o noglob). Jun 8, 2020 at 9:56
  • It should be grep -xFe "$name" -A1 phonebook.txt or grep -xFA1 -- "$name" phonebook.txt or it won't work properly if $name starts with - or $POSIXLY_CORRECT is in the environment. Jun 8, 2020 at 9:58
  • 1
    In bash, it's either [[ $string = $pattern ]] or [[ $string = "$otherstring" ]]. Or IOW, you need to quote word expansions on the right hand side of the = operator inside [[...]] if you don't want it to be taken as a pattern. Jun 8, 2020 at 10:02
2
index=0
while read line; do
  myArray[index]="$line"
done < inputfile

Newer versions of bash support associative arrays. That would make it easier:

declare -A myArray
while read name; do
  read number
  myArray[name]="$number"
done < inputfile

echo ${myArray[name]}
8
  • and how would you return the item of myArray[name + 1] ? Apr 2, 2013 at 21:43
  • In the indexed example you already had the solution thus I didn't copy it. Apr 2, 2013 at 22:36
  • May I be wrong, but using such pipe would not make myArray "invisible" to outside of loop??? Jan 19, 2015 at 22:27
  • @FredericYesidPeñaSánchez Neither the question nor my answer contains a pipeline thus I don't know what you are talking about. Jan 19, 2015 at 23:26
  • "while read name;" looks to me like a pipeline, anyway, i found the "declare -a array" fixes troubles i got before, thanks!!! +1 Jan 19, 2015 at 23:31
2

In a question titled "BASH reading txt file and storing in array" I feel readarray deserves a mention. An example of this method I use to read test files into an array would be:

readarray -t arrayIPblacklist < /etc/postfix/IP-black-list.txt

The foregoing loads a file of IP addresses- separated by newlines- into an array called "arrayIPblacklist". Would work on your phonebook file. readarray was introduced in bash 4, so this method won't work on older hosts running earlier bash versions.

The -t option strips the newline delimiters from each element, as most of the time you don't want them included.

$ seq 3 | bash -c 'readarray a; printf "<%q>\n" "${a[@]}"'
<$'1\n'>
<$'2\n'>
<$'3\n'>
$ seq 3 | bash -c 'readarray -t a; printf "<%q>\n" "${a[@]}"'
<1>
<2>
<3>
2
  • Note that the accepted answer already mentioned mapfile which is another (albeit more confusing) name for the readarray builtin. Jun 8, 2020 at 9:53
  • My bad- didn't see that. Thanks @StéphaneChazelas !
    – F1Linux
    Jun 8, 2020 at 9:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .