BASH Les pièges du mode numérique

L’interpréteur bash embarque un mode arithmétique très pratique pour des calculs sur des entiers.

Par exemple pour tester si une chaîne est de la forme aaa.bbb.ccc.ddd où les aaa, … ddd sont des entiers entre 0 et 255 (adresse IPv4) on peut être tenter d’extraire ce qui est séparé par des ‘.’ et de tester que l’on ne dépasse pas 255.

`
#!/bin/bash

set -euo pipefail
# shopt -o -s nounset

# validation function one parameter “$IP” required
function isIPv4()
{
(( $# == 1 )) || return 1
declare v="" # v without numeric/integer attribute
declare -a values
IFS=’.’ read -a values <<< "$1"
declare -i nvals=${#values[@]}
(( nvals == 4 )) || return 1
declare -i vn=9

declare -p values v vn &>>$FLOG

for v in "${values[@]}"
do
  # declare vn="$v" || return 1 # si -i et v="ttt" unbound variable (avec set -u)
  set +u
    vn="${v:-}"
  set -u
    (( vn >= 0 )) || return 1
    (( vn <= 255 )) || return 1
    # a value of "xxx" gives 0 in numeric context automatically
    if (( vn == 0 )); then
       # [[ "$v" ]] || return 1
       [[ "W${v}" == "W0" ]] || return 1
    fi
done
return 0

}

# validation function one parameter “$IP” required
isIP()
{
(( $# == 1 )) || return 1
declare v="" # v without numeric/integer attribute
declare -a values # =( “placeholder” )

IFS='.' read -a values <<< "$1"

set – “${values[@]:+}” break $good

set -- "${values[@]:-}"
(( $# == 4 )) || return 1

for v
do
  set +u
    declare -i vn="${v}"
  set -u
    (( vn >= 0 )) || return 1
    (( vn <= 255 )) || return 1
    # a value of "xxx" gives 0 in numeric context automatically
    if (( vn == 0 )); then
       # [[ "$v" ]] || return 1
       [[ "W${v}" == "W0" ]] || return 1
    fi
done
return 0

}

# run the given preducate :
function vraifaux() {
"$@" && echo ‘vrai’ || echo “faux”
}

test_IPs() {
declare IP res res2
printf " IP\t\t\tresult\tres2\n"
for IP
do
res=$(vraifaux isIPv4 “$IP”)
res2=$(vraifaux isIP “$IP”)
printf “”%s"\t\t%s\t%s\n" “$IP” “$res” "$res2"
done
}

do_tests() {
declare vide="" space=" " good="10.67.24.198"
test_IPs “$good” “10.67.24.0” "10.67.24"
echo

# set -x
QQQ="sss"
test_IPs “$good” “$vide” “$space” “10.67.24.QQQ”
}

(( $# == 0 )) && do_tests || test_IPs “$@”

`

Le nouveau widget d’édition est une vraie catastrophe :rage:

Lançcons notre test intégré
`
fp2x@drhpcmsa:/tmp$ ./test_IP.sh
IP result res2
"10.67.24.198" vrai vrai
"10.67.24.0" vrai vrai
"10.67.24" faux faux

IP result res2
"10.67.24.198" vrai vrai
"" faux faux
" " faux faux
"10.67.24.QQQ" faux faux
`

On pourrait se dire que c’est gagné! Un lecteur attentif aura remarqué les bidouilles set +u/set -u
En fait nous n’avons que repoussé le problème.
fp2x@drhpcmsa:/tmp$ AA="BB" BB="CC" CC="DD" DD="10" ./test_IP.sh 10.10.10.10 AA. BB.CC.DD IP result res2 "10.10.10.10" vrai vrai "AA.BB.CC.DD" vrai vrai

La chaîne “AA.BB.CC.DD” est évaluée comme “10.10.10.10” et passe le test!

D’ailleurs ces fonctions sont loin d’être robustes
fp2x@drhpcmsa:/tmp$ AA="BB" BB="CC" CC="DD" DD="AA" ./test_IP.sh 10.10.10.10 AA.BB.CC.DD IP result res2 "10.10.10.10" vrai vrai ./test_IP.sh: ligne 21: DD : dépassement du niveau de récursivité dans l'expression (le symbole erroné est "DD")

On peut rendre isIP plus robuste mais cela demande d’utiliser d’autres techniques comme par exemple les expressions rationnelles et cela est laissé comme exercice au lecteur :smile:

Cordialement,
Regards,
Mit freundlichen Grüßen,
مع تحياتي الخالصة

F. Petitjean
Bureau Veritas

Je viens de trouver dans un autre message ma manière d’avoir le verbatim sur plusieurs lignes ce serait avec 3 backtics

#!/bin/bash

set -euo pipefail
# shopt -o -s nounset

# validation function  one parameter "$IP" required
isIP()
{
    (( $# == 1 )) || return 1
    declare v="" # v without numeric/integer attribute
    declare -a values  # =( "placeholder" )

    IFS='.' read -a values <<< "$1"
 #  set -- "${values[@]:+}"   break $good
    set -- "${values[@]:-}"
    (( $# == 4 )) || return 1

   for v
    do
      set +u
        declare -i vn="${v}"
      set -u
        (( vn >= 0 )) || return 1
        (( vn <= 255 )) || return 1
        # a value of "xxx" gives 0 in numeric context automatically
        if (( vn == 0 )); then
           # [[ "$v" ]] || return 1
           [[ "W${v}" == "W0" ]] || return 1
        fi
    done
    return 0
}
```
Et c'est en couleur !  Que demande le peuple?

Cordialement,
Regards,
Mit freundlichen Grüßen,
مع  تحياتي الخالصة  
---
F. Petitjean
```
# ;email.b98;"naej"v
      >,v
      |:<"F. Petit"<
 v  ,a<
 >aab*1-:2+:c-:7-2/:88*5++:9-9-:a9++:b-:9+:d-:7a++:1-: aa+-:4+:d+:3+:a9+-88v
 @_,#! #:<+2*aa+8+9:-d:+b:-c:+6:-a:-6*bb-+2*88:+b:-f:+b:-b:+a:+5:+4:-d:*ab*<

```