Qual é a diferença entre <<
, <<<
e < <
no bash?
Qual é a diferença entre <<
, <<<
e < <
no bash?
Respostas:
Aqui documento
<<
é conhecido como here-document
estrutura. Você deixa o programa saber qual será o texto final e, sempre que esse delimitador for visto, ele lerá todas as coisas que você deu ao programa como entrada e executará uma tarefa nele.
Aqui está o que eu quero dizer:
$ wc << EOF
> one two three
> four five
> EOF
2 5 24
Neste exemplo, dizemos wc
ao programa para aguardar a EOF
string, digite cinco palavras e depois digita EOF
para sinalizar que estamos dando entrada. Na verdade, é semelhante a correr wc
sozinho, digitando palavras e pressionandoCtrlD
No bash, eles são implementados por meio de arquivos temporários, geralmente no formato /tmp/sh-thd.<random string>
, enquanto no painel são implementados como pipes anônimos. Isso pode ser observado através do rastreamento de chamadas do sistema com o strace
comando Substitua bash
por sh
para ver como /bin/sh
executa esse redirecionamento.
$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'
Here string
<<<
é conhecido como here-string
. Em vez de digitar texto, você fornece uma sequência de texto pré-criada para um programa. Por exemplo, com o programa bc
que podemos fazer bc <<< 5*4
para obter saída para esse caso específico, não é necessário executar o bc interativamente.
As strings here no bash são implementadas por meio de arquivos temporários, geralmente no formato /tmp/sh-thd.<random string>
, que são desvinculados posteriormente, fazendo com que ocupem algum espaço de memória temporariamente, mas não apareçam na lista de /tmp
entradas do diretório, e existam efetivamente como arquivos anônimos, que ainda podem ser referenciado por meio do descritor de arquivo pelo próprio shell e esse descritor de arquivo ser herdado pelo comando e posteriormente duplicado no descritor de arquivo 0 (stdin) via dup2()
função. Isso pode ser observado via
$ ls -l /proc/self/fd/ <<< "TEST"
total 0
lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted)
lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4
lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4
lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd
E via rastreamento syscalls (saída reduzida para facilitar a leitura; observe como o arquivo temporário é aberto como fd 3, dados gravados nele, em seguida, é reaberto com o O_RDONLY
sinalizador como fd 4 e depois desvinculado, depois dup2()
para o fd 0, que é herdado cat
posteriormente. ):
$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"'
execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0
...
strace: Process 10229 attached
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid 10229] write(3, "TEST", 4) = 4
[pid 10229] write(3, "\n", 1) = 1
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4
[pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0
[pid 10229] dup2(4, 0) = 0
[pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0
...
[pid 10229] read(0, "TEST\n", 131072) = 5
[pid 10229] write(1, "TEST\n", 5TEST
) = 5
[pid 10229] read(0, "", 131072) = 0
[pid 10229] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
Opinião: potencialmente porque as strings here usam arquivos de texto temporários, é a possível razão pela qual as strings here sempre inserem uma nova linha à direita, já que o arquivo de texto por definição POSIX precisa ter linhas que terminam em caracteres de nova linha.
Substituição de Processo
Como tldp.org explica,
A substituição de processo alimenta a saída de um processo (ou processos) no padrão de outro processo.
Portanto, na verdade, isso é semelhante ao canal stdout de um comando para o outro, por exemplo echo foobar barfoo | wc
. Mas observe: na página de manual do bash, você verá que é indicado como <(list)
. Então, basicamente, você pode redirecionar a saída de vários comandos (!).
Nota: tecnicamente quando você diz < <
que não está se referindo a uma coisa, mas dois redirecionamentos com redirecionamento único <
e de processo de saída de <( . . .)
.
Agora, o que acontece se processarmos apenas a substituição?
$ echo <(echo bar)
/dev/fd/63
Como você pode ver, o shell cria um descritor temporário de arquivo para /dev/fd/63
onde vai a saída (que, de acordo com a resposta de Gilles , é um canal anônimo). Isso significa <
que redireciona esse descritor de arquivo como entrada para um comando.
Um exemplo muito simples seria fazer a substituição do processo da saída de dois comandos echo no wc:
$ wc < <(echo bar;echo foo)
2 2 8
Então, aqui fazemos o shell criar um descritor de arquivo para toda a saída que ocorre entre parênteses e redirecioná-la como entrada para wc
. Como esperado, o wc recebe esse fluxo de dois comandos de eco, que por si só produziriam duas linhas, cada uma com uma palavra, e apropriadamente, temos 2 palavras, 2 linhas e 6 caracteres mais duas novas linhas contadas.
Nota: A substituição de processo pode ser chamada de bashismo (um comando ou estrutura utilizável em shells avançados como bash
, mas não especificado pelo POSIX), mas foi implementada ksh
antes da existência do bash como página de manual do ksh e esta resposta sugere. Cascas como tcsh
e mksh
no entanto não têm substituição de processo. Então, como podemos redirecionar a saída de vários comandos para outro comando sem substituição de processo? Agrupamento e tubulação!
$ (echo foo;echo bar) | wc
2 2 8
Efetivamente, é o mesmo do exemplo acima. No entanto, isso é diferente sob a cobertura da substituição do processo, uma vez que fazemos stdout de todo o subshell e stdin wc
vinculados ao pipe . Por outro lado, a substituição do processo faz com que um comando leia um descritor de arquivo temporário.
Portanto, se podemos agrupar com tubulação, por que precisamos de substituição de processo? Porque às vezes não podemos usar tubulações. Considere o exemplo abaixo - comparando saídas de dois comandos com diff
(que precisa de dois arquivos e, neste caso, estamos fornecendo dois descritores de arquivos)
diff <(ls /bin) <(ls /usr/bin)
< <
é usado quando alguém está obtendo stdin de uma substituição de processo . Tal comando pode parecer: cmd1 < <(cmd2)
. Por exemplo,wc < <(date)
< <
não é uma coisa por si só, no caso de substituição de processo, é apenas um <
seguido por outra coisa que acontece no início<
<<<
foi implementado pela porta Unix do Plan 9 rc shell e depois adotado pelo zsh, bash e ksh93. Eu não chamaria isso de basismo.
echo 'foo' | read; echo ${REPLY}
vai não voltar foo
, porque read
é iniciado em uma sub-shell - tubulação inicia uma sub-shell. No entanto, read < <(echo 'foo'); echo ${REPLY}
retorna corretamente foo
, porque não há subcasca.
< <
é um erro de sintaxe:
$ cat < <
bash: syntax error near unexpected token `<'
< <()
é a substituição do processo ( <()
) combinada com o redirecionamento ( <
):
Um exemplo artificial:
$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63
Com a substituição do processo, o caminho para o descritor de arquivo é usado como um nome de arquivo. Caso você não queira (ou não possa) usar um nome de arquivo diretamente, combine a substituição do processo com o redirecionamento.
Para ser claro, não há < <
operador.
<()
fornece uma coisa parecida com o nome do arquivo, por isso é geralmente mais útil - < <()
está substituindo o stdin onde pode não ser necessário. Em wc
, o último passa a ser mais útil. Pode ser menos útil em outro lugar
< <
é um erro de sintaxe, você provavelmente quer dizer command1 < <( command2 )
que é um redirecionamento simples de entrada seguido por uma substituição de processo e é muito semelhante, mas não equivalente a:
command2 | command1
A diferença assumindo que você está executando bash
é command1
executada em um subshell no segundo caso, enquanto é executada no shell atual no primeiro. Isso significa que as variáveis definidas command1
não seriam perdidas com a variante de substituição do processo.
< <
dará um erro de sintaxe. O uso adequado é o seguinte:
Explicando com a ajuda de exemplos:
Exemplo para < <()
:
while read line;do
echo $line
done< <(ls)
No exemplo acima, a entrada para o loop while virá do ls
comando que pode ser lido linha por linha e echo
ed no loop.
<()
é usado para substituição de processo. Mais informações e exemplos <()
podem ser encontrados neste link: