Если я вызываю какую-то команду, например, echo
я могу использовать результаты этой команды в нескольких других командах с tee
. Пример:
echo "Hello world!" | tee >(command1) >(command2) >(command3)
С помощью cat я могу собрать результаты нескольких команд. Пример:
cat <(command1) <(command2) <(command3)
Я хотел бы иметь возможность делать обе вещи одновременно, так что я могу использовать tee
для вызова этих команд на выходе чего-то еще (например, echo
я написал), а затем собрать все их результаты на одном выходе с cat
,
Важно , чтобы сохранить результаты в порядке, это означает , что линии на выходе command1
, command2
и command3
не должны быть связаны между собой, но упорядоченный как команды (как это происходит с cat
).
Там может быть лучше варианты , чем cat
и , tee
но это те , которые я знаю до сих пор.
Я хочу избежать использования временных файлов, потому что размер ввода и вывода может быть большим.
Как я мог это сделать?
PD: другая проблема заключается в том, что это происходит в цикле, что усложняет обработку временных файлов. Это текущий код, который у меня есть, и он работает для небольших тестовых случаев, но он создает бесконечные циклы при чтении и записи из вспомогательного файла некоторым образом, который я не понимаю.
somefunction()
{
if [ $1 -eq 1 ]
then
echo "Hello world!"
else
somefunction $(( $1 - 1 )) > auxfile
cat <(command1 < auxfile) \
<(command2 < auxfile) \
<(command3 < auxfile)
fi
}
Чтения и записи в auxfile, кажется, пересекаются, заставляя все взорваться.
источник
echo HelloWorld > file; (command1<file;command2<file;command3<file)
или для выходаecho | tee cmd1 cmd2 cmd3; cat cmd1-output cmd2-output cmd3-output
. Вот как это работает - вы можете форкать ввод, только если все команды работают и обрабатываются параллельно. если одна команда спит (потому что вы не хотите чередования), она просто заблокирует все команды, чтобы предотвратить заполнение памяти вводом ...Ответы:
Вы можете использовать комбинацию GNU stdbuf и
pee
от moreutils :Писать
popen(3)
эти 3 командные строки оболочки, а затемfread
ввод иfwrite
все три, которые будут буферизироваться до 1М.Идея состоит в том, чтобы иметь буфер по крайней мере такой же большой, как входные данные. Таким образом, даже несмотря на то, что три команды запускаются одновременно, они будут видеть ввод только при
pee
pclose
последовательном выполнении трех команд.После каждого
pclose
,pee
переполнить буфер в команде и ждет его завершения. Это гарантирует, что до тех пор, пока этиcmdx
команды не начнут выводить что-либо до того, как они получат какие-либо входные данные (и не будут обрабатывать процесс, который может продолжить вывод после возвращения их родителя), выходные данные трех команд не будут чередоваться.По сути, это немного похоже на использование временного файла в памяти, с тем недостатком, что 3 команды запускаются одновременно.
Чтобы избежать одновременного запуска команд, вы можете написать
pee
в виде функции оболочки:Но имейте в виду, что оболочки, отличные от тех,
zsh
которые не будут использоваться для двоичного ввода с символами NUL.Это позволяет избежать использования временных файлов, но это означает, что весь ввод хранится в памяти.
В любом случае вам придется хранить входные данные где-то, в памяти или временном файле.
На самом деле, это довольно интересный вопрос, поскольку он показывает нам предел идеи Unix, заключающейся в том, чтобы несколько простых инструментов взаимодействовали с одной задачей.
Здесь мы хотели бы, чтобы несколько инструментов сотрудничали с задачей:
echo
)tee
)cmd1
,cmd2
,cmd3
)cat
).Было бы хорошо, если бы они все могли работать вместе и выполнять свою тяжелую работу над данными, которые они должны обрабатывать, как только они станут доступны.
В случае одной команды фильтра это легко:
Все команды выполняются одновременно,
cmd1
начинает собирать данные,src
как только они становятся доступными.Теперь, используя три команды фильтра, мы можем сделать то же самое: запустить их одновременно и соединить с помощью каналов:
Что мы можем сделать относительно легко с именованными каналами :
(выше,
} 3<&0
чтобы обойти тот факт, что&
перенаправленияstdin
от/dev/null
, и мы используем,<>
чтобы избежать открытия каналов для блокировки, пока другой конец (cat
) также не открылся)Или, чтобы избежать именованных каналов, немного сложнее с
zsh
coproc:Теперь вопрос: как только все программы будут запущены и подключены, будет ли поток данных?
У нас есть два ограничения:
tee
передает все свои выходы с одинаковой скоростью, поэтому он может отправлять данные только со скоростью самого медленного канала вывода.cat
начнёт чтение только со второго канала (канал 6 на рисунке выше), когда все данные будут считаны из первого (5).Это означает, что данные не будут передаваться в канал 6 до
cmd1
тех пор, пока он не закончится. И, как в случаеtr b B
вышеупомянутого, это может означать, что данные также не будут передаваться в трубе 3, что означает, что они не будут течь ни в одном из каналов 2, 3 или 4, так какtee
подача происходит с самой низкой скоростью из всех 3.На практике эти каналы имеют ненулевой размер, поэтому некоторым данным удастся пройти, и по крайней мере в моей системе я смогу заставить их работать до:
Помимо этого, с
У нас тупик, где мы находимся в такой ситуации:
Мы заполнили трубы 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):
источник
+1
за хорошее искусство ASCII :-)То, что вы предлагаете, не может быть легко сделано с помощью любой существующей команды, и в любом случае не имеет особого смысла. Вся идея труб (
|
в Unix / Linux) является то , что вcmd1 | cmd2
наcmd1
выходе пишет (не более) , пока буфер памяти заливок, а затемcmd2
бежит чтение данных из буфера (не более) , пока он не пуст. Т.е.,cmd1
иcmd2
работать одновременно, никогда не нужно, чтобы между ними было больше, чем ограниченное количество данных. Если вы хотите подключить несколько входов к одному выходу, если один из считывателей отстает от других, либо вы останавливаете другие (какой смысл работать параллельно тогда?), Либо вы скрываете вывод, который отстающий еще не прочитал (какой смысл не иметь промежуточный файл тогда?). более сложный.За почти 30-летний опыт работы в Unix я не помню ни одной ситуации, которая бы действительно выиграла для такого канала с множественным выходом.
Вы можете объединить несколько выходов в один поток сегодня, просто не в какой - либо чередованием образом (как следует выходы
cmd1
иcmd2
перемежаться? На одну строку , в свою очередь? Сменяться писать 10 байт? Alternate «пункты» определены как - то? И если только что Безразлично» долго ничего не пишешь? со всем этим сложно справиться). Это делается, например(cmd1; cmd2; cmd3) | cmd4
, программамиcmd1
,cmd2
иcmd3
запускаются одна за другой, выходные данные отправляются как входные данныеcmd4
.источник
Для вашей перекрывающейся проблемы в Linux (и с
bash
илиzsh
без, сksh93
) вы можете сделать это следующим образом:Обратите внимание на использование
(...)
вместо того,{...}
чтобы получать новый процесс на каждой итерации, чтобы у нас мог быть новый fd 3, указывающий на новыйauxfile
.< /dev/fd/3
это трюк для доступа к удаленному файлу. Он не будет работать в системах, отличных от Linux, где< /dev/fd/3
это похоже,dup2(3, 0)
и поэтому fd 0 будет открыт в режиме только для записи с курсором в конце файла.Чтобы избежать разветвления для вложенной функции some, вы можете написать ее так:
Оболочка будет заботиться о резервном копировании fd 3 на каждой итерации. Вы бы в конечном итоге исчерпали файловые дескрипторы раньше.
Хотя вы найдете, что это более эффективно сделать так:
То есть не вкладывайте перенаправления.
источник