Percursos
/
Bash
Bash
/
Programa
/
Here Documents
He

Here Documents em Bash

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

Sobre Here Documents

Na programação em Bash, um here document (ou heredoc) redireciona várias linhas de entrada para um comando ou programa, como se as estivesses a escrever diretamente no terminal. É uma ferramenta poderosa para incorporar texto com várias linhas nos teus scripts sem precisares de ficheiros externos nem de manipulação complexa de strings.

Características e sintaxe principais

  1. Delimitador: um heredoc começa com o operador << seguido de uma palavra delimitadora (a que se chama muitas vezes "marcador" ou "terminador"). Este delimitador pode ser a palavra que quiseres, mas é comum usar algo como EOF, END ou TEXT para maior clareza. Para um código mais legível, podes usar algo descritivo como delimitador, por exemplo END_INSTALLATION_INSTRUCTIONS.

  2. Conteúdo: depois do << DELIMITER inicial, escreves o conteúdo que queres redirecionar. Pode ser várias linhas de texto, código ou qualquer outra coisa.

  3. Terminação: o heredoc termina quando a palavra delimitadora volta a aparecer sozinha numa linha, sem espaços em branco antes nem depois.

Sintaxe básica

command << DELIMITER
  Content line 1
  Content line 2
  ...
  Content line N
DELIMITER

Como funciona

  • O Bash lê todas as linhas entre o << DELIMITER inicial e o DELIMITER final.
  • O Bash liga este conteúdo à entrada padrão do comando.
  • O comando processa esta entrada como se estivesse a vir do teclado.

Exemplo 1: saída de texto simples

cat << EOF
This is the first line.
This is the second line.
This is the third line.
EOF

Saída:

This is the first line.
This is the second line.
This is the third line.

Neste exemplo:

  • cat é o comando.
  • << EOF inicia o heredoc com EOF como delimitador.
  • As três linhas de texto são o conteúdo.
  • EOF sozinho numa linha termina o heredoc.
  • O cat imprime então o conteúdo que recebeu.

Exemplo 2: utilização com o wc (contagem de palavras)

wc -l << END
Line 1
Line 2
Line 3
END

Saída:

3

Aqui, o wc -l conta o número de linhas. O heredoc fornece as três linhas como entrada.

Exemplo 3: passar dados a um script

O script:

#!/usr/bin/env bash

# Script to process input
while IFS= read -r line; do
  echo "Processing: $line"
done

Chama o script a partir de um prompt bash interativo com um heredoc:

./your_script << MY_DATA
Item 1
Item 2
Item 3
MY_DATA

Saída:

Processing: Item 1
Processing: Item 2
Processing: Item 3

Variações e funcionalidades avançadas

Conteúdo literal

O Bash efetua expansão de variáveis, substituição de comandos e expansão aritmética dentro de um heredoc. Neste sentido, os heredocs comportam-se como strings entre aspas duplas.

cat << EOF
The value of HOME is $HOME
The current date is $(date)
Two plus two is $((2 + 2))
EOF

Saída:

The value of HOME is /home/glennj
The current date is Thu Apr 24 13:47:32 EDT 2025
Two plus two is 4

Quando o delimitador está entre aspas (simples ou duplas), estas expansões são impedidas. O conteúdo é interpretado literalmente. É como acontece com as strings entre aspas simples.

cat << 'EOF'
The value of $HOME is not expanded here.
The result of $(date) is not executed.
Two plus two is calculated by $((2 + 2))
EOF

Saída:

The value of $HOME is not expanded here.
The result of $(date) is not executed.
Two plus two is calculated by $((2 + 2))

Remover tabulações iniciais

Se usares <<- (com um hífen no fim) em vez de <<, o Bash remove todos os carateres de tabulação iniciais de cada linha do heredoc. Isto é útil para indentar o conteúdo do heredoc dentro do script sem afetar a saída.

# Note, the leading whitespace is tab characters only, not spaces!
# The ending delimiter can have leading tabs as well.
cat <<- END
	This line has 1 leading tab.
	  This line has a leading tab and some spaces.
		This line 2 leading tabs.
	END

A saída é impressa com todas as tabulações iniciais removidas:

This line has 1 leading tab.
    This line has a leading tab and some spaces.
This line 2 leading tabs.
Caution

