使用带有'source'的Dockerfile中的RUN指令不起作用

Hug*_*own 237 bash shell docker

我有一个Dockerfile,我正在整理它来安装一个vanilla python环境(我将在其中安装一个应用程序,但在以后).

FROM ubuntu:12.04

# required to build certain python libraries
RUN apt-get install python-dev -y

# install pip - canonical installation instructions from pip-installer.org
# http://www.pip-installer.org/en/latest/installing.html
ADD https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py /tmp/ez_setup.py
ADD https://raw.github.com/pypa/pip/master/contrib/get-pip.py /tmp/get-pip.py
RUN python /tmp/ez_setup.py
RUN python /tmp/get-pip.py
RUN pip install --upgrade pip 

# install and configure virtualenv
RUN pip install virtualenv 
RUN pip install virtualenvwrapper
ENV WORKON_HOME ~/.virtualenvs
RUN mkdir -p $WORKON_HOME
RUN source /usr/local/bin/virtualenvwrapper.sh
Run Code Online (Sandbox Code Playgroud)

构建运行正常,直到最后一行,我得到以下异常:

[previous steps 1-9 removed for clarity]
...
Successfully installed virtualenvwrapper virtualenv-clone stevedore
Cleaning up...
 ---> 1fc253a8f860
Step 10 : ENV WORKON_HOME ~/.virtualenvs
 ---> Running in 8b0145d2c80d
 ---> 0f91a5d96013
Step 11 : RUN mkdir -p $WORKON_HOME
 ---> Running in 9d2552712ddf
 ---> 3a87364c7b45
Step 12 : RUN source /usr/local/bin/virtualenvwrapper.sh
 ---> Running in c13a187261ec
/bin/sh: 1: source: not found
Run Code Online (Sandbox Code Playgroud)

如果我ls进入该目录(只是为了测试前面提到的步骤)我可以看到文件按预期存在:

$ docker run 3a87 ls /usr/local/bin
easy_install
easy_install-2.7
pip
pip-2.7
virtualenv
virtualenv-2.7
virtualenv-clone
virtualenvwrapper.sh
virtualenvwrapper_lazy.sh
Run Code Online (Sandbox Code Playgroud)

如果我尝试运行该source命令,我会得到与上面相同的"未找到"错误.但是,如果我运行交互式shell会话,则源确实有效:

$ docker run 3a87 bash
source
bash: line 1: source: filename argument required
source: usage: source filename [arguments]
Run Code Online (Sandbox Code Playgroud)

我可以从这里运行脚本,然后愉快地访问workon,mkvirtualenv等等.

我已经做了一些挖掘,最初看起来好像问题可能在于bash作为Ubuntu 登录shelldash作为Ubuntu 系统shell之间的区别,破坏不支持source命令.

但是,答案似乎是使用'.' 而不是source,但这只会导致Docker运行时爆炸出现恐慌异常.

从Dockerfile RUN指令运行shell脚本以解决此问题的最佳方法是什么(运行Ubuntu 12.04 LTS的默认基本映像).

cho*_*obo 288

RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"

  • 嗯?如果您在shell中仅为该命令存在脚本,则它不能对将来运行的任何命令产生持久影响,假设其操作的总和是设置环境变量.那么为什么你会使用`source`,而不仅仅是`bash/usr/local/bin/virtualenvwrapper.sh`,那么呢? (61认同)
  • 即使它有效,这也是不正确的.阅读https://docs.docker.com/engine/reference/builder/#run并在第二个代码示例后不要停止.阅读**注意:**紧随其后.因为`/ bin/sh -c`是默认shell,所以RUN的这个"shell形式"转换为`RUN ["/ bin/sh"," - c","/ bin/bash"" - c""source /usr/local/bin/virtualenvwrapper.sh"]`.你应该继续使用RUN的"exec形式",这样你就可以把`sh`取出来像`RUN ["/ bin/bash""-c""source /usr/local/bin/virtualenvwrapper.sh" ]` (25认同)
  • `RUN/bin/bash -c"source /usr/local/bin/virtualenvwrapper.sh; my_command; my_command; my_command;"` (12认同)
  • 请参阅/sf/answers/3156095771/以了解为什么这会创建一个嵌套在`sh`中的`bash`,因此应该避免. (7认同)
  • 一个更好的答案是在这里:/sf/answers/2955123251/ (3认同)
  • 上次我检查这对我不起作用 (2认同)

小智 143

原始答案

FROM ubuntu:14.04
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
Run Code Online (Sandbox Code Playgroud)

这适用于每个Ubuntu docker基础映像.我通常会为我编写的每个Dockerfile添加此行.

由有关旁观者编辑

如果你想获得"使用bash而不是sh整个Dockerfile"的效果,而不改变可能损坏容器内的操作系统,你可以告诉Docker你的意图.这是这样完成的:

SHELL ["/bin/bash", "-c"]
Run Code Online (Sandbox Code Playgroud)

*可能的损坏是Linux 中的许多脚本(在新的Ubuntu安装上grep -rHInE '/bin/sh' /返回超过2700个结果)期望完全POSIX shell /bin/sh.bash shell不只是POSIX加上额外的内置函数.内置(和更多)与POSIX中的内容完全不同.我完全支持避免POSIX(以及任何你没有在另一个shell上测试的脚本因为你认为你避免使用basmisms而工作的谬论)并且只是使用了bashism.但是你在脚本中使用适当的shebang来做到这一点.不是从整个操作系统下拉出POSIX shell.(除非您有时间验证Linux附带的所有2700多个脚本以及您安装的任何软件包中的所有脚本.)

以下答案中有更多细节./sf/answers/3156095771/

  • `ln -s/bin/bash/bin/sh`这是一个可怕的想法.ubuntu目标/ bin/sh冲破原因.dash是一个完全posix shell,比bash快几个数量级.将/ bin/sh链接到bash将大大降低服务器的性能.引用:http://wiki.ubuntu.com/DashAsBinSh (27认同)
  • 这可以简化一点:`ln -snf/bin/bash/bin/sh` (18认同)
  • 这是一个肮脏的黑客,而不是一个解决方案.如果你的脚本是由`sh` shell运行的,但你想要`bash`,那么正确的解决办法就是让`sh`进程调用`bash`作为一次性,例如'bash -c'源/ script.sh && ...'`,*或*你甚至可以完全避免使用bashisms(比如`source`),而是选择只使用有效的POSIX等价物,例如`./ script.sh`.(注意`.`之后的空格!)最后,如果你的脚本是可执行的(不仅仅是可获取的),*永远不要*如果它实际上不兼容,那么你的脚本应该用`#!/ bin/sh` shebang.请改用#!/ bin/bash. (6认同)
  • 现在,我该如何对原始答案进行投票,并通过“有关人员”对编辑进行投票? (6认同)
  • 给相关旁观者一个椰子。我来这里是为了`SHELL`命令,而不是为了用bash替换sh。 (3认同)
  • @ user1442219这将默认命令解释器从`sh`替换为`bash` (2认同)

Ahm*_*any 52

RUN指令的默认shell 是["/bin/sh", "-c"].

RUN "source file"      # translates to: RUN /bin/sh -c "source file"
Run Code Online (Sandbox Code Playgroud)

使用SHELL指令,您可以更改RUNDockerfile中后续指令的默认shell :

SHELL ["/bin/bash", "-c"] 
Run Code Online (Sandbox Code Playgroud)

现在,默认shell已更改,您无需在每个RUN指令中明确定义它

RUN "source file"    # now translates to: RUN /bin/bash -c "source file"
Run Code Online (Sandbox Code Playgroud)

附加说明:您还可以添加--login启动登录shell的选项.这意味着~/.bachrc例如将被读取,您不需要在命令之前明确地获取它


And*_*ndi 45

我遇到了同样的问题,为了在virtualenv中执行pip安装,我不得不使用这个命令:

RUN pip install virtualenv virtualenvwrapper
RUN mkdir -p /opt/virtualenvs
ENV WORKON_HOME /opt/virtualenvs
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh \
    && mkvirtualenv myapp \
    && workon myapp \
    && pip install -r /mycode/myapp/requirements.txt"
