如何通过docker run将参数传递给Shell脚本


131

我是docker世界的新手。我必须调用一个通过Docker容器获取命令行参数的shell脚本。例如:我的Shell脚本如下所示:

#!bin/bash
echo $1

Dockerfile看起来像这样:

FROM ubuntu:14.04
COPY ./file.sh /
CMD /bin/bash file.sh

我不确定如何在运行容器时传递参数

Answers:


61

使用相同 file.sh

#!/bin/bash
echo $1

使用现有的Dockerfile构建映像:

docker build -t test .

使用参数abcxyz其他方式运行映像。

docker run -ti test /file.sh abc

docker run -ti test /file.sh xyz

27
如果您不希望最终用户直接了解file.sh,我认为ENTRYPOINT是必经之路。
greg.kindel

你怎么能像这样启动一个脚本docker run -ti test /file.sh abc。我认为该脚本不会运行,因为它应该运行docker run -ti test sh /file.sh abc。sh或/ bin / sh将正确运行。
Vamsidhar Muggulla '16

1
对于任何其他来这里的人。/ usr / bin / env技巧是可选的样式首选项,不是使它起作用的必要条件。也是#!行指示使用默认购买的解释器。因此它可以仅通过调用脚本来运行。
whered那名字来自

163

有了这个脚本 file.sh

#!/bin/bash
echo Your container args are: "$@"

还有这个 Dockerfile

FROM ubuntu:14.04
COPY ./file.sh /
ENTRYPOINT ["/file.sh"]

你应该能够:

% docker build -t test .
% docker run test hello world
Your container args are: hello world

9
如果您像我那样忘记了“ /file.sh”周围的“”,它将无法正常工作。
kev

6
由于某些原因,这ENTRYPOINT ./file.sh
不适

6
不要忘记chmod +x file.sh设置可执行标志。
topskip

1
@kev,你知道为什么会这样吗?["/file.sh"]/file.sh[/file.sh]
Nitzankin '18

1
@Nitzankin看到我的答案,为什么需要正确的json格式。
BMitch

60

对于Docker,传递此类信息的正确方法是通过环境变量。

因此,使用相同的Dockerfile,将脚本更改为

#!/bin/bash
echo $FOO

构建后,使用以下docker命令:

docker run -e FOO="hello world!" test

20
为什么这是投票率最高的答案?Env vars是传递信息的另一种方法,但这不是OP的要求。当然,OP将args传递到容器的愿望绝对没有不当之处。
局部多云,

8
@PartlyCloudy我认为人们喜欢这种方式,因为尽管显然是错误的,但它似乎给出了“正确”的答案。Docker的主要设计原则是将教条优先于常识。
augurar

1
@augurar:要改善此答案,也许您可​​以解释为什么您认为此答案“明显错误”?
EmilStenström'18年

2
SO上存在许多XY问题。由于OP指出他们是Docker的新手,因此给出建议的实现目标的建议是完全合理的答案。因此,这是一个很好的答案。
colm.anseo,

1
完成工作的最简单方法,而不是将变量作为生成args和所有这些混乱的东西传递。对于将机密作为环境变量进行传递非常有用。
Arvind Sridharan的

32

这里有一些相互作用的东西:

  1. docker run your_image arg1 arg2将替代的价值CMDarg1 arg2。那是CMD的完全替代,而不是附加更多的值。这就是为什么您经常看到docker run some_image /bin/bash在容器中运行bash shell的原因。

  2. 当您同时定义了ENTRYPOINT和CMD值时,docker通过将两者串联并运行该串联命令来启动容器。因此,如果您将入口点定义为file.sh,则现在可以运行带有其他args的容器,这些args将作为args传递给file.sh

  3. docker中的入口点和命令具有两种语法,一种将启动外壳的字符串语法,以及一种将执行exec的json语法。该外壳程序对于处理IO重定向,将多个命令链接在一起(以及类似的操作&&,变量替换等)非常有用。但是,该外壳程序会妨碍信号处理(如果您曾经看到10秒的延迟来停止)容器,这通常是原因)并将入口点和命令串联在一起。如果将入口点定义为字符串,则它将运行/bin/sh -c "file.sh",仅此一项就可以了。但是,如果您也将命令定义为字符串,则会看到类似/bin/sh -c "file.sh" /bin/sh -c "arg1 arg2"的命令正在容器内启动,效果不是很好。有关这两个选项如何相互作用的更多信息,请参见此处的表格

  4. shell -c选项仅接受一个参数。之后的所有内容都会以$1$2等形式传递给该单个参数,但不会传递给嵌入式Shell脚本,除非您显式传递了args。即/bin/sh -c "file.sh $1 $2" "arg1" "arg2"会工作,但/bin/sh -c "file.sh" "arg1" "arg2"因为file.sh没有参数就不会被调用。

