Guidelines

This site is for tech Q&A. Please keep your posts focused on the subject at hand.

Ask one question at a time. Don't conflate multiple problems into a single question.

Make sure to include all relevant information in your posts. Try to avoid linking to external sites.

Links to documentation are fine, but in addition you should also quote the relevant parts in your posts.

0 votes
284 views
284 views

I have a shell function for validating IPv4 address strings:

is_ipv4_addr() {
  IFS='.'
  declare -a octets=( ${1:-} )
  if [ "${#octets[*]}" -eq 4 ]; then
    for octet in "${octets[@]}"; do
      if [[ ! "$octet" =~ ^[0-9]+$ ]] || [ "$octet" -lt 0 ] || [ "$octet" -gt 255 ]; then
        return 2
      fi
    done
    return 0
  fi
  return 1
}

However, shellcheck complains about the unquoted ${1:-} and adding quotes around it would break splitting the string at dots. I know I could just disable this particular check with a comment, but that always seems like an ugly workaround to me, so I decided to use mapfile instead:

is_ipv4_addr() {
  mapfile -d. -t octets <<<"${1:-}"
  if [ "${#octets[*]}" -eq 4 ]; then
    for octet in "${octets[@]}"; do
      if [[ ! "$octet" =~ ^[0-9]+$ ]] || [ "$octet" -lt 0 ] || [ "$octet" -gt 255 ]; then
        return 2
      fi
    done
    return 0
  fi
  return 1
}

But now validation always fails, even for valid IPv4 addresses like 8.8.8.8. The returned status code is 2, so the function thinks that one of the octets is incorrect. Why?

in Scripting
by (115)
2 19 33
edit history

Please log in or register to answer this question.

1 Answer

0 votes
 

The issue is caused by the here-string, which implicitly adds a trailing newline to the string. If you add an echo statement before the return

if [[ ! "$octet" =~ ^[0-9]+$ ]] || [ "$octet" -lt 0 ] || [ "$octet" -gt 255 ]; then
  echo "-${octet}-"
  return 2
fi

you'll see output like this:

$ is_ipv4_addr '1.2.3.4'
-1-
-2-
-3-
-4
-

To fix the issue use process substitution instead of the here-doc. Make sure the command you use for echoing the string in that substituted statement also does not add a trailing newline, e.g.

mapfile -d. -t octets < <(echo -n "${1:-}")

or

mapfile -d. -t octets < <(printf "%s" "${1:-}")

Note that the parameter -d is not available prior to Bash 4.4. For older versions you need to use something like this instead:

mapfile -t octets < <(echo -n "${1:-}" | tr '.' "\n")

edited by
by (115)
2 19 33
edit history
...