Я не знаю, возможно ли это, но я обычно вставляю, со всеми версиями VI, строка с числами, которые могут помочь мне определить местоположение столбцов.
В командном режиме:
10a1234567890
Я получу строку 100 символов (просто изменяют число повторения для меньше или больше).
Вы могли использовать комбинацию GNU stdbuf и pee
от moreutils:
echo "Hello world!" | stdbuf -o 1M pee cmd1 cmd2 cmd3 > output
моча popen(3)
s те 3 командных строки оболочки и затем fread
s вход и fwrite
s все это три, который будет буферизован к до 1M.
Идея состоит в том, чтобы иметь буфер, по крайней мере, столь же большой как вход. Этот путь даже при том, что три команды запускаются одновременно, они будут только видеть, что вход входит когда pee
pclose
s три команды последовательно.
На каждого pclose
, pee
сбрасывает буфер к команде и ожидает ее завершения. Это гарантирует что настолько же долго как они cmdx
команды не начинают производить что-либо, прежде чем они получили любой вход (и не разветвляйте процесс, который может продолжить производить после того, как их родитель возвратился), вывод трех команд не будет чередован.
В действительности это немного похоже на использование временного файла в памяти с недостатком, что 3 команды запускаются одновременно.
Чтобы постараться не запускать команды одновременно, Вы могли записать pee
как функция оболочки:
pee() (
input=$(cat; echo .)
for i do
printf %s "${input%.}" | eval "$i"
done
)
echo "Hello world!" | pee cmd1 cmd2 cmd3 > out
Но остерегайтесь, который окружает кроме zsh
перестал бы работать для двоичного входа с символами NUL.
Это избегает использования временных файлов, но это означает, что целый вход хранится в памяти.
В любом случае необходимо будет сохранить вход где-нибудь в памяти или временном файле.
На самом деле это - вполне интересный вопрос, поскольку это показывает нам, предел идеи Unix наличия нескольких простых инструментов сотрудничает к единственной задаче.
Здесь, мы хотели бы иметь несколько инструментов, сотрудничают к задаче:
echo
)tee
)cmd1
, cmd2
, cmd3
)cat
).Было бы хорошо, если они могли бы все работать вместе одновременно и сделать свою тяжелую работу на данных, которые они предназначены для обработки, как только это доступно.
В случае одной команды фильтрации это легко:
src | tee | cmd1 | cat
Все команды выполняются одновременно, cmd1
начинает громко жевать данные из src
как только это доступно.
Теперь, с тремя командами фильтрации, мы можем все еще сделать то же: запустите их одновременно и соедините их с каналами:
┏━━━┓▁▁▁▁▁▁▁▁▁▁┏━━━━┓▁▁▁▁▁▁▁▁▁▁┏━━━┓
┃ ┃░░░░2░░░░░┃cmd1┃░░░░░5░░░░┃ ┃
┃ ┃▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃
┏━━━┓▁▁▁▁▁▁▁▁▁▁┃ ┃▁▁▁▁▁▁▁▁▁▁┏━━━━┓▁▁▁▁▁▁▁▁▁▁┃ ┃▁▁▁▁▁▁▁▁▁┏━━━┓
┃src┃░░░░1░░░░░┃tee┃░░░░3░░░░░┃cmd2┃░░░░░6░░░░┃cat┃░░░░░░░░░┃out┃
┗━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃▔▔▔▔▔▔▔▔▔┗━━━┛
┃ ┃▁▁▁▁▁▁▁▁▁▁┏━━━━┓▁▁▁▁▁▁▁▁▁▁┃ ┃
┃ ┃░░░░4░░░░░┃cmd3┃░░░░░7░░░░┃ ┃
┗━━━┛▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┗━━━┛
Который мы можем сделать относительно легко с именованными каналами:
pee() (
mkfifo tee-cmd1 tee-cmd2 tee-cmd3 cmd1-cat cmd2-cat cmd3-cat
{ tee tee-cmd1 tee-cmd2 tee-cmd3 > /dev/null <&3 3<&- & } 3<&0
eval "$1 < tee-cmd1 1<> cmd1-cat &"
eval "$2 < tee-cmd2 1<> cmd2-cat &"
eval "$3 < tee-cmd3 1<> cmd3-cat &"
exec cat cmd1-cat cmd2-cat cmd3-cat
)
echo abc | pee 'tr a A' 'tr b B' 'tr c C'
(выше } 3<&0
должен работать вокруг факта это &
перенаправления stdin
от /dev/null
, и мы используем <>
избегать открытия каналов для блокирования до другого конца (cat
) открылся также),
Или избегать именованных каналов, немного более мучительно с zsh
coproc:
pee() (
n=0 ci= co= is=() os=()
for cmd do
eval "coproc $cmd $ci $co"
exec {i}<&p {o}>&p
is+=($i) os+=($o)
eval i$n=$i o$n=$o
ci+=" {i$n}<&-" co+=" {o$n}>&-"
((n++))
done
coproc :
read -p
eval tee /dev/fd/$^os $ci "> /dev/null &" exec cat /dev/fd/$^is $co
)
echo abc | pee 'tr a A' 'tr b B' 'tr c C'
Теперь, вопрос: после того как все программы запущены и соединены, будет поток данных?
У нас есть два ограничения:
tee
подача все его выводы на том же уровне, таким образом, это может только диспетчеризировать данные по курсу своего самого медленного выходного канала.cat
только начнет читать из второго канала (передайте по каналу 6 в рисунке выше), когда все данные были считаны с первого (5).То, что это означает, - то, что данные не будут течь в канале 6 до cmd1
закончился. И, как в случае tr b B
выше, который может означать, что данные не будут течь в канале 3 или, что означает, это не будет течь ни в одном из каналов 2, 3 или 4 с тех пор tee
подача на самом медленном уровне всех 3.
На практике те каналы имеют непустой размер, таким образом, некоторым данным удастся пройти, и в моей системе, по крайней мере, я могу заставить это работать до:
yes abc | head -c $((2 * 65536 + 8192)) | pee 'tr a A' 'tr b B' 'tr c C' | uniq -c -c
Кроме того, с
yes abc | head -c $((2 * 65536 + 8192 + 1)) | pee 'tr a A' 'tr b B' 'tr c C' | uniq -c
У нас есть мертвая блокировка, где мы находимся в этой ситуации:
┏━━━┓▁▁▁▁2▁▁▁▁▁┏━━━━┓▁▁▁▁▁5▁▁▁▁┏━━━┓
┃ ┃░░░░░░░░░░┃cmd1┃░░░░░░░░░░┃ ┃
┃ ┃▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃
┏━━━┓▁▁▁▁1▁▁▁▁▁┃ ┃▁▁▁▁3▁▁▁▁▁┏━━━━┓▁▁▁▁▁6▁▁▁▁┃ ┃▁▁▁▁▁▁▁▁▁┏━━━┓
┃src┃██████████┃tee┃██████████┃cmd2┃██████████┃cat┃░░░░░░░░░┃out┃
┗━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃▔▔▔▔▔▔▔▔▔┗━━━┛
┃ ┃▁▁▁▁4▁▁▁▁▁┏━━━━┓▁▁▁▁▁7▁▁▁▁┃ ┃
┃ ┃██████████┃cmd3┃██████████┃ ┃
┗━━━┛▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┗━━━┛
Мы заполнили каналы 3 и 6 (64 кибибита каждый). tee
считал, что дополнительный байт, это подало его к cmd1
, но
cmd2
освободить егоcmd2
не может освободить его, потому что это заблокировало запись на канале 6, ожидая cat
освободить егоcat
не может освободить его, потому что это ожидает, до там больше не вводится на канале 5.cmd1
не может сказать cat
там больше не вводится, потому что это ожидает само более входа от tee
.tee
не может сказать cmd1
там больше не вводится, потому что это заблокировалось... и так далее.У нас есть цикл зависимостей и таким образом мертвая блокировка.
Теперь, каково решение? Большие каналы 3 и 4 (достаточно большой для содержания всего из src
вывод), сделал бы это. Мы могли сделать это, например, путем вставки pv -qB 1G
между tee
и cmd2/3
где pv
мог сохранить до 1G данных, ожидающих cmd2
и cmd3
считать их. Это означало бы две вещи хотя:
cmd2
в действительности только начал бы обрабатывать данные, когда cmd1 закончился.Решение второй проблемы состояло бы в том, чтобы сделать каналы 6 и 7 больших также. Принятие этого cmd2
и cmd3
произведите так очень вывод, как они используют, который не использовал бы больше памяти.
Единственный способ постараться не копировать данные (в первой проблеме) состоял бы в том, чтобы реализовать хранение данных в диспетчере самих, который является реализацией вариация на tee
это может подать данные по курсу самого быстрого вывода (содержащий данные для питания более медленных в их собственном темпе). Едва ли тривиальный.
Так, в конце лучшее, которое мы можем обоснованно получить без программирования, является, вероятно, чем-то как (синтаксис Zsh):
max_hold=1G
pee() (
n=0 ci= co= is=() os=()
for cmd do
if ((n)); then
eval "coproc pv -qB $max_hold $ci $co | $cmd $ci $co | pv -qB $max_hold $ci $co"
else
eval "coproc $cmd $ci $co"
fi
exec {i}<&p {o}>&p
is+=($i) os+=($o)
eval i$n=$i o$n=$o
ci+=" {i$n}<&-" co+=" {o$n}>&-"
((n++))
done
coproc :
read -p
eval tee /dev/fd/$^os $ci "> /dev/null &" exec cat /dev/fd/$^is $co
)
yes abc | head -n 1000000 | pee 'tr a A' 'tr b B' 'tr c C' | uniq -c
То, что Вы предлагаете, не может быть сделано легко ни с какой существующей командой и не имеет большого смысла так или иначе. Вся эта мысль о каналах (|
в Unix/Linux), это в cmd1 | cmd2
cmd1
вывод записей (самое большее) до буфера памяти заполняется, и затем cmd2
выполнения, считывающие данные с буфера (самое большее), пока это не пусто. Т.е. cmd1
и cmd2
выполненный одновременно, никогда не необходимо иметь больше, чем ограниченный объем данных "в полете" между ними. Если Вы хотите подключить несколько исходных данных к единственному выводу, если один из читателей отстает от других любой, который Вы останавливаете другие (какой смысл того, чтобы работать параллельно затем?) или Вы прячете вывод, который отстающий еще не считал (какой смысл того, чтобы не иметь промежуточного файла затем?). Плюс целая синхронизация становится намного более сложным.
За мои почти 30 лет на опыте Unix я не помню ситуации, которая действительно принесла бы пользу для такого нескольким - выходной канал.
Можно ли объединить несколько выводов в один поток сегодня, просто не каким-либо чередованным способом (как должен выводы cmd1
и cmd2
быть чередованными? одна строка в свою очередь? сменяться пишущий 10 байтов? чередовать "абзацы", определенные так или иначе? и если Вы просто ничего не пишете в течение долгого времени? все это сложно для обработки). Это сделано, например. (cmd1; cmd2; cmd3) | cmd4
, программы cmd1
,cmd2
, и cmd3
выполняются один за другим, вывод отправляется, как введено в cmd4
.
Для Вашей перекрывающейся проблемы, на Linux (и с bash
или zsh
но не с ksh93
), Вы могли сделать это как:
somefunction()
(
if [ "$1" -eq 1 ]
then
echo "Hello world!"
else
exec 3> auxfile
rm -f auxfile
somefunction "$(($1 - 1))" >&3 auxfile 3>&-
exec cat <(command1 < /dev/fd/3) \
<(command2 < /dev/fd/3) \
<(command3 < /dev/fd/3)
fi
)
Отметьте использование (...)
вместо {...}
получить новый процесс при каждом повторении, таким образом, у нас может быть новый fd 3, указывающий на новое auxfile
. < /dev/fd/3
прием должен получить доступ к этому теперь удаленный файл. Это не будет работать над системами кроме Linux где < /dev/fd/3
похож dup2(3, 0)
и так fd 0 было бы открыто в режиме только для записи с курсором в конце файла.
Для предотвращения ветвления для вложенного somefunction Вы могли записать это как:
somefunction()
{
if [ "$1" -eq 1 ]
then
echo "Hello world!"
else
{
rm -f auxfile
somefunction "$(($1 - 1))" >&3 auxfile 3>&-
exec cat <(command1 < /dev/fd/3) \
<(command2 < /dev/fd/3) \
<(command3 < /dev/fd/3)
} 3> auxfile
fi
}
Оболочка заботилась бы о резервном копировании fd 3 при каждом повторении. Вы закончили бы тем, что закончились дескрипторы файлов раньше все же.
Хотя Вы найдете, что это - более эффективное, чтобы сделать это как:
somefunction() {
if [ "$1" -eq 1 ]; then
echo "Hello world!" > auxfile
else
somefunction "$(($1 - 1))"
{ rm -f auxfile
cat <(command1 < /dev/fd/3) \
<(command2 < /dev/fd/3) \
<(command3 < /dev/fd/3) > auxfile
} 3< auxfile
fi
}
somefunction 12; cat auxfile
Таким образом, не вкладывайте перенаправления.
+1
поскольку хорошее ASCII-творчество :-) – Kurt Pfeifle 16.08.2015, 18:00