Como o produtor do pipe pode dizer ao consumidor de tubo que ele atingiu 'Fim do Arquivo'? "(un-named-pipe, not named-pipe)

1

Eu tenho um aplicativo que exige que um produtor envie nomes de arquivo para um consumidor , e produtor indique ao consumidor quando o último nome de arquivo foi enviado e o fim do arquivo foi atingido.

Para simplificar, no exemplo a seguir, produtor é demonstrado com echo e printf , enquanto o consumidor é demonstrado com cat . Eu tentei extrapolar o método "here file" sem sucesso, usando <<EOF para indicar ao produtor-wrapper (se tal coisa existe) o que procurar como uma indicação de fim do arquivo . Se funcionou, cat deve filtrar EOF da saída.

Ex 1)

entrada

{
echo "Hello World!" 
printf '\x04' 
echo "EOF"
} <<EOF |\
cat

saída

bash: warning: here-document at line 146 delimited by end-of-file (wanted 'EOF')
Hello World!
EOF

Ex 2)

entrada

{ 
echo "Hello World!" 
printf '\x04' 
echo "EOF"
} |\
cat <<EOF

saída

bash: warning: here-document at line 153 delimited by end-of-file (wanted 'EOF')

É correto que o método "here files" para indicar o delimitador funcione apenas para texto estático e não para texto criado dinamicamente?

- a aplicação real -

inotifywait -m --format '%w%f' /Dir |  <consumer>

O consumidor está aguardando que os arquivos sejam gravados no diretório / Dir. Seria bom se, quando um arquivo "/ Dir / EOF" fosse escrito, o consumidor detectasse a condição lógica de fim de arquivo simplesmente escrevendo o shell script da seguinte forma:

inotifywait -m --format '%w%f' /Dir |<</Dir/EOF  <consumer>

- Em resposta à resposta do Giles -

É teoricamente possível implementar

cat <<EOF
hello
world
EOF

como

SpecialSymbol="EOF"
{
    echo hello
    echo world
    echo $SpecialSymbol
} |\
while read Line; do 
  if [[ $Line == $SpecialSymbol ]]
    break
  else 
    echo $Line
  fi
done |\
cat

Por teoricamente possível, quero dizer "ele suportaria padrões de uso existentes e só permitiria padrões de uso extras que anteriormente eram sintaxes ilegais?" - significando que nenhum código legal existente seria quebrado.

    
por Craig Hicks 31.08.2017 / 01:28

3 respostas

5

Para um pipe, o final do arquivo é visto pelo consumidor (s) assim que todos os produtores tiverem fechado o descritor de arquivo para o canal e o consumidor tiver lido todos os dados.

Então, em:

{
  echo foo
  echo bar
} | cat

cat verá o fim do arquivo assim que o segundo echo terminar e cat ler foo\n e bar\n . Não há mais nada para você fazer.

É importante ter em mente que, se alguns dos comandos do lado esquerdo do pipe iniciarem algum processo em segundo plano, esse processo em segundo plano herdará um fd no canal (seu stdout), portanto cat não verá eof até que esse processo também morra ou feche sua stdout. Como em:

{
  echo foo
  sleep 10 &
  echo bar
} | cat

Você vê cat não retornar antes de 10 segundos se passarem.

Aqui, você pode querer redirecionar o stdout de sleep para algo como /dev/null se não quiser que sua (não) saída seja alimentada para cat :

{
  echo foo
  sleep 10 > /dev/null &
  echo bar
} | cat

Se você quiser que a extremidade de gravação do canal seja fechada antes que o último comando na subcamada à esquerda do | seja executado, é possível fechar o stdout ou redirecionar para essa sub-rede no meio do subshell com exec como:

{
  echo foo
  exec > /dev/null
  sleep 10
} | (cat; echo "cat is now gone")

No entanto, observe que a maioria das shells ainda aguardará essa subshell, além do comando cat . Então, enquanto você verá cat is now gone imediatamente (após foo ser lido), você ainda terá que esperar 10 segundos para que todo o pipeline seja concluído. Claro, nesse exemplo acima, faria mais sentido escrevê-lo:

echo foo | cat
sleep 10

<<ANYTHING...content...ANYTHING é um documento aqui, é para tornar o stdin de comando um arquivo que contém o conteúdo . Não seria útil lá. é um byte que, quando lido de um terminal, faz com que os dados mantidos por um dispositivo terminal sejam liberados para a leitura do aplicativo (e quando não há dados, read() retorna 0, o que significa fim de arquivo). Mais uma vez, não é de nenhuma utilidade aqui.

    
por 31.08.2017 / 13:21
4

O problema com sua tentativa é que o que você chama de "o método do arquivo aqui" simplesmente não existe. Aqui, os documentos são um recurso da sintaxe da linguagem de programação shell. Eles não são interpretados por aplicativos como cat .

Considere um script de shell com um documento aqui:

cat <<EOF
hello
world
EOF

A maneira como funciona é que, como parte da análise do script, o interpretador de shell vê a construção sintática do documento aqui. Quando ele executa essas linhas de código, o intérprete reúne os dados e os transmite como entrada para cat . Dependendo do shell, isso é essencialmente equivalente a

echo 'hello
world
' >/tmp/heredoc.tmp
cat </tmp/heredoc.tmp
rm /tmp/heredoc.tmp

(mas com o melhor gerenciamento do arquivo temporário) ou

echo 'hello
world
' | cat

Se você tem echo EOF e deseja que isso indique o final de um documento aqui, a saída do comando echo deve ser um shell.

{
  echo 'cat <<EOF'
  echo hello
  echo world
  echo EOF
} | sh

Isso funciona, mas raramente é útil.

Nada disso ajuda você a fazer o que se propõe a fazer. Não há nada mágico sobre um documento aqui que faz com que seja mais difícil do que qualquer outra entrada. O fim do arquivo não é um marcador que é transmitido pelo canal de entrada, é uma condição do canal de entrada. Quando a entrada é um arquivo regular, a condição de fim de arquivo é acionada quando o aplicativo tenta ler após o último byte do arquivo. Quando a entrada é um canal (chamado ou não), a condição de fim de arquivo é acionada quando o aplicativo tenta ler, mas o produtor fechou o canal.

Geralmente é o suficiente para escrever

producer | consumer

Quando producer sai, a extremidade de gravação do canal será fechada, pois nenhum processo a mantém aberta por mais tempo. Se você quiser que o pipe seja fechado antes, organize o produtor para fechar sua saída padrão ( exec >&- em sh, close(1) em C).

Observe que, para chegar ao fim do arquivo em um canal, todos os processos que tiveram a extremidade de gravação do canal aberta devem tê-lo fechado. Se o produtor executar processos em segundo plano, convém definir o sinalizador de fechamento no exec no canal ( fcntl(1, F_SETFD, fcntl(1, F_GETFC, 0) | FD_CLOEXEC) ) antes de forquecê-los.

    
por 01.09.2017 / 02:01
3

Se você quiser usar um FIFO, então você pode fazer isso, é claro:

script 1

FIFO_PATH="/path/to/fifo"
exec 3<"$FIFO_PATH" # stalls until FIFO is opened for writing, too
while read line; do
    : whatever
done <&3
exec 3<&-
: continue script

script 2

FIFO_PATH="/path/to/fifo"
exec 3>"$FIFO_PATH" # stalls until FIFO is opened for reading, too
echo foo >&4
exec 4>&-
: continue script
    
por 31.08.2017 / 13:02