Pseudo arquivos para dados temporários

98

Muitas vezes, eu quero alimentar dados de string relativamente curtos (embora possam ser várias linhas) para programas de linha de comando que aceitam apenas entradas de arquivos (por exemplo, wdiff) de forma repetida. Claro que posso criar um ou mais arquivos temporários, salvar a string e executar o comando com o nome do arquivo como parâmetro. Mas parece-me que este procedimento seria altamente ineficiente se os dados fossem realmente gravados no disco e também poderia prejudicar o disco mais do que o necessário se eu repetir esse procedimento várias vezes, por exemplo, se eu quiser alimentar linhas únicas de texto longo arquivos para wdiff. Existe uma maneira recomendada de contornar isso, digamos, usando pseudo arquivos, como pipes, para armazenar os dados temporariamente sem gravá-los no disco (ou gravá-los apenas se excederem um comprimento crítico). Observe que o wdiff usa dois argumentos e,wdiff <"text".

highsciguy
fonte
Isso pode ser resolvido via xargs?
NN
Não sei, mas não seria óbvio para mim como. Tanto quanto eu entendi xargsfaria as linhas de entrada dos argumentos de seqüência de caracteres do arquivo para o comando. Mas eu preciso do contrário.
highsciguy
@rahmu Eu dei uma olhada, mas acho que a configuração do problema é um pouco diferente lá. Pelo menos não vejo como as respostas ajudariam. A resposta aceita para produzir arquivos temporários corretamente é essencialmente o que eu não devo evitar, se não houver algum tipo de buffer que realmente impeça a gravação dos arquivos. Eu tenho um entendimento limitado de como os arquivos temporários funcionam!
highsciguy
O que há de errado echo $data_are_here | dumb_program?
vonbrand
1
Isso suportaria apenas um arquivo de entrada e nem todos os programas liam do stdin.
highsciguy

Respostas:

55

Use um pipe nomeado . A título ilustrativo:

mkfifo fifo
echo -e "hello world\nnext line\nline 3" > fifo

O -ecomando echo para interpretar corretamente a nova linha escape ( \n). Isso bloqueará, ou seja, seu shell travará até que algo leia os dados do pipe.

Abra outro shell em algum lugar e no mesmo diretório:

cat fifo

Você lerá o eco, que liberará o outro shell. Embora o canal exista como um nó de arquivo no disco, os dados que passam por ele não; tudo acontece na memória. Você pode colocar em segundo plano ( &) o eco.

O pipe possui um buffer de 64k (no linux) e, como um soquete, bloqueará o gravador quando estiver cheio, para que você não perca dados enquanto não matar prematuramente o gravador.

Cachinhos Dourados
fonte
Ok, obrigado, isso funciona também com dois pipes nomeados e wdiff. Mas pensei entender que há uma certa (pequena) quantidade de memória disponível para o pipe como buffer. O que acontece se eu exceder o tamanho do buffer?
highsciguy
Eu adicionei um parágrafo final sobre esse assunto.
GOLDILOCKS
3
/tmpé configurado na maioria das distros para usar um tmpfssistema de arquivos que esteja na RAM. Quando você escreve um arquivo, /tmpele vai diretamente para a sua RAM, o que faz com que seja uma boa resposta para arquivos semi-resilientes que precisam ser acessados ​​rapidamente e reescritos muitas vezes.
129

No Bash, você pode usar a command1 <( command0 )sintaxe de redirecionamento, que redireciona command0o stdout do sistema e o passa para um command1que aceita um nome de arquivo como argumento da linha de comando. Isso é chamado de substituição de processo .

Alguns programas que usam argumentos de linha de comando de nome de arquivo realmente precisam de um arquivo de acesso aleatório real, portanto, essa técnica não funcionará para eles. No entanto, ele funciona bem com wdiff:

user@host:/path$ wdiff <( echo hello; echo hello1 ) <( echo hello; echo hello2 )
hello
[-hello1-]
{+hello2+}

Em segundo plano, isso cria um FIFO, canaliza o comando dentro <( )do FIFO e passa o descritor de arquivo do FIFO como argumento. Para ver o que está acontecendo, tente usá-lo echopara imprimir o argumento sem fazer nada:

user@host:/path$ echo <( echo hello )
/dev/fd/63

Criar um pipe nomeado é mais flexível (se você deseja escrever lógica de redirecionamento complicada usando vários processos), mas para muitos propósitos isso é suficiente e é obviamente mais fácil de usar.

Há também a >( )sintaxe para quando você deseja usá-lo como saída, por exemplo

$ someprogram --logfile >( gzip > out.log.gz )

Consulte também a folha de dicas sobre redirecionamentos do Bash para obter técnicas relacionadas.

Caracol mecânico
fonte
Isso não é suportado no KSH
chanchal1987
5
O ksh inventou isso. Você está usando uma variante de ksh que que não apoiá-lo
Neil McGuigan
2
Alguns programas que usam argumentos de linha de comando de nome de arquivo realmente precisam de um arquivo de acesso aleatório real, portanto, essa técnica não funcionará para eles. O que você faz nesses casos? Por exemplo, ssh -F <(vagrant ssh-config) defaultseria muito bom, mas infelizmente.
Sukima
10

O wdiff é um caso especial porque requer 2 argumentos de nome de arquivo, mas para todos os comandos que exigem apenas 1 argumento e que se recusam teimosamente a aceitar qualquer coisa, exceto um argumento de nome de arquivo, existem 2 opções:

  • O nome do arquivo '-' (ou seja, um sinal de menos) funciona cerca de metade do tempo. Parece depender do comando em questão e se o desenvolvedor do comando intercepta esse caso e o manipula conforme o esperado. por exemplo

    $> ls | gato -

  • Existe um arquivo psuedo chamado / dev / stdin que existe no linux e pode ser usado se um nome de arquivo for absolutamente necessário para um comando. É mais provável que funcione, pois não requer nenhum tratamento de nome de arquivo especial do comando. Se um fifo funcionar, ou o método de substituição do processo bash funcionar, também deve funcionar e não é específico do shell. por exemplo

    $> ls | cat / dev / stdin

dabuntu
fonte
1
menos e openssl como / dev / stdin em vez de / dev / fd / NUM :-)
enguia ghEEz