Каков переносимый (POSIX) способ добиться замены процесса?

25

Некоторые оболочки, например bash, поддерживают замену процесса, которая представляет собой способ представления результатов процесса в виде файла, например:

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

Однако эта конструкция не POSIX и, следовательно, не переносимая. Как можно заменить процесс на POSIX- дружественный метод (т. Е. Тот, который работает в /bin/sh) ?

примечание: вопрос не в том, как различить два отсортированных файла - это только надуманный пример, демонстрирующий подстановку процесса !

starfry
источник
mywiki.wooledge.org/ProcessSubstitution имеет примечание, что mywiki.wooledge.org/NamedPipes может использоваться ..
Sundeep
2
См. Также unix.stackexchange.com/a/43536/117549
Джефф Шаллер
1
POSIX не обязательно /bin/shдолжен быть оболочкой POSIX, только для того, чтобы где-то была вызвана команда, shкоторая в правильной среде совместима с POSIX. Например, в Solaris 10 /bin/shэто не оболочка POSIX, это древняя оболочка Bourne, и оболочка POSIX включена /usr/xpg4/bin/sh.
Стефан Шазелас

Ответы:

17

Эта функция была введена ksh(впервые задокументирована в ksh86) и использовала эту /dev/fd/nфункцию (добавлена ​​независимо в некоторых BSD и системах AT & T ранее). В kshи до ksh93u, он не будет работать , если ваша система не имеет поддержки / DEV / FD / н. zsh, bash и ksh93u+выше могут использовать временные именованные каналы (именованные каналы добавлены в SysIII, я считаю), где / dev / fd / n недоступны.

В системах, где это возможно (POSIX не указывает их), вы можете выполнить подстановку процесса самостоятельно ( ) с помощью:/dev/fd/ndiff <(cmd1) <(cmd2)

{
  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

Однако это не работает ksh93в Linux, так как там, каналы оболочки реализованы с парами сокетов вместо каналов и открытием, /dev/fd/3где fd 3 указывает на сокет, не работает в Linux.

Хотя POSIX не указывает . Он определяет именованные каналы. Именованные каналы работают как обычные каналы, за исключением того, что вы можете получить к ним доступ из файловой системы. Проблема здесь заключается в том, что вам нужно создавать временные и впоследствии очищать, что трудно сделать надежно, особенно учитывая, что POSIX не имеет стандартного механизма (такого как в некоторых системах) для создания временных файлов или каталогов, и делает обработку сигналов переносимой. (убирать при повешении или убивать) также трудно сделать переносимым./dev/fd/nmktemp -d

Вы могли бы сделать что-то вроде:

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"

(не заботясь об обработке сигналов здесь).

Стефан Шазелас
источник
Пример с именованным каналом мне совершенно понятен (я думаю, 10 - произвольный предел?), Но я не могу понять ваш /dev/fd/nпример. Почему дескриптор 4 закрывается дважды? (И дескриптор 3). Я потерян.
Wildcard
@Wildcard, она закрыта для команд, которые не нуждаются в этом. Здесь только diff нуждается в fd 3. Никому не нужен fd 4, он используется только для распространения исходного stdin cmd2( dup2(0,4)на внешнем {...}, восстановлен с помощью dup2(4,0)для внутреннего {...}).
Стефан Шазелас
Вы можете использовать его, mktemp -dчтобы убедиться, что вы можете получить FIFO, поскольку никто не должен писать в ваш новый случайный временный каталог.
Даниэль Х
@DanielH, я уже упоминал mktemp -d. Но это не стандартная команда / POSIX.
Стефан
Да, я этого не осознавал. К сожалению. Быстрый поиск показывает, что большинство систем поддерживают его, поэтому он все еще может быть переносимым, но это не POSIX.
Даниэль Х
-1

При необходимости, чтобы избежать потери переменной в полезной cmd | while read A B C, вместо:

VAR="before"
while read A B C 
do
  VAR="$A $VAR"
done < <(cmd)
echo "$VAR"

вы можете использовать:

VAR="before"
while read A B C 
do
  VAR="$A $VAR"
done << EndOfText
`cmd`
EndOfText
echo "$VAR"

Итак, чтобы ответить на вопрос:

sort file1 | diff /dev/stdin /dev/stdout 2<<EOT
`sort file2`
EOT
defdefred
источник