Вопрос или проблема
Если я выполню команду docker exec -it stupefied_poitras bash -c 'time date'
, то получу следующий вывод
Чт Сен 19 05:02:04 UTC 2024
real 0m0.000s
user 0m0.000s
sys 0m0.000s
Если я выполню команду A=$(docker exec -it stupefied_poitras bash -c 'time date') && echo $A
, то получу
sys 0m0.000ss02:09 UTC 2024
Если я выполню команду A=$(docker exec -it stupefied_poitras bash -c 'time date' 1>&2) && echo $A
, то получу
Чт Сен 19 05:14:59 UTC 2024
real 0m0.002s
user 0m0.001s
sys 0m0.000s
Меня довольно сильно сбивает с толку второй вывод. Почему включена только последняя строка stderr и откуда берется возврат каретки (при условии, что это причина перезаписи)?
Кроме того, меня также смущает еще два других поведения:
Это поведение присутствует только если я использую docker exec
. То есть A=$(bash -c 'time date') && echo $A
не показывает этого (хотя в этом случае вывод времени предшествует выводу даты). Почему оно перевернуто?
Если я перенаправлю stderr на stdout (2>&1
) в третьем случае, то я получаю тот же испорченный вывод, как и во втором случае. Почему это имеет значение, перенаправляю ли я stdout на stderr или наоборот?
Ответ или решение
Когда вы выполняете команду docker exec
, важным аспектом является то, как наследуются стандартные потоки ввода-вывода при выполнении команд внутри контейнера. Разберем ваши вопросы по порядку.
-
Разница в выводе между разными командами:
- Когда вы выполняете
docker exec -it stupefied_poitras bash -c 'time date'
, вы получаете ожидаемый вывод, где первый вывод — это результат командыdate
, а последующие строки — результат выполнения командыtime
, который включает в себя время выполнения. - В случае
A=$(docker exec -it stupefied_poitras bash -c 'time date') && echo $A
, вывод помещается в переменнуюA
, но здесь происходит перезапись stdout и stderr. На самом делеtime
выводит данные в stderr, а так как переменнаяA
захватывает stdout, делает это неправильно: выводdate
и время выполнения перемешиваются, что и приводит к неожиданному выводу. Мы видим только последние 2 символа времени, что может быть связано с тем, что выводtime
помещается в stderr, и поведение зависит от консольного вывода (символы могут перезаписывать друг друга).
- Когда вы выполняете
-
Почему поведение
docker exec
отличается от локального выполнения:- В случае
A=$(bash -c 'time date')
, все выполняется в одном и том же контексте и выводы stderr и stdout идут в порядке их вызова, поэтому вы видите сначалаdate
, а затемtime
. В случаеdocker exec
, контейнер создает отдельный процесс, и потоки вывода могут обрабатываться отдельно, что может привести к переполнению буфера или изменению порядка вывода.
- В случае
- Перенаправление stderr в stdout:
- Когда вы используете
A=$(docker exec -it stupefied_poitras bash -c 'time date' 2>&1) && echo $A
, вы объединяете stderr и stdout, что дает более предсказуемый результат, так как оба потока теперь идут в одну и ту же переменную. Однако при этом могут возникнуть те же проблемы с порядком вывода и перезаписью, из-за чего иногда вывод может казаться «разваленным».
- Когда вы используете
Суммируя, поведение, которое вы наблюдаете, связано с различиями в контексте выполнения и способах обработки потоков ввода-вывода, а также с тем, как контейнеры Docker управляют этими потоками. Чтобы предотвратить проблемы с порядка вывода, вы можете либо явно перенаправлять потоки, как в третьем примере, либо использовать другие подходы для обработки вывода (например, записывать результаты в файлы и затем считывать эти файлы, хотя это может быть менее удобно).