综上所述,常见的设计是:

FROM ubuntu:14.04
COPY ./file.sh /
RUN chmod 755 /file.sh
# Note the json syntax on this next line is strict, double quotes, and any syntax
# error will result in a shell being used to run the line.
ENTRYPOINT ["file.sh"]

然后,您可以使用以下命令运行它:

docker run your_image arg1 arg2

有关更多详细信息,请访问:


1
我已经尝试过设置入口点以["bash", "--login", "-c"]获取映像中的/ etc / profile源,但是后来却想知道为什么不将args传递给传递给docker run的shell脚本...您的回答清除了,谢谢!
Apteryx

23

我所拥有的是一个实际运行脚本的脚本文件。此脚本文件可能相对复杂。我们称之为“ run_container”。该脚本从命令行获取参数:

run_container p1 p2 p3

一个简单的run_container可能是:

#!/bin/bash
echo "argc = ${#*}"
echo "argv = ${*}"

我想做的是,在“ dockering”之后,我希望能够使用docker命令行上的参数来启动此容器,如下所示:

docker run image_name p1 p2 p3

并以p1 p2 p3作为参数运行run_container脚本。

这是我的解决方案:

Dockerfile:

FROM docker.io/ubuntu
ADD run_container /
ENTRYPOINT ["/bin/bash", "-c", "/run_container \"$@\"", "--"]

7
ENTRYPOINT"/run_container \"$@\""表示包含空格的参数正确处理数组中的第三个值(例如docker run image_name foo 'bar baz' quux)。
davidchambers

在将switch / case语句添加到我的bash文件中之后,ENTRYPOINT [“ run_container.sh”]不再对我有用,但是ENTRYPOINT [“ sh”,“-c”,“ run_container.sh”]将不再接受我的参数。此解决方案(建议使用@davidchambers)对我有用。
汉密尔顿

10

如果要运行@build time:

CMD /bin/bash /file.sh arg1

如果要运行@run time:

ENTRYPOINT ["/bin/bash"]
CMD ["/file.sh", "arg1"]

然后在主机外壳中

docker build -t test .
docker run -i -t test

2
ENTRYPOINT对于我认为需要运行时的OP是一个很好的答案,但是如果您确实想要构建时间变量,那么这个答案就被打破了。使用ARGdocker build --build-arg docs.docker.com/engine/reference/builder/#arg
greg.kindel

0

另外的选择...

为了使这项工作

docker run -d --rm $IMG_NAME "bash:command1&&command2&&command3"

在dockerfile中

ENTRYPOINT ["/entrypoint.sh"]

在entrypoint.sh中

#!/bin/sh

entrypoint_params=$1
printf "==>[entrypoint.sh] %s\n" "entry_point_param is $entrypoint_params"

PARAM1=$(echo $entrypoint_params | cut -d':' -f1) # output is 1 must be 'bash' it     will be tested    
PARAM2=$(echo $entrypoint_params | cut -d':' -f2) # the real command separated by     &&

printf "==>[entrypoint.sh] %s\n" "PARAM1=$PARAM1"
printf "==>[entrypoint.sh] %s\n" "PARAM2=$PARAM2"

if [ "$PARAM1" = "bash" ];
then
    printf "==>[entrypoint.sh] %s\n" "about to running $PARAM2 command"
    echo $PARAM2 | tr '&&' '\n' | while read cmd; do
        $cmd
    done    
fi

一些说明和限制。...带有“:”的命令需要在cut -d':'中进行更改,而诸如docker run -d --rm $ IMG_NAME之类的命令“ bash:echo $ PATH”将显示主机路径值而不是主机一个
wagnermarques
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.