Criando arquivo temporário vs substituição de processo vs expansão de variável?

3

Se eu estiver fazendo algo como

  1. criando arquivo temporário

    some process generating output > temp_file
    cat  temp_file
    
  2. substituição de processos:

    cat <(some process generating output)
    
  3. de outra forma:

    cat <<<(some process generating output)
    

Eu tenho algumas dúvidas sobre isso:

  1. Existe algum limite no tamanho da saída de dados da substituição do processo <() >() ou expansão variável <<<()
  2. Qual desses é o mais rápido ou existe uma maneira de fazer isso mais rápido?

Minha saída do comando ulimit é:

bash-3.00$ ulimit -a
core file size        (blocks, -c) unlimited
data seg size         (kbytes, -d) unlimited
file size             (blocks, -f) unlimited
open files                    (-n) 256
pipe size          (512 bytes, -p) 10
stack size            (kbytes, -s) 8480
cpu time             (seconds, -t) unlimited
max user processes            (-u) 8053
virtual memory        (kbytes, -v) unlimited
    
por munish 25.09.2013 / 18:57

2 respostas

5

A substituição do processo Bash na forma de <(cmd) e >(cmd) é implementada com pipes nomeados se o sistema os suportar. O comando cmd é executado com sua entrada / saída conectada a um pipe. Quando você executa, por exemplo cat <(sleep 10; ls) você pode encontrar o canal criado no diretório /proc/pid_of_cat/fd . Este pipe nomeado é então passado como um argumento para o comando atual ( cat ).

A capacidade de buffer de um pipe pode ser estimada com um uso complicado do comando dd , que envia dados zero para a entrada padrão do comando sleep (que não faz nada). Aparentemente, o processo vai durar algum tempo para que o buffer fique cheio:

(dd if=/dev/zero bs=1 | sleep 999) &

Dê um segundo e envie o sinal USR1 para o processo dd :

pkill -USR1 dd

Isso faz com que o processo imprima estatísticas de E / S:

65537+0 records in
65536+0 records out
65536 bytes (66 kB) copied, 8.62622 s, 7.6 kB/s

No meu caso de teste, o tamanho do buffer é 64kB ( 65536B ).

Como você usa a expansão <<<(cmd) ? Estou ciente de que é uma variação dos documentos aqui que é expandida e passada para o comando em sua entrada padrão.

Espero que eu tenha lançado alguma luz sobre a questão do tamanho. Em relação à velocidade, não tenho tanta certeza, mas presumo que ambos os métodos podem fornecer uma taxa de transferência semelhante.

    
por 25.09.2013 / 21:40
4

<(cmd) é um recurso ksh encontrado também em zsh e bash chamado substituição de processo .

Em sistemas que suportam /dev/fd/n ou /proc/self/fd/n , ele é implementado com pipes e quando não com pipes nomeados temporários. Em qualquer caso, é uma forma de pipe que é um mecanismo de comunicação entre processos.

cmd1 <(cmd2)

Pode ser escrito (com canais normais):

{ cmd2 3<&- | 3<&0 <&3 3<&- cmd1 /dev/fd/3; } 3<&0

Ou (com pipes nomeados):

mkfifo /tmp/named_pipe
cmd2 > /tmp/named_pipe & cmd1 /tmp/named_pipe

Ou seja, ambos os comandos são iniciados simultaneamente e se comunicam com um pipe. Normalmente, você usaria cmd2 | cmd1 para isso, mas a substituição do processo geralmente é para os casos em que cmd1 só pode receber entrada de um nome de arquivo e não da entrada padrão ou quando mais de uma entrada é necessária, como em diff <(cmd1) <(cmd2) . / p>

Não há nenhum rlimit que o afete além dos gerais, como o número de processos, o tempo de CPU ou a memória.

O PIPEBUF relatado por algumas implementações de ulimit como bash e algumas implementações de ksh não é um rlimit, mas o tamanho máximo para o qual uma gravação em um pipe é garantida como atômica é irrelevante aqui. O tamanho do canal em si (64kB no Linux, conforme relatado por @ dsmsk80) não é realmente um limite em si. Apenas diz que é tanto que cmd2 pode gravar no pipe mesmo depois que cmd1 parou de ler a partir dele.

Há uma limitação, no entanto, em que cmd1 só pode ler desse arquivo. Por ser um pipe, ele não pode gravar no arquivo ou procurar no arquivo.

zsh tem uma terceira forma de substituição de comando usando arquivos temporários comuns:

 cmd1 =(cmd2)

chama cmd1 com um arquivo temporário que contém a saída de cmd2 . Nesse caso, cmd1 é executado após cmd2 em vez de concorrentemente. O limite do tamanho dos arquivos pode ser alcançado lá.

Não conheço nenhum shell que esteja implementando um operador <<<(...) . No entanto, há um operador <<< em zsh (inspirado na mesma operadora na porta Unix de rc ) também encontrado nas versões recentes de ksh93 e bash . É uma variação do operador << heredoc chamado herestring.

Em:

 cmd <<< something

Qual é o mesmo que o padrão:

 cmd << EOF
 something
 EOF

O shell cria um arquivo temporário com something\n como o conteúdo e alimenta isso como entrada padrão para um novo processo, desvincula esse arquivo e executa cmd nesse novo processo. Novamente, esse é um arquivo regular para que o rlimit no tamanho máximo de um arquivo seja atingido.

Agora, você pode combinar o operador <<< com $(...) (substituição de comando) para emular de alguma forma o operador zsh =(...) em bash e ksh93 :

cmd1 <<<"$(cmd2)"

Executaria cmd2 com stdout redirecionado para um canal. Na outra extremidade do canal, o shell lê a saída de cmd2 e armazena menos os caracteres de nova linha à direita e com um caractere de nova linha adicionado em um arquivo temporário e chama cmd1 com esse arquivo temporário aberto para leitura como stdin ( note que há outra limitação em que não funcionará se cmd2 output contiver caracteres NUL).

Para ser como =(...) , você teria que escrevê-lo:

cmd1 /dev/fd/3 3<<<"$(cmd3)"

Observe que o shell precisa ler toda a saída de cmd3 na memória antes de gravá-la no arquivo temporário, portanto, além do tamanho máximo do arquivo, você também pode atingir o limite de uso de memória.

    
por 26.09.2013 / 19:27