Run Code Online (Sandbox Code Playgroud)

我希望它有所帮助.


mix*_*xja 41

最简单的方法是使用点运算符代替source,这是与bash source命令相当的sh :

代替:

RUN source /usr/local/bin/virtualenvwrapper.sh
Run Code Online (Sandbox Code Playgroud)

使用:

RUN . /usr/local/bin/virtualenvwrapper.sh
Run Code Online (Sandbox Code Playgroud)

  • 这不起作用,因为每个RUN命令独立工作.当RUN命令完成时,来自`source`或`.`的更改将丢失.请参阅:/sf/answers/2803215131/ (5认同)

Mit*_*ril 23

如果您使用的是Docker 1.12或更高版本,请使用SHELL!

简答:

一般:

SHELL ["/bin/bash", "-c"] 
Run Code Online (Sandbox Code Playgroud)

为python vituralenv:

SHELL ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]
Run Code Online (Sandbox Code Playgroud)

答案很长:

来自https://docs.docker.com/engine/reference/builder/#/shell

SHELL ["executable", "parameters"]
Run Code Online (Sandbox Code Playgroud)

SHELL指令允许覆盖用于shell形式命令的默认shell.Linux上的默认shell是["/ bin/sh"," - c"],在Windows上是["cmd","/ S","/ C"].SHELL指令必须以JSON格式写入Dockerfile.

SHELL指令在Windows上特别有用,其中有两个常用且完全不同的本机shell:cmd和powershell,以及包括sh的备用shell.

SHELL指令可以多次出现.每个SHELL指令都会覆盖所有先前的SHELL指令,并影响所有后续指令.例如:

FROM microsoft/windowsservercore

# Executed as cmd /S /C echo default
RUN echo default

# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default

# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello

# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello
Run Code Online (Sandbox Code Playgroud)

当在Dockerfile中使用它们的shell形式时,SHELL指令可能会影响以下指令:RUN,CMD和ENTRYPOINT.

以下示例是在Windows上找到的常见模式,可以使用SHELL指令简化:

...
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
...
Run Code Online (Sandbox Code Playgroud)

docker调用的命令将是:

cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
Run Code Online (Sandbox Code Playgroud)

由于两个原因,这是低效的.首先,调用一个不必要的cmd.exe命令处理器(也就是shell).其次,shell形式的每个RUN指令都需要一个额外的powershell -command前缀命令.

为了提高效率,可以采用两种机制中的一种.一种是使用RUN命令的JSON形式,例如:

...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...
Run Code Online (Sandbox Code Playgroud)

虽然JSON表单是明确的,并且不使用不必要的cmd.exe,但它确实需要通过双引号和转义更加详细.备用机制是使用SHELL指令和shell表单,为Windows用户提供更自然的语法,特别是与escape parser指令结合使用时:

# escape=`

FROM microsoft/nanoserver
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'
Run Code Online (Sandbox Code Playgroud)

导致:

PS E:\docker\build\shell> docker build -t shell .
Sending build context to Docker daemon 4.096 kB
Step 1/5 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/5 : SHELL powershell -command
 ---> Running in 6fcdb6855ae2
 ---> 6331462d4300
Removing intermediate container 6fcdb6855ae2
Step 3/5 : RUN New-Item -ItemType Directory C:\Example
 ---> Running in d0eef8386e97


    Directory: C:\


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       10/28/2016  11:26 AM                Example


 ---> 3f2fbf1395d9
Removing intermediate container d0eef8386e97
Step 4/5 : ADD Execute-MyCmdlet.ps1 c:\example\
 ---> a955b2621c31
Removing intermediate container b825593d39fc
Step 5/5 : RUN c:\example\Execute-MyCmdlet 'hello world'
 ---> Running in be6d8e63fe75
hello world
 ---> 8e559e9bf424
Removing intermediate container be6d8e63fe75
Successfully built 8e559e9bf424
PS E:\docker\build\shell>
Run Code Online (Sandbox Code Playgroud)

SHELL指令也可用于修改shell的运行方式.例如,在Windows上使用SHELL cmd/S/C/V:ON | OFF,可以修改延迟的环境变量扩展语义.

