setting a positional parameter in a function in bash

304 views Asked by At

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

There are 4 answers

5
sjsam On

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.

0
mklement0 On

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/\.$//')
0
muru On

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.

0
agc On

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.