Como eu testo se uma variável tem múltiplas linhas não-brancas no bash?

7

Digamos que eu tenha duas variáveis no bash:

MULTILINE="I have
more than one line"
SINGLE_LINE="I only have one line
"

Eu quero detectar quando uma variável realmente contém mais de uma linha de texto, desconsiderando os caracteres extra de nova linha.

Então isso:

if [ some test on "$MULTILINE" ]; then echo 'yes'; else echo 'no'; fi

imprimiria yes e isso:

if [ some test on "$SINGLE_LINE" ]; then echo 'yes'; else echo 'no'; fi

imprimiria no .

Para o meu caso específico, acho que não preciso me preocupar em liderar as linhas em branco, mas não faria mal saber como fazer isso.

Como posso fazer isso?

    
por jpmc26 11.08.2014 / 07:55

4 respostas

4

A solução mais simples que eu conheço é:

if (( $(grep -c . <<<"$MULTILINE") > 1 ))

por exemplo:

VAR="a
b"
if (( $(grep -c . <<<"$VAR") > 1 )); then
  echo VAR has more than one line
else
  echo VAR has at most one line
fi

== >

VAR has more than one line

O texto acima ignora todas as linhas em branco: à frente, à direita e no interior. Mas observe que não é possível ter uma linha em branco interior a menos que haja pelo menos duas linhas não em branco, portanto sua existência não pode alterar a questão de haver mais de uma linha após o corte das linhas em branco iniciais e finais.

    
por 11.08.2014 / 17:52
3
$ echo "$MULTILINE" | wc -l
2

$ echo "$SINGLE_LINE" | wc -l
2

$ echo "$SINGLE_LINE" | sed -re '/^$/d' | wc -l
1

$ echo "$MULTILINE" | sed -re '/^$/d' | wc -l
2

Veja link
para saber mais sobre como cortar / excluir espaços em branco e linhas vazias usando sed.

Agora, escreva suas citações internas if expression ... use $( ... ) para obter o número de linhas e teste o número:

if [ "$(echo "$MULTILINE" | sed -re '/^$/d' | wc -l)" -gt 1 ]; then 
  echo 'more than one line'; 
else 
  echo 'single or no line'; 
fi
    
por 11.08.2014 / 09:34
0

Uma pequena modificação no este código deve ser feita. Você pode colocá-lo em seu próprio script para reutilização assim:

#!/bin/bash
nlhit=""
for (( i=0; i<${#1}; i++ )); do
    if [[ "${1:$i:1}" == $'\n' ]]; then
        nlhit="1"
    elif [[ "$nlhit" == "1" ]]; then
        exit 1
    fi
done

exit 0

Então você pode usá-lo assim (assumindo que você nomeou o script anterior multiline-check.sh ):

#!/bin/bash

EMPTYLINE=""
BLANKLINE="    "
ONLYLINES="


"

MULTILINE="I have
more than one line"
SINGLE_LINE="I only have one line
"
SECOND_LINE="
I begin with a newline"


echo -n "EMPTYLINE Check: "
multiline-check.sh "$EMPTYLINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "BLANKLINE Check: "
multiline-check.sh "$BLANKLINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "ONLYLINES Check: "
multiline-check.sh "$ONLYLINES"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "MULTILINE Check: "
multiline-check.sh "$MULTILINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "SINGLE_LINE Check: "
multiline-check.sh "$SINGLE_LINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi

echo -n "SECOND_LINE Check: "
multiline-check.sh "$SECOND_LINE"
if [ $? -eq 1 ]; then echo "Yes"; else echo "No"; fi
    
por 11.08.2014 / 08:05
0

Ignorando linhas em branco à direita

Aqui está uma abordagem usando awk :

echo "$TEST" | tac | awk 'f==0 && /./ {f=NR} END{if(f==NR){exit 0}; exit 1 }' && echo "Found A Single Line"

Como funciona:

  • echo "$ TEST"

    Isso pega qualquer variável de shell que nos interessa e envia para o padrão.

  • tac

    Isso inverte a ordem das linhas para que a última linha seja retornada primeiro. Depois de executar tac , as linhas finais se tornam as linhas principais.

    (O nome tac é o reverso de cat , porque o tac faz o que o cat faz, mas ao contrário).

  • awk 'f==0 && /./ {f=NR} END{if(f==NR){exit 0}; exit 1 }'

    Isso armazena o número da linha da primeira linha não vazia na variável f . Depois de ler todas as linhas, ele compara f com o número total de linhas, NR . se f for igual a NR , teríamos apenas uma única linha (ignorando os espaços em branco iniciais) e sairíamos com o código 0. Se houvesse uma ou mais linhas depois da primeira linha em branco, ela sairia com o código '.

  • && echo "Found A Single Line"

    Se awk sair com o código 0, a instrução echo será executada.

Ignorando as linhas em branco iniciais e finais

Ao criar uma variável awk adicional, podemos estender o teste para ignorar as linhas em branco iniciais e finais:

echo "$TEST" | awk 'first==0 && /./ {first=NR} /./ {last=NR} END{if(first==last){exit 0}; exit 1 }' && echo " Found A Single Line"

Como essa versão do código awk manipula espaços em branco iniciais e finais, tac não é mais necessário.

Tendo o código awk um pedaço de cada vez:

  • first==0 && /./ {first=NR}

    Se a variável first for zero (ou ainda não tiver sido definida) e a linha tiver um caractere, qualquer caractere, defina first como o número da linha. Quando awk terminar de ler as linhas, first será definido para o número da linha da primeira linha não vazia.

  • /./ {last=NR}

    Se a linha tiver algum caractere, defina a variável last como o número da linha atual. Quando awk terminar de ler todas as linhas, essa variável terá o número da linha da última linha não vazia.

  • END{if(first==last){exit 0}; exit 1 }

    Isso é executado depois que todas as linhas foram lidas. Se first for igual a last , teremos linhas zero ou não-brancas e awk sairá com o código 0 . Caso contrário, sai com o código 1 . O script de shell pode testar o código de saída normalmente com if declarações ou && ou || .

por 11.08.2014 / 09:09

Tags