1

I'm making a function to easly convert my strings to arrays as I need to.

I am somewhat running into a weird issue. I am still new to bash and this is really bugging me. Would anybody be able to shed some light onto this?

convert.sh

#!/bin/bash
convert2array () {
read -a $1_arr <<< $1
}

mx=$(dig +short google.com mx | cut -d' ' -f 2 | sed 's/\.$//')

convert2array "$mx"

echo ${mx_arr[@]}

Output:

bash -x convert2array.sh 
++ sed 's/\.$//'
++ cut '-d ' -f 2
++ dig +short google.com mx
+ mx='alt2.aspmx.l.google.com
alt3.aspmx.l.google.com
alt1.aspmx.l.google.com
aspmx.l.google.com
alt4.aspmx.l.google.com'
+ convert2array mx
+ read -a mx_arr
+ echo 585911
585911

4 Answers 4

3

Try the following:

convert2array () {
  # Bash v4+ alternative: `readarray -t` instead of `IFS=$'\n' read -d '' -ra`
  IFS=$'\n' read -d '' -ra "$1" <<<"$2"
}

mx=$(dig +short google.com mx | cut -d' ' -f 2 | sed 's/\.$//')

convert2array mx_arr "$mx"

printf '%s\n' "${mx_arr[@]}"

As for what you tried:

  • $1 inside convert2array is not the name of your input variable, $mx, but its value.
    You need to pass in the name of the variable you want to declare (possibly after modifying the input name) explicitly, as a separate argument.

  • read by default only reads the 1st line of the input, whereas you're passing multiple lines.
    -d '' makes read read all lines, and IFS=$'\n' makes read read each line as a whole.
    In Bash v4+, using builtin readarray, IFS=$'\n' read -d '' -ra can be replaced with readarray -t.


A streamlined version of your command would be to read the dig ... output lines directly into an array, using the Bash v4+ readarray builtin:

readarray -t mx_arr < <(dig +short google.com mx | cut -d' ' -f 2 | sed 's/\.$//')

Bash v3.x alternative:

IFS=$'\n' read -d ''  -ra mx_arr < <(dig +short google.com mx | cut -d' ' -f 2 | sed 's/\.$//')
Sign up to request clarification or add additional context in comments.

Comments

2

You can directly store the dig results in the array

 declare -a results=( $( dig +short google.com mx | cut -d' ' -f 2 | sed 's/\.$//' ) )
 echo "${results[@]}"

Also, you need not use cut here, sed alone would suffice.

declare -a results=( $( dig +short google.com mx  | sed -E 's/^[[:digit:]]*[[:blank:]]*(.*)\.$/\1/') )
echo "${results[@]}"
aspmx.l.google.com
alt1.aspmx.l.google.com
alt2.aspmx.l.google.com
alt4.aspmx.l.google.com
alt3.aspmx.l.google.com

See [ bash arrays ],[ command subsctitution ] and [ positional parameters ].

You're warned : The output can be but in one format. Though ( $( .. ) ) is an anti-pattern as pointed out in comment#1 , for this case, it would suffice.

5 Comments

By double-quoting the command substitution you're invariably creating an array with a single element containing all lines, which is not the intent. Without double-quoting, the output lines are invariably subject to word-splitting and globbing, and even though that happens to work in this case, ( $(...) ) is not a generally robust way to read command output into an array (unless you set $IFS and turn off globbing, which is not worth the effort).
@mklement0 : Thanks for the pointer. I thought about it but then forgot to take the double quotes off in a rush to post an answer :) Changed now
@mklement0 : Also, I agree to the second part of your comment
I'm glad to hear that, but I really wish you'd preface your solution with a prominent warning to that effect. I believe that ( $(...) ) is an anti-pattern that should not be promoted. It's seductively simple, but it will come back to bite you - see mywiki.wooledge.org/…
@mklement0 : Well said, reminds me of there are dark corners in the Bourne shell, and people use all of them- Chet Ramey
1

Just a note on what went wrong in your function:

convert2array () {
read -a $1_arr <<< $1
}

Here, you're reading in the contents of $1, which is the name of the variable, when you want to read the contents of the variable itself. You can use indirection here:

convert2array () {
  read -a $1_arr <<< ${!1}
}

As others have noted, there are easier ways to get your output in arrays.

Comments

0

Provided the array is only a one-off, for a read-only output loop, POSIX shell can do it:

set -- `dig +short google.com mx` ; \
while [ "$2" ] ; do echo "${2%.*}" ; shift 2 ; done

Output:

alt2.aspmx.l.google.com
alt3.aspmx.l.google.com
alt4.aspmx.l.google.com
alt1.aspmx.l.google.com
aspmx.l.google.com

If the output needs further fussing, pipe the echo to whatever else it is that needs doing.


Note: The above code contains a simple kludge to avoid sed and cut -- instead just output only the even number array members, and use parameter expansion "Remove Smallest Suffix Pattern" to remove that '.' at the end.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.