O autor não recomenda esta utilização. Embora possa melhorar a legibilidade do script,

  1. é fácil substituir acidentalmente os carateres de tabulação por espaços (o teu editor pode fazer isto automaticamente), e
  2. é difícil detetar a diferença entre espaços e tabulações.

Quando usar here documents

  • Entrada com várias linhas: quando precisas de fornecer várias linhas de texto a um comando.
  • Ficheiros de configuração: incorporar pequenos excertos de configuração dentro de um script.
  • Geração de código: criar código dinamicamente dentro de um script.
  • Interações em scripts: simular a entrada do utilizador para programas interativos.
  • Evitar ficheiros externos: quando queres evitar criar ficheiros temporários.

Uma utilização típica pode ser fornecer algum texto de ajuda:

#!/usr/bin/env bash

usage() {
    cat << END_USAGE
Refresh database tables.

usage: ${0##*/} [-h|--help] [-A|--no-archive]

where: --no-archive flag will _skip_ the archive jobs
END_USAGE
}

# ... parsing command line options here ...

if [[ $flag_help == "true" ]]; then
  usage
  exit 0
fi

Possíveis desvantagens

  • Documentos incorporados grandes podem tornar o teu código mais difícil de ler. Pode ser melhor publicar o teu script com a documentação em ficheiros separados.
  • Os here documents podem quebrar o fluxo do código. Podes estar numa secção de código profundamente aninhada quando queres passar algum texto a um programa. A indentação do heredoc pode parecer desconcertante em comparação com o código em volta.

Here strings

Tal como os here documents, as here strings (ou herestrings) fornecem entrada a um comando. No entanto, enquanto os heredocs são dados como um bloco de texto, as here strings são dadas como uma única string de texto. As here strings usam a sintaxe <<< "text".

tr 'a-z' 'A-Z' <<< "upper case this string"

Saída:

UPPER CASE THIS STRING

Ao contrário dos heredocs, não é necessário nenhum delimitador final.

Porquê usar here strings?

Podes usar um pipeline em vez de uma here string:

echo "upper case this string" | tr 'a-z' 'A-Z'

Então porque usar uma here string?

Considera o caso em que obténs a string como saída de um cálculo demorado e queres passar o resultado a dois comandos separados. Com pipelines, tens de executar o cálculo duas vezes:

some_long_running_calculation | first_command
some_long_running_calculation | second_command

Uma abordagem mais eficiente é capturar a saída do cálculo (usando substituição de comandos) e usar here strings para fornecer entrada aos dois comandos seguintes:

result=$( some_long_running_calculation )
first_command <<< "$result"
second_command <<< "$result"

Eis uma aplicação real desse exemplo:

  • capturar a resposta JSON a um pedido a uma API REST (que é paginado),
  • fornecer os dados JSON a um programa jq para analisar os resultados e escrevê-los num ficheiro, e depois
  • fornecer os dados JSON a outro programa jq para determinar o URL do pedido seguinte.
# initialize the output CSV file
echo "ID,VALUE" > data.csv

url='https//example.com/api/query?page=1'

while true; do
  json=$( curl "$url" )

  # convert the results part of the response into CSV
  jq -r '.results[] | [.id, .value] | @csv' <<< "$json"

  # get the URL for the next page
  url=$( jq -r '.next_url // ""' <<< "$json" )
  if [[ "$url" == "" ]]; then
    break
  fi
done >> data.csv

Repara na posição do redirecionamento da saída. Toda a saída do ciclo while será acrescentada ao ficheiro data.csv.

Here documents e here strings como redirecionamento

Como são apenas formas de redirecionamento, podem ser combinados com outras operações de redirecionamento:

cat <<< END_OF_TEXT > output.txt
This is my important text.
END_OF_TEXT

awk '...' <<< "$my_var" >> result.csv

Em resumo

Os here documents (ou heredocs) são uma forma flexível e prática de gerir entrada com várias linhas em scripts Bash. Simplificam o processo de incorporar texto e dados diretamente nos teus scripts, tornando-os mais autónomos e mais fáceis de ler.

As here strings (ou herestrings) são como os here documents, mas oferecem uma sintaxe mais simples e mais dinâmica.

Editar via GitHub A ligação abre numa nova janela ou separador