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
IT kid
  • 43
  • 4

4 Answers4

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/\.$//')
mklement0
  • 245,023
  • 45
  • 419
  • 492
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.

Community
  • 1
  • 1
sjsam
  • 19,686
  • 3
  • 43
  • 88
  • 1
    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 Dec 19 '16 at 04:44
  • @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 – sjsam Dec 19 '16 at 04:51
  • @mklement0 : Also, I agree to the second part of your comment – sjsam Dec 19 '16 at 04:52
  • 2
    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 http://mywiki.wooledge.org/BashPitfalls?highlight=%28array%29#hosts.3D.28_.24.28aws_....29_.29 – mklement0 Dec 19 '16 at 05:04
  • 1
    @mklement0 : Well said, reminds me of *there are dark corners in the Bourne shell, and people use all of them- Chet Ramey* – Мона_Сах Dec 19 '16 at 05:42
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.

muru
  • 4,232
  • 30
  • 69
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.

agc
  • 7,002
  • 2
  • 25
  • 44