Trilhas
/
Bash
Bash
/
Programa
/
Pipelines e listas de comandos
Pi

Pipelines e listas de comandos em Bash

{one: "1 exercício", many: "%{count} de exercícios", other: "%{count} exercícios"}

Sobre Pipelines e listas de comandos

Já vimos como escrever comandos simples, em que um comando é seguido de argumentos. Agora vamos ver como montar comandos mais complexos compondo comandos simples.

Entrada e saída

Antes de começar, uma rápida introdução à entrada e à saída.

Os processos têm "canais padrão de entrada e saída".

  • Um processo pode consumir entrada na "stdin".
  • Um processo pode emitir saída na "stdout".
  • Um processo pode emitir saída de erro na "stderr".

O comando tr é um exemplo bem puro disso. Tudo o que ele faz é ler texto da sua entrada, fazer transliterações de caracteres e imprimir o texto resultante na sua saída.

Vamos ver mais sobre como manipular os canais de "stdio" mais adiante, no programa.

Pipelines

Este é um dos recursos mais poderosos da programação de shell. Os pipelines permitem criar transformações sofisticadas em um fluxo de texto.

Para produzir uma lista ordenada de usuários:

cat /etc/passwd | cut -d : -f 1 | sort

O símbolo de pipe (|) conecta a saída de um comando à entrada de outro. O cut lê a saída do cat, e o sort lê a saída do cut.

Advanced
  • Por padrão, cada comando de um pipeline roda em um subshell separado. (Um subshell é um processo filho que é uma cópia do shell em execução no momento.)

  • Existe um custo de desempenho ao rodar pipelines. Se você perceber que tem pipelines longos de comandos semelhantes, considere combiná-los em um único comando. Por exemplo, pipelines que usam várias instâncias de grep, cut, sed, awk e tr geralmente podem ser combinados em um único comando awk para ganhar eficiência.

  • O status de saída de um pipeline é o status de saída do último comando do pipeline. Isso significa que, se algum comando anterior falhou mas o último comando teve êxito, o status de saída do pipeline inteiro é zero.

    Existe uma configuração do shell que pode mudar isso; se a configuração "pipefail" estiver ativada (com set -o pipefail), o status de saída do pipeline será:

    • zero se todos os comandos tiverem êxito, caso contrário
    • o último status de saída diferente de zero dos comandos do pipeline.

Listas de comandos

Uma lista de comandos é uma sequência de pipelines separados por ; (ou nova linha), && ou ||.

  • A; B é uma lista de comandos em que B é executado depois que A termina.
  • A && B, em que B só é executado se A tiver êxito (sair com status zero).
  • A || B, em que B só é executado se A falhar (sair com status diferente de zero).

O status de saída de uma lista de comandos é o status de saída do último comando executado.

Os operadores && e || podem ser encadeados de modo que o próximo comando seja executado condicionalmente, com base no status dos comandos anteriores. Por exemplo

A && B && C && D || E
  • B é executado se A tiver êxito,
  • C é executado se A e B tiverem êxito,
  • D é executado se A, B e C tiverem êxito,
  • E é executado se qualquer um de A, B, C ou D falhar.
Caution

Use esses operadores lógicos com moderação. Eles podem rapidamente levar a um código ilegível, ou a uma lógica difícil de entender.

Por exemplo, você pode pensar que estes são iguais:

if A; then B; else C; fi
A && B || C

A diferença é: quando o C é executado?

  • No primeiro trecho (a condicional), C só será executado se A falhar.
  • No segundo trecho, C será executado se A falhar ou se A tiver êxito mas B falhar!

Usos das listas de comandos

Aqui estão alguns exemplos em que as listas de comandos podem simplificar o código.

Ler blocos de linhas de um arquivo

Suponha que você tenha um arquivo de dados com informações sobre triângulos, e que cada triângulo seja representado por três linhas separadas que contêm os lados do triângulo.

$ cat triangle.dat
3
4
5
9
12
14

Você pode usar um laço while em que a condição é três comandos read separados:

while read a && read b && read c; do
    if is_pythagorean "$a" "$b" "$c"; then
        echo "$a:$b:$c is pythagorean"
    else
        echo "$a:$b:$c is not pythagorean"
    fi
done < triangle.dat

Supondo que is_pythagorean seja um comando que determina se os três lados satisfazem a equação de Pitágoras, a saída seria:

3:4:5 is pythagorean
9:12:14 is not pythagorean
Asserções

Muitas linguagens de programação têm alguma forma de asserção em que uma exceção é lançada se alguma condição falhar

assert(user == "Administrator", "wrong user");

Podemos usar um operador OR no Bash para simular essa função:

die () { # a little convenience function
    echo "$*" >&2
    exit 1
}

[[ $user == "Administrator" ]] || die "wrong user"
[[ $password == "secret!" ]]   || die "incorrect password"

Considerações de estilo

Listas de comandos longas ficam difíceis de ler muito rápido. Usar quebras de linha com generosidade pode ajudar bastante.

Considere este exemplo em que uma palavra é adicionada a um array se duas condições forem atendidas.

[[ "$word" != "$topic" ]] && [[ "$key" == "$(sorted "$topic")" ]] && anagrams+=("$candidate")

O Bash permite adicionar uma nova linha depois de um pipe ou de um operador lógico.

[[ "$word" != "$topic" ]] &&
[[ "$key" == "$(sorted "$topic")" ]] &&
anagrams+=("$candidate")

No entanto, o operador pode passar despercebido no fim da linha. Usar uma continuação de linha significa que você pode colocar o operador primeiro, o que deixa mais óbvio que a lista está continuando:

[[ "$word" != "$topic" ]] \
&& [[ "$key" == "$(sorted "$topic")" ]] \
&& anagrams+=("$candidate")
Note

Uma continuação de linha é a sequência de dois caracteres "barra invertida" e "nova linha" (\ + \n). Quando o Bash encontra essa sequência, ela é simplesmente removida do código, continuando assim a linha atual com a linha seguinte. Tome cuidado para não deixar nenhum espaço entre a barra invertida e a nova linha.

Aqui está outro exemplo

printf "%s\n" "${numbers[@]}" | bc --mathlib | sort --general-numeric-sort

ou

printf "%s\n" "${numbers[@]}" \
| bc --mathlib \
| sort --general-numeric-sort
Editar via GitHub O link abre em uma nova janela ou aba