Melhor solução shell quando as linhas em branco podem ser canalizadas para o wc

1

Eu tenho um código que faz algo assim:

#!/bin/sh
CONTENTS=$(cat "somefile")
RELEVANT_LINES=$(echo "$CONTENTS" | grep -E "SEARCHEXPR")
COUNT=$(echo "$RELEVANT_LINES" | wc -l)

Eu achei aborrecido que este código não tenha saído da mesma forma se não houvesse nenhuma correspondência, em comparação com a saída correta dada pela substituição da terceira linha por:

COUNT=$(echo "$CONTENTS" | grep -E "SEARCHEXPR" | wc -l)

Eu eventualmente rastreei o fato de que quando não havia nenhuma correspondência, RELEVANT_LINES estava sendo definido para a string vazia, e echo estava exibindo uma string vazia de uma linha + \ n para uma contagem de linha de 1.

Eu tentei usar printf e echo -n na terceira linha, mas não consegui encontrar a solução alternativa elegante e acabei usando COUNT=$(echo "$RELEVANT_LINES" | grep '0' | wc -l) (todas as linhas contêm um zero) para evitar ter que regexar o filtro todo o arquivo de origem duas vezes.

Isso não pode estar certo, mas não consigo entender a correção correta.

Eu não deixei cair o script com -eq '' porque não tinha certeza de que seria tão robusto e prefiro canalizar diretamente para wc para pureza pura.

Alguma sugestão de como obter o conteúdo do arquivo em uma variável, para distinguir claramente zero vs uma linha depois de ser filtrada pelo grep? :)

    
por Stilez 14.01.2018 / 14:25

1 resposta

4

Usar CONTENTS para manter o arquivo apenas em echo novamente é um pouco redundante, você poderia apenas

lines=$(cat "somefile" | grep -E "SEARCHEXPR")

ou melhor

lines=$(grep -E "SEARCHEXPR" "somefile")

Se você quiser apenas a contagem de linhas correspondentes, use grep -c

count=$(grep -c -E "SEARCHEXPR" "somefile")

O problema imediato que você vê é causado pelo fato de que echo gera sempre a nova linha. No caso em que você obtém pelo menos uma linha a partir das substituições de comandos, isso realmente ajuda, já que substituições de comandos removem novas linhas à direita . Ele também funciona com linhas vazias finais, tente ver: x=$(echo foo; echo; echo); echo "$x" .

Se você quiser processar as linhas no shell script de alguma outra forma, além de contar, o armazenamento do texto em uma variável pode não ser o melhor. Você poderia tentar

for line in $lines ; do 
    something with "$line"
done

Mas isso tem os problemas comuns com variáveis não citadas, ou seja, globalização de nomes de arquivos e divisão de palavras no espaço em branco. Uma linha contendo "foo bar doo", seria vista como três, já que, por padrão, os espaços também se dividem.

Você pode querer usar um while read loop, mas, para isso, um shell que suporte a substituição de processos pode ser bom. Consulte BashFAQ 001 e não muda após encontrar & enquanto href="https://unix.stackexchange.com/questions/143958/in-bash-read-after-a-pipe-is-not-setting-values"> e .

    
por 14.01.2018 / 14:40