如果需要备用shell,例如zsh,csh,tcsh等,也可以在Linux上使用SHELL指令.

在Docker 1.12中添加了SHELL功能.


Tom*_*Tom 20

基于此页面上的答案,我将补充说,您必须知道每个RUN语句都独立于其他语句运行/bin/sh -c,因此不会获得通常在登录shell中获取的任何环境变量.

到目前为止,我找到的最好的方法是添加脚本/etc/bash.bashrc,然后以bash登录的方式调用每个命令.

RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
RUN /bin/bash --login -c "your command"
Run Code Online (Sandbox Code Playgroud)

例如,您可以安装和设置virtualenvwrapper,创建虚拟环境,在使用bash登录时激活它,然后将python模块安装到此环境中:

RUN pip install virtualenv virtualenvwrapper
RUN mkdir -p /opt/virtualenvs
ENV WORKON_HOME /opt/virtualenvs
RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
RUN /bin/bash --login -c "mkvirtualenv myapp"
RUN echo "workon mpyapp" >> /etc/bash.bashrc
RUN /bin/bash --login -c "pip install ..."
Run Code Online (Sandbox Code Playgroud)

阅读有关bash启动文件的手册有助于了解何时获取源代码.


Bru*_*sky 15

https://docs.docker.com/engine/reference/builder/#run默认的[Linux的]壳牌RUN/bin/sh -c.你似乎期待bashisms,所以你应该使用"exec形式" RUN来指定你的shell.

RUN ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]
Run Code Online (Sandbox Code Playgroud)

否则,使用RUN的"shell表单"并指定不同的shell会导致嵌套shell.

# don't do this...
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"
# because it is the same as this...
RUN ["/bin/sh", "-c", "/bin/bash" "-c" "source /usr/local/bin/virtualenvwrapper.sh"]
Run Code Online (Sandbox Code Playgroud)

如果您有多个命令需要不同的shell,则应阅读https://docs.docker.com/engine/reference/builder/#shell并通过将其置于RUN命令之前更改您的默认shell:

SHELL ["/bin/bash", "-c"]
Run Code Online (Sandbox Code Playgroud)

最后,如果您在根用户的.bashrc文件中放置了所需的任何内容,则可以将该-l标志添加到SHELLRUN命令以使其成为登录shell并确保其获取源.

注意:我故意忽略了这样一个事实:将脚本作为RUN中唯一的命令来源是没有意义的.


Gia*_*ati 11

根据Docker文档

要使用除'/ bin/sh'之外的其他shell,请使用传入所需shell的exec表单.例如,

RUN ["/bin/bash", "-c", "echo hello"]
Run Code Online (Sandbox Code Playgroud)

请参阅https://docs.docker.com/engine/reference/builder/#run


vik*_*027 6

source在 Dockerfile 中运行时也遇到了问题

这对于构建 CentOS 6.6 Docker 容器来说运行得非常好,但在 Debian 容器中却出现了问题

RUN cd ansible && source ./hacking/env-setup
Run Code Online (Sandbox Code Playgroud)

这就是我解决这个问题的方法,可能不是一种优雅的方式,但这对我有用

RUN echo "source /ansible/hacking/env-setup" >> /tmp/setup
RUN /bin/bash -C "/tmp/setup"
RUN rm -f /tmp/setup
Run Code Online (Sandbox Code Playgroud)


Moh*_*han 5

如果你SHELL有空,你应该使用这个答案——不要使用已接受的答案,这会迫使你根据此评论将 dockerfile 的其余部分放在一个命令中。

如果您使用的是旧的 Docker 版本并且无权访问SHELL,那么只要您不需要任何东西.bashrc(这在 Dockerfiles 中很少见),这将起作用:

ENTRYPOINT ["bash", "--rcfile", "/usr/local/bin/virtualenvwrapper.sh", "-ci"]
Run Code Online (Sandbox Code Playgroud)

请注意,-i需要让 bash 完全读取 rcfile。


Pau*_*rie 2

发生这种情况可能是因为sourcebash 是内置的,而不是文件系统上某处的二进制文件。您打算让您所获取的脚本随后更改容器吗?