Estou curioso sobre a teoria por trás de como os heredocs podem ser passados como um arquivo para um utilitário de linha de comando.
Recentemente, descobri que posso passar um arquivo como heredoc.
Por exemplo:
awk '{ split($0, arr, " "); print arr[2] }' <<EOF
foo bar baz
EOF
bar
Isso é vantajoso para mim por vários motivos:
- Heredocs melhoram a legibilidade para entradas de várias linhas.
- Não preciso memorizar cada sinalizador de utilitários para passar o conteúdo do arquivo da linha de comando.
- Posso usar aspas simples e duplas nos arquivos fornecidos.
- Eu posso controlar a expansão do shell.
Por exemplo:
ruby <<EOF
puts "'hello $HOME'"
EOF
'hello /Users/mbigras'
ruby <<'EOF'
puts "'hello $HOME'"
EOF
'hello $HOME'
Não estou claro o que está acontecendo. Parece que o shell pensa que o heredoc é um arquivo com conteúdo igual ao valor do heredoc. Eu usei essa técnica com gato, mas ainda não tenho certeza do que estava acontecendo:
cat <<EOL
hello world
EOL
hello world
Eu sei que cat
imprime o conteúdo de um arquivo, então presumivelmente este heredoc é um arquivo temporário de algum tipo.
Estou confuso sobre o que exatamente está acontecendo quando "passo um heredoc para um programa de linha de comando".
Aqui está um exemplo usando ansible-playbook . Passo ao utilitário um playbook como heredoc; no entanto, falha, conforme mostrado usando echo $?
:
ansible-playbook -i localhost, -c local <<EOF &>/dev/null
---
- hosts: all
gather_facts: false
tasks:
- name: Print something
debug:
msg: hello world
EOF
echo $?
5
No entanto, se eu passar o utilitário, o mesmo heredoc, mas o preceder, /dev/stdin
será bem-sucedido
ansible-playbook -i localhost, -c local /dev/stdin <<EOF &>/dev/null
---
- hosts: all
gather_facts: false
tasks:
- name: Print something
debug:
msg: hello world
EOF
echo $?
0
- O que exatamente está acontecendo quando alguém "transmite um heredoc como um arquivo"?
- Por que a primeira versão
ansible-playbook
falha, mas a segunda versão é bem-sucedida? - Qual é o significado de passar
/dev/stdin
diante do heredoc? - Por que outros utilitários gostam
ruby
ouawk
não precisam do/dev/stdin
antes do heredoc?
Você não é. Aqui-documentos fornecem entrada padrão, como um tubo. seu exemplo
é exatamente equivalente a
awk
,cat
eruby
todos lidos da entrada padrão se não receberem um nome de arquivo para ler na linha de comando. Essa é uma escolha de implementação.ansible-playbook
não lê a entrada padrão por padrão, mas requer um caminho de arquivo. Esta é uma escolha de design./dev/stdin
é provavelmente um link simbólico para , que é uma maneira de falar sobre o descritor de arquivo nº 0/dev/fd/0
do processo atual (entrada padrão). Isso é algo exposto pelo seu kernel (ou biblioteca do sistema). O comando abre como um arquivo de sistema de arquivos regular e termina lendo sua própria entrada padrão, que de outra forma teria sido ignorada.ansible-playbook
/dev/stdin
Você provavelmente também tem links
/dev/stdout
para/dev/stderr
os FDs 1 e 2, que também pode usar se estiver dizendo algo onde colocar sua saída.É um argumento para o
ansible-playbook
comando.Eles leem da entrada padrão por padrão como uma opção de design, porque são feitos para serem usados em pipelines. Eles gravam na saída padrão pelo mesmo motivo.
Um here-document é um redirecionamento para a entrada padrão de um comando, assim como
<
. Isso significa que em qualquer lugar onde você pode usar<
para redirecionar o conteúdo de um arquivo, você pode redirecionar o conteúdo de um here-document. O padrão POSIX lista here-documents juntamente com os outros operadores de redirecionamento .Em seu exemplo Ansible,
ansible-playbook
por padrão, não lê de seu fluxo de entrada padrão, pois espera um nome de arquivo. Ao fornecer/dev/stdin
o nome do arquivo e, em seguida, fornecer o documento aqui na entrada padrão, você ignora essa restrição no utilitário. O/dev/stdin
"arquivo" sempre conterá o fluxo de dados de entrada padrão do processo atual.ruby
eawk
assim como muitos outros utilitários lerão da entrada padrão , a menos que um nome de arquivo seja fornecido na linha de comando.Portanto, você está tecnicamente errado quando diz "Parece que o shell pensa que o heredoc é um arquivo com conteúdo igual ao valor do heredoc". Ele não age como um arquivo (no que diz respeito a ter um nome de arquivo e ser pesquisável), mas como um fluxo de dados na entrada padrão. Pelo menos do ponto de vista da utilidade.
A diferença é a mesma entre
e
Na primeira instância,
cat
abre o arquivofile
, mas na segunda (que também é o que acontece com um here-document), como nenhum nome de arquivo foi fornecido como argumento paracat
,cat
apenas lê seu fluxo de entrada padrão (e o shell abre o arquivo, ou fornece o documento aqui, na entrada padrão para o utilitário). O utilitário não precisa saber se os dados fornecidos vêm de um arquivo, pipe, here-document ou alguma outra fonte de dados.Como os here-documents são implementados pelo shell é de certa forma sem importância, mas pode ser através do uso de um FIFO ou mesmo com um arquivo temporário.
O que exatamente está acontecendo com here-docs depende de como o shell implementa here-doc: pode ser feito com pipes internamente como no caso de
dash
ou com descritor de arquivo temporário, como embash
. Portanto, em um caso, pode não ser possívellseek()
, mas no outro - pode ser (o que para o usuário médio significa que você pode pular o conteúdo do here-doc). Veja a resposta relacionada .Quanto ao caso de dois comandos ansible-playbook, também depende de como o comando é implementado (portanto, a menos que você leia o código-fonte, não saberá realmente). Alguns comandos simplesmente verificam se há ou não um arquivo fornecido e não suportam
stdin
. Outros comandos comoawk
eruby
- eles são projetados para esperarstdin
ou um arquivo especificado na linha de comando.O que você pode tentar fazer, no entanto, é se estiver usando Linux, execute
strace ansible-playbook ...<other args>
e veja o que ele tenta abrir, quais syscalls ocorrem, etc. Por exemplo, você verá que comstrace -e open tail /dev/stdin <<< "Jello World"
o comando tail tentará abrir/dev/stdin
como arquivo, enquantotrace -e open tail
não.