Довольно часто при работе в командной строке я обнаруживаю, что задаю одну и ту же операцию для множества различных экземпляров, указанных в некотором входном потоке, а затем хочу объединить их выходные данные каким-то определенным образом.
Два варианта использования вчерашнего дня:
Мне хотелось увидеть субъекты нескольких SSL-сертификатов, объединенных в PEM-файл.
cat mypemfile.crt | (openssl x509 -noout -text | fgrep Subject:)
показывает мне только первый. Мне нужно разделить сертификаты и запустить одну и ту же команду для каждого из них, затем объединить результаты.
csplit
можно разделить их, но только на файлы. Это хлопотно.Почему я просто не могу сказать
cat mypemfile.crt | csplit-tee '/BEGIN/ .. /END/' | on-each ( openssl x509 -noout -text | fgrep Subject: ) | merge --cat
?
Мы запускаем экземпляр JupyterHub, который разделяет серверы блокнотов как контейнеры Docker. Я хочу посмотреть их журналы с временными метками. Это достаточно просто для одного контейнера:
sudo docker logs -t -f $container_id
(
-t
Добавляет временную метку и оставляет-f
канал открытым, напримерtail -f
.)Достаточно просто составить список журналов всех контейнеров, отсортированных по временным меткам:
sudo docker ps | awk '{print $1}' | while read container_id do sudo docker logs -t $container_id done | sort
или
sudo docker ps | awk '{print $1}' | xargs -n1 sudo docker logs -t | sort
или
sudo docker ps | awk '{print $1}' | parallel sudo docker logs -t {} | sort
но ничто из этого не позволит мне использовать
-f
возможность просмотра журналов по мере их поступления.Почему я не могу просто использовать
sudo docker ps | awk '{print $1}' | csplit-tee /./ | on-each (xargs echo | sudo docker logs -t -f) | merge --line-by-line --lexicographically
или
sudo docker ps | awk '{print $1}' | parallel --multipipe sudo docker logs -t -f {} | merge --line-by-line --lexicographically
?
Очевидно, что для этого необходимо
- Специфическая поддержка оболочки. Возможно, нам понадобится отдельный символ "многоканальный".
- Новые инструменты (
csplit-tee
,on-each
иmerge
), которые разделяют и объединяют конвейеры. - Фиксированное соглашение о том, как указать в инструментах произвольное количество дескрипторов входных и выходных файлов, чтобы эта оболочка воспринимала их как параллельные конвейеры.
Было ли это сделано? Или что-то эквивалентное, что я могу применить к своим вариантам использования?
Возможно ли это без поддержки определенного ядра? Я знаю, что ядро обычно имеет фиксированное максимальное количество открытых файловых дескрипторов, но реализация может обойти это, не пытаясь слепо открывать их все одновременно.
Осуществимо ли это, зная, чтоGNU-параллельосуществимо?
решение1
С помощью GNU Parallel:
cat mypemfile.crt |
parallel --pipe -N1 --recstart '-----BEGIN' 'openssl x509 -noout -text | fgrep Subject:'
Непроверено:
sudo docker ps | awk '{print $1}' |
sudo parallel -j0 --lb docker logs -t -f {}
sudo docker ps | awk '{print $1}' |
sudo parallel -j0 --tag --lb docker logs -t -f {}
решение2
Нет, оболочка не может этого сделать. Труба — это просто поток от источника к месту назначения, обычно в разных процессах.
Давайте рассмотрим ваш первый пример:
cat mypemfile.crt |
(openssl x509 -noout -text | fgrep Subject:)
Вы хотите, чтобы входной файл был разделен по строкам, содержащим BEGIN и END, но cat
не заботитесь о содержимом, а каналы в любом случае не имеют индикации выхода за пределы разделителей записей.
Этого можно было бы добиться с помощью определенных соглашений об источнике и месте назначения канала, но это устранило бы главное преимущество канала — возможность объединять программы в качестве строительных блоков для задач, которые не были предусмотрены их авторами.
В этом конкретном примере было бы гораздо проще выполнить модификацию openssl
для обработки нескольких сертификатов в одном потоке, чем модификацию openssl
для обработки нескольких потоков И модификацию cat
для предоставления этих нескольких потоков.