Inconsistências entre redirecionar entrada de arquivo, aqui docs e aqui strings

4

Por que isso é inconsistente? Eu esperaria aqui docs e aqui strings para ser funcionalmente equivalente ao redirecionamento de entrada de um arquivo.

$ bash --version
GNU bash, version 4.1.2(1)-release (x86_64-unknown-linux-gnu)
...

Saída esperada:

Imprime cada item da lista.

$ for server in $(<servers.txt); do echo ${server}; done
server1
server2
server3
$

Saída inesperada:

Não imprime nada.

$ for server in $(<<EOF
> server1
> server2
> server3
> EOF
> ); do echo ${server}; done
$

Saída inesperada:

Não imprime nada.

$ for server in $(<<<"server1
> server2
> server3"); do echo ${server}; done
$

Edit: Eu segui o bash usando um redirecionador de entrada, e novamente com uma string here. Aqui está a diferença de comportamento entre os dois:

Redirecionador de entrada

$ echo $(<servers.txt)

...
31929 open("servers.txt", O_RDONLY)     = 3
31929 read(3, "server1\nserver2\nserver3\n", 128) = 24
31929 write(1, "server1\nserver2\nserver3\n", 24) = 24
...

Isso gera o processo filho 31929, que abre o server.txt como FD 3 e o grava no stdout.

Aqui string

$ echo $(<<<"server1
> server2
> server3")

...
31990 open("/tmp/sh-thd-106091305575", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600) = 3
31990 write(3, "server1\nserver2\nserver3", 23) = 23
31990 write(3, "\n", 1)                 = 1
31990 open("/tmp/sh-thd-106091305575", O_RDONLY) = 4
31990 close(3)                          = 0
31990 unlink("/tmp/sh-thd-106091305575") = 0
31990 fcntl(0, F_GETFD)                 = 0
31990 fcntl(0, F_DUPFD, 10)             = 10
31990 fcntl(0, F_GETFD)                 = 0
31990 fcntl(10, F_SETFD, FD_CLOEXEC)    = 0
31990 dup2(4, 0)                        = 0
31990 close(4)                          = 0
31990 dup2(10, 0)                       = 0
31990 fcntl(10, F_GETFD)                = 0x1 (flags FD_CLOEXEC)
31990 close(10)
...

Várias etapas são realizadas aqui:

  1. O processo filho 31990 é gerado
  2. A string aqui é gravada em / tmp / sh-thd-106091305575
  3. / tmp / sh-thd-106091305575 é aberto como somente leitura (FD 4)
  4. / tmp / sh-thd-106091305575 está desvinculado (assim, quando o FD 4 for fechado, ele será excluído)
  5. stdin é enganado para FD 10
  6. FD 4 (o arquivo temporário contendo nossa string aqui) é enganado para FD 0 (stdin) É onde um processo - neste caso, o subshell bash - atuaria em stdin
  7. FD 4 é fechado e FD 10 é levado de volta para FD 0

O surpreendente é que isso acontece no exemplo do redirecionador de entrada:

31929 write(1, "server1\nserver2\nserver3\n", 24) = 24

Eu suspeito que o bash tenha um comportamento especial para este caso. Provavelmente precisará cavar a fonte para uma resposta definitiva.

    
por Zach 27.02.2014 / 02:09

2 respostas

4

$(<servers.txt) é um atalho especial no bash para $(cat <servers.txt) , conforme documentado em “Substituição de comando” . É uma exceção à sintaxe regular em que o redirecionamento seria aplicado a um comando vazio e, portanto, não faz nada, exceto sinalizar um erro se o arquivo não existir. O Bash não estende essa exceção aqui para documentos ou strings aqui: você precisa incluir cat explicitamente.

Este recurso foi adicionado no bash 2.02 (é mencionado no changelog). No código-fonte, ele é implementado pelo lógica em torno da chamada para cat_file em parse_and_execute em builtins/evalstring.c .

    
por 03.03.2014 / 01:26
3

Demasiado cansado para investigar a explicação, mas aqui está um # 2 de trabalho (faltando cat )

for server in $(cat <<EOF
server1
server2
server3
EOF
); do echo ${server}; done

e # 3 (leia a linha de uma linha de cada vez)

while read server; do
  echo ${server}
done <<<"server1
server2
server3"
    
por 27.02.2014 / 04:23