Probar si la variable contiene nueva línea (POSIX)

8

Sé que algunos proyectiles aceptan este tipo de prueba:

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

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

en ejecución:

$ bash ./script
yes no
  1. ¿Cuál es el equivalente de trabajo POSIX (guión)?

  2. ¿Es la siguiente una forma confiable de probar?

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

Respuestas:

12

Puede poner una nueva línea rígida en una variable y hacer coincidir el patrón con 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 forma alternativa de hacer la nueva línea es algo como esto:

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

La sustitución de comando obvia no funciona porque la sustitución elimina las nuevas líneas finales.

ilkkachu
fuente
5

Si,

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

(por principio, me gusta citar todas las expansiones variables dentro del casepatrón a menos que quiera que sean tratadas como un patrón, aunque aquí no hace la diferencia ya $nlque no contiene comodines).

o

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

Todos deberían funcionar y son compatibles con POSIX, y lo que usaría para eso. Si elimina el (s, incluso funcionaría en el antiguo shell Bourne.

Para otra forma de establecer la $nlvariable:

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

Tenga en cuenta que $'\n'está previsto incluirlo en la próxima versión del estándar POSIX . Ya está apoyada por ksh93, zsh, bash, mksh, busybox y FreeBSD shpor lo menos (en febrero de 2018).

En cuanto a si la prueba que tiene es suficiente, tiene una prueba para ambos casos, por lo que probaría todas las rutas de código.

Actualmente hay algo que no se especifica claramente en la especificación POSIX: si *coincide en una cadena que contiene secuencias de bytes que no forman caracteres válidos o si las variables de shell pueden contener esas cadenas.

En la práctica, aparte de yashcuyas variables solo pueden contener caracteres, y aparte de los bytes NUL (que no pueden contener shell pero zshpueden almacenar en sus variables), *$nl*deben coincidir en cualquier cadena que contenga, $nlincluso si contienen secuencias de bytes que no son válidas caracteres (como $'\x80'en UTF-8).

Algunas findimplementaciones, por ejemplo, no coincidirían con -name "*$nl*"ellas, por lo que si prueba un nuevo shell y tiene la intención de tratar con cosas que no se garantiza que sean texto (como nombres de archivos), es posible que desee agregar un caso de prueba. Al igual que con:

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

en un entorno local UTF-8.

Stéphane Chazelas
fuente