Tester si la variable contient une nouvelle ligne (POSIX)

8

Je sais que certains obus acceptent ce genre de test:

t() { [[ $var == *$'\n'* ]] && res=yes || res=no
      printf '%s ' "$res";
    }

var='ab
cd'
t
var='abcd'
t
echo

à l'exécution:

$ bash ./script
yes no
  1. Quel est l'équivalent de travail POSIX (tiret)

  2. Est-ce que ce qui suit est un moyen fiable de tester?

    nl='
    '
    
    t() {  case "$var" in
               *$nl* ) res=yes ;;
               * ) res=no ;;
           esac
           printf '%s ' "$res"
         }
    
    var='ab
    cd'
    t
    var='abcd'
    t
    echo
Isaac
la source

Réponses:

12

Vous pouvez mettre une nouvelle ligne dure dans une variable et faire correspondre le motif avec case.

$ cat nl.sh
#!/bin/sh
nl='
'
case "$1" in
    *$nl*)  echo has newline ;;
    *)      echo no newline  ;;
esac

$ dash nl.sh $'foo\nbar'
has newline
$ dash nl.sh $'foobar'
no newline

La manière alternative de créer la nouvelle ligne est quelque chose comme ceci:

nl=$(printf "\nx"); nl=${nl%x}

La substitution de commande évidente ne fonctionne pas car les retours à la ligne de fin sont supprimés par la substitution.

ilkkachu
la source
5

Oui,

nl='
'
case $var in
  (*"$nl"*) echo yes;;
  (*)       echo no;;
esac

(par principe, j'aime citer toutes les extensions de variables à l'intérieur du casemodèle, sauf si je veux qu'elles soient traitées comme un modèle, bien qu'ici cela ne fasse aucune différence car $nlil ne contient pas de caractères génériques).

ou

case $var in
  (*'
'*) echo yes;;
  (*) echo no;;
esac

Si tous fonctionnent et sont conformes à POSIX, et ce que j'utiliserais pour cela. Si vous supprimez le (s, cela fonctionnerait même dans l'ancien shell Bourne.

Pour une autre façon de définir la $nlvariable:

eval "$(printf 'nl="\n"')"

Notez qu'il $'\n'est prévu de l'inclure dans la prochaine version de la norme POSIX . Il est déjà soutenu par ksh93, zsh, bash, mksh, busybox et FreeBSD shau moins (en Février 2018).

Quant à savoir si le test que vous avez est suffisant, vous avez un test pour les deux cas, ce serait donc tester tous les chemins de code.

Il y a actuellement quelque chose qui n'est pas clairement spécifié dans la spécification POSIX: si *correspond à une chaîne qui contient des séquences d'octets qui ne forment pas des caractères valides ou si les variables de shell peuvent contenir ces chaînes.

En pratique, en dehors des yashvariables dont les caractères ne peuvent contenir que des caractères, et en dehors des octets NUL (qui ne zshpeuvent pas être shell mais stockés dans leurs variables), *$nl*doivent correspondre sur n'importe quelle chaîne qui contient $nlmême si elles contiennent des séquences d'octets qui ne sont pas valides caractères (comme $'\x80'en UTF-8).

Certaines findimplémentations, par exemple, ne pourraient pas correspondre avec -name "*$nl*"elles, donc si vous testez un nouveau shell et si vous avez l'intention de traiter des choses qui ne sont pas garanties comme du texte (comme les noms de fichiers), vous voudrez peut-être lui ajouter un cas de test. Comme avec:

test=$(printf '\200\n\200')

dans un environnement local UTF-8.

Stéphane Chazelas
la source