Чтение / запись в тот же дескриптор файла с перенаправлением оболочек

С оболочками POSIX:

string=whateverDELIMrestDELIMmore
before_first_DELIM=${string%%DELIM*}
before_last_DELIM=${string%DELIM*}
after_first_DELIM=${string#*DELIM}
after_last_DELIM=${string##*DELIM}
4
05.10.2018, 10:56
2 ответа

В

{ err=$(exec 2>&1 >&3; ls -ld /x /bin); exec 0<&3; out=$(cat); } 3>&1

{... } 3>&1клонирует fd 1 в fd 3. Это просто означает, что fd 3 теперь указывает на тот же ресурс, (то же описание открытого файла ), что и то, на что указывает fd 1. Если вы запустили это с терминала, это, вероятно, будет fd, открытый в режиме чтения + записи для терминального устройства.

После exec 0<&3fds 0, 1 и 3 указывают на одно и то же описание открытого файла (, созданное, когда ваш эмулятор терминала открыл подчиненную сторону пары псевдотерминалов -, которую он создал ранее. выполнение вашей оболочки в случае запуска команды в терминальном случае выше ).

Затем в out=$(cat)для процесса, выполняющего cat, $(...)изменяет fd 1 на записывающий конец канала, в то время как 0 по-прежнему является устройством tty. Таким образом, catбудет считываться с терминального устройства, поэтому то, что вы печатаете на клавиатуре (, и если бы это было не терминальное устройство, вы, вероятно, получили бы ошибку, так как fd, вероятно, был открыт для записи -только режим ).

Чтобы catчитал то, что lsпишет на своем стандартном выводе, вам нужно, чтобы lsстандартный вывод и catстандартный ввод были двумя концами механизма IPC, такого как канал, пара сокетов или псевдотерминальная пара -.. Например, lsstdout — конец канала для записи, а catstdin — конец для чтения.

Но вам также потребуется, чтобы lsи catвыполнялись одновременно, а не один за другим, так как это механизм межпроцессного взаимодействия (IPC -между процессами ).

Так как каналы могут содержать некоторые данные (64 КиБ по умолчанию в текущих версиях Linux ), вам бы сошли с рук короткие выходные данные, если бы вам удалось создать второй канал, но для больших выходы, вы столкнетесь с взаимоблокировками, lsбудет зависать, когда канал заполнен, и будет зависать, пока что-то не опустошит канал, но catможет очистить канал только после возврата ls.

Кроме того,только yashимеет необработанный интерфейс для pipe(), который вам потребуется для создания второго канала для чтения из lsstdout (другого канала для stderr, созданного конструкцией $(...)).

В yash, вы бы сделали:

{ out=$(ls -d / /x 2>&3); exec 3>&-; err=$(exec cat <&4); } 3>>|4

Где3>>|4(yash -специфическая функция )создает этот второй канал с концом записи на fd 3 и концом чтения на fd 4.

Но опять же, если вывод stderr больше, чем размер канала, это зависнет. Мы эффективно используем канал как временный файл в памяти, а не как канал.

Чтобы действительно использовать каналы, нам нужно начать lsс stdout, являющегося концом записи одного канала, и stderr, являющимся концом записи другого канала, а затем оболочка одновременно считывает другие концы этих каналов, как данные приходят (не один за другим, иначе вы снова столкнетесь с мертвыми -блокировками )для сохранения в две переменные.

Чтобы иметь возможность читать из этих двух fd по мере поступления данных, вам потребуется оболочка с поддержкой select()/poll(). zshявляется такой оболочкой, но у нее нет yashфункции перенаправления конвейера ¹, поэтому вам нужно будет использовать именованные каналы (, чтобы управлять их созданием, разрешениями и очисткой. )и использовать сложный цикл с zselect/ sysread...

¹ Однако в Linux вы можете использовать тот факт, что /proc/self/fd/xв канале ведет себя как именованный канал, поэтому вы можете:

#! /bin/zsh
zmodload zsh/zselect
zmodload zsh/system

(){exec {wo}>$1 {ro}<$1} <(:) # like yash's wo>>|ro (but on Linux only)
(){exec {we}>$1 {re}<$1} <(:)

ls -d / /x >&$wo 2>&$we &
exec {wo}>&- {we}>&-
out= err=
o_done=0 e_done=0

while ((! (o_done && e_done))) && zselect -A ready $ro $re; do
  if ((${#ready[$ro]})); then
    sysread -i $ro && out+=$REPLY || o_done=1
  fi
  if ((${#ready[$re]})); then
    sysread -i $re && err+=$REPLY || e_done=1
  fi
done
3
27.01.2020, 20:54

Это похоже на базовое ограничение синтаксиса присваивания переменных и побочные -эффекты порождения подоболочек оболочки. Вы можете захватить либо stderr, либо stdout, но не оба :, другой поток необходимо перенаправить в файл (, возможно, в FIFO ).

# a function for testing
your_command() { sh -c 'echo "this is stdout"; echo "this is stderr" >&2'; }

errfile=$(mktemp)
out=$( your_command 2>|"$errfile" )
err=$(< "$errfile")
rm "$errfile"

echo "out: $out"
echo "err: $err"
1
27.01.2020, 20:54

Теги

Похожие вопросы