>(cmd)
é uma sintaxe Bash. Por exemplo,
$ echo 42 > >(cat)
42
Isso funciona bem.
Então, espero usar esse método para passar o arquivo recém-editado e salvo como entrada para outro comando (estou usando apenas cat
como exemplo):
$ EDITOR=nano # or emacs, vi, ...
$ $EDITOR >(cat)
Então ele simplesmente congelou. Por quê?
O editor está tentando ler o texto atual do arquivo que você especificou, como editores de texto geralmente fazem. Como nada foi escrito no pipe (ainda que o pipe ainda esteja aberto para escrita), o editor ficará preso para sempre na
read()
chamada, esperando que os dados cheguem.Para fazer a ideia funcionar, você precisará criar um pipe nomeado
mkfifo
usando . Inicie o editor e (simultaneamente, em segundo plano) faça cat/echo do seu texto para o pipe – mesmo que não haja texto, ainda é importante abrir e fechar o pipe para escrita pelo menos uma vez – antes de dar o pipe para o programa "leitor". Isso pode ser difícil de conseguir no bash, pois o leitor precisa já estar em execução quando o nano for solicitado a escrever o novo texto, mas só deve ser executado depois que o nano já tiver lido o texto original.É uma ideia melhor usar
mktemp
um arquivo temporário regular.Usar
>(…)
fornece ao seu editor um descritor de arquivo representando a extremidade 'write' do pipe (enquanto cat está segurando a extremidade 'read') e um/dev/fd/###
caminho de estilo para esse descritor de arquivo.nano
,/dev/fd/63
]0
←tty,1
→tty,2
→tty,63
→pipe}Mas o editor não reconhece especificamente
/dev/fd
caminhos como "isso representa um FD que eu já tenho, então vamos usar isso" – em vez disso, o editor o trata como qualquer outro caminho, chamandoopen()
e obtendo um novo descritor de arquivo. (Ele continua sem saber que tem o FD #63.)A maneira como
/dev/fd
funciona no Linux (não apenas com pipes, mas com tentativas de reabrir "caminhos do descritor de arquivo" em geral) é que isso é tratado como uma tentativa completamente nova de abrir aquele arquivo ou objeto, em vez de duplicar o descritor de arquivo 1:1.open("/dev/fd/63", O_RDONLY)
⇒3
0
←tty,1
→tty,2
→tty,3
←pipe,63
→pipe}Portanto, se o editor abrir esse caminho para leitura , ele de fato obterá um descritor de arquivo para a extremidade 'leitura' do pipe, não para a extremidade 'gravação' – e, portanto, as
read()
chamadas subsequentes não falharão imediatamente com um erro "tentando ler de um FD somente gravação", como teria acontecido com o FD original, mas, em vez disso, bloquearão a espera por dados.Nesse sentido, o
/dev/fd
caminho do Linux para um pipe anônimo se comporta exatamente como um caminho regular para um pipe nomeado se comportaria. Se você fosse criar um pipe usandomkfifo ~/foo
, o editor também o abriria para leitura e ficaria preso tentando ler a partir dele – até que você ecoasse algo para o pipe.Mas além de tudo isso: pipes só relatam "fim do arquivo" quando o fim da gravação está completamente fechado . Então, como o editor ainda mantém, sem saber, o descritor de arquivo original #63 para o fim da 'gravação', isso significa que sempre há um escritor ativo – o próprio editor – e mesmo que ele pudesse receber alguns dados pelo pipe (por exemplo, escritos via 'echo' manual através de
/proc/PID/fd
), ele ainda continuaria esperando para sempre pela indicação de "fim do arquivo". Na verdade, o editor está em um deadlock consigo mesmo.O último problema não acontece com pipes nomeados, pois não há um descritor de arquivo aberto preexistente para o pipe – o hipotético
echo yay > ~/yay
é o único escritor, então, assim que ele sai, o editor alcança "EOF" e mostra o texto. Então, se você deseja experimentar, eu sugiro fortemente experimentar pipes nomeados para obter um ambiente mais controlado do que os métodos envolvendo/dev/fd
.