Qual é a maneira portátil (POSIX) de conseguir a substituição do processo?

21

Algumas conchas, como bash , suportam Substituição de processos , que é uma maneira de apresentar a saída do processo como um arquivo, assim:

$ diff <(sort file1) <(sort file2)

No entanto, essa construção não é POSIX e, portanto, não é portável. Como é possível processar a substituição em uma POSIX maneira amigável (ou seja, um que funciona em /bin/sh ) ?

nota: a questão não está perguntando como diferenciar dois arquivos classificados - isso é apenas um exemplo artificial para demonstrar o processo substituição !

    
por starfry 13.09.2016 / 11:34

1 resposta

12

Esse recurso foi introduzido por ksh (em ksh86) e estava fazendo uso do recurso /dev/fd/n (adicionado independentemente em alguns sistemas BSDs e AT & T anteriormente). Em ksh e até ksh93u, não funcionaria a menos que seu sistema tivesse suporte para / dev / fd / n. zsh, bash e ksh93u+ e acima podem fazer uso de pipes nomeados temporários (pipes nomeados adicionados em SysIII eu acredito) onde / dev / fd / n não estão disponíveis.

Nos sistemas em que /dev/fd/n está disponível (o POSIX não especifica esses), você mesmo pode fazer a substituição do processo ( diff <(cmd1) <(cmd2) ) com:

{
  cmd1 4<&- | {
    # in here fd 3 points to the reading end of the pipe
    # from cmd1, while fd 0 has been restored from the original
    # stdin (saved on fd 4, now closed as no longer needed)

    cmd2 3<&- | diff /dev/fd/3 -

  } 3<&0 <&4 4<&- # restore the original stdin for cmd2

} 4<&0 # save a copy of stdin for cmd2

No entanto, como isso não funciona com ksh93 no Linux, os shell pipes são implementados com socketpairs em vez de pipes e abrindo /dev/fd/3 , onde fd3 aponta para um soquete que não funciona no Linux.

Embora o POSIX não especifique /dev/fd/n . Ele especifica pipes nomeados. Os pipes nomeados funcionam como pipes normais, exceto que você pode acessá-los a partir do sistema de arquivos. A questão aqui é que você tem que criar temporários e limpar depois, o que é difícil de fazer de forma confiável, especialmente considerando que POSIX não tem mecanismo padrão (como mktemp -d como encontrado em alguns sistemas) para criar arquivos ou diretórios temporários, e fazer o manuseio de sinal de forma portável (para limpeza após desligamento ou interrupção) também é difícil de ser feito de forma portável.

Você pode fazer algo como:

tmpfifo() (
  n=0
  until
    fifo=$1.$$.$n
    mkfifo -m 600 -- "$fifo" 2> /dev/null
  do
    n=$((n + 1))
    # give up after 20 attempts as it could be a permanent condition
    # that prevents us from creating fifos. You'd need to raise that
    # limit if you intend to create (and use at the same time)
    # more than 20 fifos in your script
    [ "$n" -lt 20 ] || exit 1
  done
  printf '%s\n' "$fifo"
)

cleanup() { rm -f -- "$fifo"; }
fifo=$(tmpfifo /tmp/fifo) || exit

cmd2 > "$fifo" & cmd1 | diff - "$fifo"
rm -f -- "$fifo"

(não está cuidando do manuseio de sinal aqui).

    
por 13.09.2016 / 15:47