Reputation: 41
I've got case: there's WordPress project where I'm supposed to create a script for updating plugins and commit source changes to the separated branch. While doing this I had run into a strange issue.
Input variable:
akimset,4.0.3
all-in-one-wp-migration,6.71
What I wanted to do was iterating over each line of this variable
while read -r line; do
echo $line
done <<< "$variable"
and this piece of code worked perfectly fine, but when I have added docker-compose logic everything started to act weirdly
while read -r line; do
docker-compose run backend echo $line
done <<< "$variable"
now only one line was executed and after this script exited with 0 and stopped iterating. I have found workaround with:
echo $variable > file.tmp
for line in $(cat file.tmp); do
docker-compose run backend echo $line
done
and that works perfectly fine and it iterates each line. Now my question is: why? ZSH and shell scripting could be a bit misterious and running in edge-cases like this one isn't anything new for me, but I'm wondering why succesfully executed script broke input stream.
Upvotes: 4
Views: 1899
Reputation: 23
You can use redirection to prevent the first docker-compose command from reading all of stdin. e.g. if you redirect from /dev/null:
seq 3 | while read num; do
docker-compose run --rm bash echo "Run number $num" </dev/null;
done
Outputs:
Creating tmp_bash_run ... done
Run number 1
Creating tmp_bash_run ... done
Run number 2
Creating tmp_bash_run ... done
Run number 3
But if you don't add the redirection then the first docker-compose command will read all the remaining input from stdin ending the loop after a single run.
seq 3 | while read num; do
docker-compose run --rm bash echo "Run number $num";
done
Outputs:
Creating tmp_bash_run ... done
Run number 1
This is useful if, like me, you are using a recent version of docker, you need the output of the command you are running and you don't want to edit your docker-compose.yml file.
To run the above commands you'll need a docker-compose.yml file with something like the following:
---
services:
bash:
image: bash
Upvotes: 0
Reputation: 972
I was able to solve the same problem by using a different loop :
for line in $(cat $variable)
do
docker-compose run backend echo $line
done
Upvotes: 1
Reputation: 421
The problem with this
while read -r line; do
docker-compose run backend echo $line
done <<< "$variable"
is that docker allocate pseudo-TTY. After the first execution of docker-compose run (first loop) it access to the terminal using up the next lines as input.
You have to pass -T parameter to 'docker-compose run' command in order to avoid docker allocating pseudo-TTY. Then, a working code is:
while read -r line; do
docker-compose run -T backend echo $line
done < $(variable)
The above solution is for docker version 18 and docker-compose version 1.17. For newer version the parameter -T is not working but you can try:
Upvotes: 2
Reputation: 685
I ran into a nearly identical problem about a year ago, though the shell was bash
(the command/problem was also slightly different, but it applied to your issue). I ended up writing the script in zsh
.
I'm not certain what's going on, but it's not actually the exit code (you can confirm by running the following):
variable=$'akimset,4.0.3\nall-in-one-wp-migration,6.71'
while read line; do docker-compose run backend print "$line"; print "$?"; done <<<($variable)
... which yielded ...
(akimset,4.0.3
0
(I'm not at all sure where the (
came from and perhaps solving that would answer why this problem happens)
for line in "${(f)variable}"; do
docker-compose run backend echo "$line"
done
The (f)
flag tells zsh
to split on newlines; the "${(f)variable"
is in quotes so that any blank lines aren't lost. If you're going to include escap sequences that you want to not be converted to the corresponding values (something that I often need when reading file contents from a variable), make the flags (fV)
Upvotes: 0