5

I would like to execute a script on a host machine (script_on_host.sh), which then reaches inside a docker container in order to grab some data using a second script (script_in_container.sh). The data is only available inside the container, and not from the host directly, hence the reason for doing it this way.

I am having trouble passing data between the two scripts. I tried the following:

script_in_container.sh

#!/bin/bash

x=1
y=2
z=6.3
echo $x
echo $y
echo $z

script_on_host.sh

#!/bin/bash

results=$(docker exec mycontainer './script_in_container.sh')
X=$results(1)
Y=$results(2)
Z=$results(3)

But it throws an error. Can someone help me with this? Is there a nice or more standard way to go about passing variables in this way? I feel like I'm maybe doing something a little clumsily here, by using echo to print multiple lines from the container script.

2
  • 1
    Where did you get the idea that $results(1) is the way to refer to the first word of $results? If $results were an array, it would use [] as the way to index it, and indexes start at 0.
    – Barmar
    Commented Dec 24, 2024 at 16:04
  • @Barmar this is true. I guess its because I don't exclusively work with bash and am relatively new to it. Getting confused with the () and zero-indexing is due to me switching between many different languages, each with different syntax and conventions. However, these two mistakes on my part were not the main cause for my attempt here failing.
    – teeeeee
    Commented Dec 26, 2024 at 13:19

4 Answers 4

9

You could use readarray to build an array of results delimited by newline.

readarray -t results < <(docker exec mycontainer './script_in_container.sh')
x=${results[0]}
y=${results[1]}
z=${results[2]}
7

The simplest solution would be to use set -- to assign variables from the docker container to the positional parameters like this:

#!/bin/bash

results=$(docker exec mycontainer './script_in_container.sh')

saveIFS=$IFS
IFS=$'\n'
set -- $results
IFS=$saveIFS

echo result 1: "$1"
echo result 2: "$2"
echo result 3: "$3"
9
  • This is probably the best way to do and it also POSIX compatible.
    – jesse_b
    Commented Dec 24, 2024 at 1:51
  • 1
    Caveat: this may change an unset IFS to an empty IFS, which has different semantics.
    – l0b0
    Commented Dec 24, 2024 at 11:26
  • Just set IFS so: "IFS=$'\n'" (I prefer "IFS=$'\012' for some reason) and then "unset IFS". IFS will return to its default value.
    – Wastrel
    Commented Dec 24, 2024 at 17:56
  • 1
    @RonJohn it's not more simple but it is more portable. readarray only works in bash and only bash 4.0 and later at that, so it wouldn't work on the bash that ships with macos for example.
    – jesse_b
    Commented Dec 26, 2024 at 18:38
  • 1
    @teeeeee: no because there are no arrays in POSIX sh Commented Dec 29, 2024 at 22:02
1

You can use sed, like this:

#!/bin/bash

results=$(docker exec mycontainer './script_in_container.sh')
X=$(echo "$results" |sed '1q;d')
Y=$(echo "$results" |sed '2q;d')
Z=$(echo "$results" |sed '3q;d')

The sed expression is actually two commands:

  • 1q is a command to match the first line and quit
  • ; is a command separator
  • d deletes the current line.

In the case of reading the first line the d is kind of redundant since sed will quit after it prints line 1 anyway but for other lines the d is necessary to delete any lines that do not match your first command.

4
  • Don't you need the double quotes somewhere here? Commented Dec 23, 2024 at 22:33
  • @ArkadiuszDrabczyk you are right. I've updated my answer. Commented Dec 23, 2024 at 22:41
  • Thanks, can you explain what the "1q;d" syntax is doing exactly?
    – teeeeee
    Commented Dec 23, 2024 at 22:44
  • 1
    The big disadvantage of this method is that you need to run sed for all lines, either manually or in a loop and what's worse manually control all variable names. Commented Dec 23, 2024 at 23:10
1

There are two fully-standards-compatible solutions, and they'll also be the fastest because they don't fork/exec more than necessary:

#!/bin/sh
docker exec mycontainer './script_in_container.sh' | {
  IFS= read -r X
  IFS= read -r Y
  IFS= read -r Z
  echo X=$X Y=$Y Z=$Z
}

(you can omit the IFS= prefix if you don't care about stripping initial whitespace) and

#!/bin/sh
Z="$(docker exec mycontainer './script_in_container.sh')"
X="${Z%%$'\n'*}"
Z="${Z#*$'\n'}"
Y="${Z%%$'\n'*}"
Z="${Z#*$'\n'}"
echo X=$X Y=$Y Z=$Z

%% is "remove matches recursively from the end", # is "remove one match from the start`, so removing all matches of [newline, anything] removes every line but the first, and removing one match of [anything, newline] removes the first line.

The $'\n' spelling is new in POSIX.1-2024, and not universally available; you can spell it as

'
'

(so,

X="${Z%%'
'*}"

) to increase your compatibility range.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.