Abhishek
Abhishek

Reputation: 2673

Dereference environment variable on parameter expansion in shell script

I am trying to dereference the value of environment variable using parameter expansion $@, but it doesn't seem to work.

I need to call a shell script with certain arguments. The list of arguments contain environment variables, and the environment variables are expected to be present where the shell script is to be executed. I do not know the list of commands before hand, so I am expanding those list of commands using $@. However, the script is not able to de-reference the value of environment variables.

A minimal setup which explains my problem can be done as below.

FROM alpine:3.10

ENV MY_VAR=production

WORKDIR /app

COPY run.sh .

ENTRYPOINT [ "sh", "run.sh" ]
#!/bin/sh

echo "Value of MY_VAR is" $MY_VAR

echo "Begin"

$@

echo "Done"

I can build the image using docker build . -t env-test. When I run it using docker run env-test:latest 'echo $MY_VAR', I get the below output.

Value of MY_VAR is production
Begin
$MY_VAR
Done

While the output that I am expecting is:

Value of MY_VAR is production
Begin
production
Done

SideNote: In actuality I am trying to run it using a compose file like below:

version: '3'

services:
  run:
    image: env-test:latest
    command: echo $$MY_VAR

but it again gives me the similar result.

Upvotes: 1

Views: 1920

Answers (3)

mainmachine
mainmachine

Reputation: 307

There are a more ways to skin this particular cat:

me@computer:~$ docker run -it --rm ubuntu:20.04 bash -c 'echo $HOSTNAME'
e610946f50c1

Here, we're calling on bash inside the container to process everything inside the single quotes, so variable substitution and/or expansion isn't applied by your shell, but by the shell inside the container.

Another approach is:

me@computer:~$ cat test.sh
#!/bin/bash

echo $HOSTNAME

me@computer:~$ cat test.sh | docker run -i --rm ubuntu:20.04 bash
62ba950a60fe

In this case, cat is "pushing" the contents of the script to bash the container, so it's functionally equivalent to my first example. The first method is "cleaner", however if you've got a more complex script, multi-line variables or other stuff that's difficult to put into a single command, then the second method is a better choice.

Note: The hostname is different in each example, because I'm using the --rm option which discards the container once it exits. This is great when you want to run a command in a container but don't need to keep the container afterwards.

Upvotes: 0

Darren Smith
Darren Smith

Reputation: 2488

Expanding on the eval approach, here is a simple bash script that will use eval to evaluate a string as a sequence of bash commands:

#!/usr/bin/env bash
echo program args:  $@
eval $@

but beware, eval comes with dangers:

https://medium.com/dot-debug/the-perils-of-bash-eval-cc5f9e309cae

Upvotes: 2

Adiii
Adiii

Reputation: 59906

First thing, $@ it will just print the number of argument

#!/bin/sh

echo "Value of MY_VAR is" $MY_VAR

echo "Begin"

$@

echo "Done"

$@ = stores all the arguments in a list of string

$* = stores all the arguments as a single string

$# = stores the number of arguments

What does $@ mean in a shell script?

Second thing, When run the below command

docker run env-test:latest 'echo $MY_VAR'

It will look for $MY_VAR in host system not in the container.

To access container env you have to pass them as -e MY_VAR=test ,not as argument to docker run command which will in the ENV in host.

docker run -e MY_VAR=test env-test:latest

So the value of MY_VAR will be test not production.

To debug the argument to docker run

export MY_VAR2="value_from_host"

Now run

docker run env-test:latest "echo $MY_VAR2"

so the value will value_from_host because thes argument pick from host.

Upvotes: 1

Related Questions