Teste se a variável contém nova linha (POSIX)

8

Eu sei que algumas conchas aceitam esse tipo de teste:

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

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

na execução:

$ bash ./script
yes no
  1. Qual é o equivalente de trabalho POSIX (traço)

  2. A seguinte é uma maneira confiável de testar?

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

Respostas:

12

Você pode colocar uma nova linha rígida em uma variável e combinar o padrão com 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

A maneira alternativa de criar a nova linha é algo como isto:

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

A substituição óbvia do comando não funciona porque as novas linhas à direita são removidas pela substituição.

ilkkachu
fonte
5

Sim,

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

(por princípio, eu gosto de citar todas as expansões variáveis ​​dentro do casepadrão, a menos que eu queira que elas sejam tratadas como um padrão, embora aqui não faça diferença, pois $nlnão contém curingas).

ou

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

Todos devem funcionar e são compatíveis com POSIX, e o que eu usaria para isso. Se você remover os (s, funcionaria até no antigo shell Bourne.

Para outra maneira de definir a $nlvariável:

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

Observe que $'\n'está planejado para inclusão na próxima versão do padrão POSIX . Já está apoiado por ksh93, zsh, bash, mksh, busybox e FreeBSD sh, pelo menos (em Fevereiro de 2018).

Quanto à questão de saber se o teste que você possui é suficiente, você tem um teste para os dois casos; portanto, estaria testando todos os caminhos de código.

Atualmente, existe algo que não está claramente especificado na especificação POSIX: se *corresponde a uma sequência que contém sequências de bytes que não formam caracteres válidos ou se variáveis ​​de shell podem conter essas sequências.

Na prática, além de yashcujas variáveis ​​podem conter apenas caracteres e além dos bytes NUL (que não possuem shell, mas zshpodem armazenar em suas variáveis), *$nl*devem corresponder a qualquer sequência que contenha, $nlmesmo que contenham sequências de bytes que não sejam válidas caracteres (como $'\x80'em UTF-8).

Algumas findimplementações, por exemplo, não combinariam com -name "*$nl*"elas; portanto, se estiver testando um novo shell e se você pretende lidar com coisas que não são garantidas como texto (como nomes de arquivos), convém adicionar um caso de teste. Como com:

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

em um código de idioma UTF-8.

Stéphane Chazelas
fonte