Como POSIX-contar o número de linhas em uma variável de seqüência de caracteres?

10

Eu sei que posso fazer isso no Bash:

wc -l <<< "${string_variable}"

Basicamente, tudo o que encontrei envolveu <<< operador Bash.

Mas no shell POSIX, <<< é indefinido e não consegui encontrar uma abordagem alternativa por horas. Tenho certeza de que existe uma solução simples para isso, mas infelizmente não o encontrei até agora.

    
por Vlastimil 20.11.2018 / 07:40

3 respostas

10

A resposta simples é que wc -l <<< "${string_variable}" é um atalho do ksh / bash / zsh para printf "%s\n" "${string_variable}" | wc -l .

Na verdade, existem diferenças na forma <<< e em um trabalho de canal: <<< cria um arquivo temporário que é passado como entrada para o comando, enquanto | cria um canal. Em bash e pdksh / mksh (mas não em ksh93 ou zsh), o comando no lado direito do pipe é executado em um subshell. Mas essas diferenças não importam neste caso particular.

Observe que, em termos de contagem de linhas, isso pressupõe que a variável não está vazia e não termina com uma nova linha. Não terminar com uma nova linha é o caso quando a variável é o resultado de uma substituição de comando, então você obterá o resultado correto na maioria dos casos, mas você obterá 1 para a string vazia.

Existem duas diferenças entre var=$(somecommand); wc -l <<<"$var" e somecommand | wc -l : usando uma substituição de comando e uma variável temporária retira linhas em branco no final, esquece se a última linha de saída terminou em uma nova linha ou não (sempre acontece se o comando gera um arquivo de texto não vazio válido) e conta um por um se a saída estiver vazia. Se você quer preservar o resultado e contar linhas, você pode fazer isso adicionando algum texto conhecido e retirando-o no final:

output=$(somecommand; echo .)
line_count=$(($(printf "%s\n" "$output" | wc -l) - 1))
printf "The exact output is:\n%s" "${output%.}"
    
por 20.11.2018 / 08:14
2

Não compatível com os recursos internos do shell, usando utilitários externos como grep e awk com opções compatíveis com POSIX,

string_variable="one
two
three
four"

Fazendo com grep para corresponder ao início das linhas

printf '%s' "${string_variable}" | grep -c '^'
4

E com awk

printf '%s' "${string_variable}" | awk 'BEGIN { count=0 } NF { count++ } END { print count }'

Note que algumas das ferramentas GNU, especialmente, GNU grep não respeita a opção POSIXLY_CORRECT=1 para executar a versão POSIX da ferramenta. Em grep , o único comportamento afetado pela configuração da variável será a diferença no processamento da ordem dos sinalizadores de linha de comando. Da documentação (GNU grep manual), parece que

POSIXLY_CORRECT

If set, grep behaves as POSIX requires; otherwise, grep behaves more like other GNU programs. POSIX requires that options that follow file names must be treated as file names; by default, such options are permuted to the front of the operand list and are treated as options.

Veja Como usar POSIXLY_CORRECT no grep?

    
por 20.11.2018 / 07:56
0

A string <<< aqui é praticamente uma versão de uma linha do documento aqui << . O primeiro não é um recurso padrão, mas o segundo é. Você também pode usar << neste caso. Estes devem ser equivalentes:

wc -l <<< "$somevar"

wc -l << EOF
$somevar
EOF

Embora note que ambos adicionam uma nova linha extra no final de $somevar , por ex. isso imprime 6 , mesmo que a variável tenha apenas cinco linhas:

s=$'foo\n\n\nbar\n\n'
wc -l <<< "$s"

Com printf , você pode decidir se deseja a nova linha adicional ou não:

printf "%s\n" "$s" | wc -l         # 6
printf "%s"   "$s" | wc -l         # 5

Mas, observe que wc conta apenas linhas completas (ou o número de caracteres de nova linha na string). grep -c ^ também deve contar o fragmento da linha final.

s='foo'
printf "%s" "$s" | wc -l           # 0 !

printf "%s" "$s" | grep -c ^       # 1

(Claro que você também pode contar as linhas inteiramente no shell usando a expansão ${var%...} para removê-las uma de cada vez em um loop ...)

    
por 20.11.2018 / 11:44