admin管理员组

文章数量:1277322

I have a shell script starting a docker container. Before starting the container, it starts a background loop printing some lines to the terminal. I noticed that these printed lines do not return the cursor to the beginning of the line. What I found out: this happens only during the lifetime of the container. See minimal example:

#!/usr/bin/env bash

cleanup() {
  kill "${BACKGROUND_PROCESS}"
}
trap cleanup EXIT

backgroundLoop() {
  while true; do
    echo .
    sleep 1
  done
}

backgroundLoop &
BACKGROUND_PROCESS=$!

sleep 2
echo Starting docker

docker run --rm -it alpine sleep 5

echo Docker has finished
sleep 2

cleanup

The output is:

.
.
Starting docker
.
.
 .
  .
   .
    .
     Docker has finished
.
.

Can anyone explain why this happens? It seems to be related to this question, I assume that docker reconfigures the current TTY and restores it afterwards. Is that correct?

Removing -t from the docker call actually fixes the problem, which seems to back my theory. However, I need the interactive TTY for my actual use case.

How can I change my code so that all output lines are printed with correct carriage returns?


UPDATE:

I got an answer to this question which solves the problem for my minimal example I posted above by calling stty -onocr in the docker container before the actual command.

My real-world scenario, however, uses a custom docker image with a bash script as entry point, so I cannot pass commands as arguments. Minimal example of the entrypoint script entrypoint.sh:

#!/usr/bin/env sh

sleep 5

Then replace the docker call in the test script

docker run --rm -it alpine sleep 5

with

docker run --rm -it -v ./entrypoint.sh:/entrypoint.sh --entrypoint /entrypoint.sh alpine

The output is the same, but the proposed fix does not work in this case. Any ideas how to apply the fix to this slightly different environment?

I have a shell script starting a docker container. Before starting the container, it starts a background loop printing some lines to the terminal. I noticed that these printed lines do not return the cursor to the beginning of the line. What I found out: this happens only during the lifetime of the container. See minimal example:

#!/usr/bin/env bash

cleanup() {
  kill "${BACKGROUND_PROCESS}"
}
trap cleanup EXIT

backgroundLoop() {
  while true; do
    echo .
    sleep 1
  done
}

backgroundLoop &
BACKGROUND_PROCESS=$!

sleep 2
echo Starting docker

docker run --rm -it alpine sleep 5

echo Docker has finished
sleep 2

cleanup

The output is:

.
.
Starting docker
.
.
 .
  .
   .
    .
     Docker has finished
.
.

Can anyone explain why this happens? It seems to be related to this question, I assume that docker reconfigures the current TTY and restores it afterwards. Is that correct?

Removing -t from the docker call actually fixes the problem, which seems to back my theory. However, I need the interactive TTY for my actual use case.

How can I change my code so that all output lines are printed with correct carriage returns?


UPDATE:

I got an answer to this question which solves the problem for my minimal example I posted above by calling stty -onocr in the docker container before the actual command.

My real-world scenario, however, uses a custom docker image with a bash script as entry point, so I cannot pass commands as arguments. Minimal example of the entrypoint script entrypoint.sh:

#!/usr/bin/env sh

sleep 5

Then replace the docker call in the test script

docker run --rm -it alpine sleep 5

with

docker run --rm -it -v ./entrypoint.sh:/entrypoint.sh --entrypoint /entrypoint.sh alpine

The output is the same, but the proposed fix does not work in this case. Any ideas how to apply the fix to this slightly different environment?

Share Improve this question edited Feb 26 at 11:21 carlfriedrich asked Feb 25 at 11:37 carlfriedrichcarlfriedrich 4,0931 gold badge24 silver badges38 bronze badges 2
  • so why not remove -it and done? – KamilCuk Commented Feb 26 at 12:11
  • I need to have ctrl+c forwarded to the container, and that's not possible without -it as far as I know. – carlfriedrich Commented Feb 26 at 12:17
Add a comment  | 

2 Answers 2

Reset to default 1

Tell the container not to add CR characters to the output by running stty -onocr in your container by changing the docker run command to

docker run --rm -it alpine stty -onocr && sleep 5

Why does my terminal not perform carriage returns while docker is running?

Because docker executable switches the terminal in raw mode when spawning the terminal, as part of the -t option you passed to docker run.

See man stty , https://en.wikipedia./wiki/Terminal_mode .

How can I change my code so that all output lines are printed with correct carriage returns?

Option #1:

You can remove -t flag from docker run.

Option #2:

You can just set NORAW=1 environment variable.

$ ( set -x; ( sleep 0.2; echo .; echo .; echo .; echo .; ) & NORAW=1 docker run -ti --rm alpine sleep 5 ; wait )
+ NORAW=1
+ docker run -ti --rm alpine sleep 5
+ sleep 0.2
+ echo .
.
+ echo .
.
+ echo .
.
+ echo .
.
+ wait

See https://github/docker/cli/blob/068a01ea9470df6494cc92d9e64e240805ae47a7/cli/streams/in.go#L32 .

Option #3:

You can revert from raw mode, for example with stty -raw. Something along:

$ ( set -x; ( sleep 0.2; stty -raw </dev/tty; echo .; echo .; echo .; echo .; ) & docker run -ti --rm alpine sleep 5 ; wait )
+ docker run -ti --rm alpine sleep 5
+ sleep 0.2
+ stty -raw
           + echo .
.
+ echo .
.
+ echo .
.
+ echo .
.
+ wait

The first + echo . gets printed just before stty -raw is able to take place. After -raw, terminal is cooked.

so I cannot pass commands as arguments

If you control command line, you can pass anything as arguments. For example:

docker run --rm -it -v ./entrypoint.sh:/entrypoint.sh \
   --entrypoint /usr/bin/env alpine sh -c 'stty -raw; . entrypoint.sh'

本文标签: bashWhy does my terminal not perform carriage returns while docker is runningStack Overflow