Sourcing (& ldquo; dotting & rdquo;) сценарий оболочки из Docker [дубликат]

Другое событие NullPointerException возникает, когда объявляется массив объектов, а затем сразу же пытается разыменовать его внутри.

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

Этот конкретный NPE можно избежать, если порядок сравнения отменяется ; а именно, использовать .equals для гарантированного непустого объекта.

Все элементы внутри массива инициализируются их общим начальным значением ; для любого типа массива объектов, это означает, что все элементы null.

Вы должны инициализировать элементы в массиве перед доступом или разыменованием их.

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

163
задан Hugo Rodger-Brown 17 December 2013 в 15:29
поделиться

13 ответов

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

187
ответ дан chobo 19 August 2018 в 00:29
поделиться
  • 1
    ^ Это правильный ответ – xero 9 May 2016 в 18:36
  • 2
    А? Если вы создаете скрипт внутри оболочки, которая существует только для этой команды, она не может иметь долгосрочного эффекта для любых будущих команд, предполагая, что общая сумма его действия задает переменные среды. Итак, почему вы использовали source вообще, а только bash /usr/local/bin/virtualenvwrapper.sh, в таком случае? – Charles Duffy 21 June 2016 в 13:31
  • 3
    RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh; my_command; my_command; my_command;" – Leons 1 September 2016 в 02:28
  • 4
    Это неверно, хотя оно работает. Прочитайте docs.docker.com/engine/reference/builder/#run и не останавливайтесь после второго образца кода. Прочтите Примечание: , который следует за ним. Поскольку /bin/sh -c является оболочкой по умолчанию, эта «форма оболочки» означает, RUN переводится на RUN ["/bin/sh", "-c", "/bin/bash" "-c" "source /usr/local/bin/virtualenvwrapper.sh"]. Вы должны продолжить и использовать форму "exec" из RUN, чтобы вы могли вывести sh так, как RUN ["/bin/bash" "-c" "source /usr/local/bin/virtualenvwrapper.sh"] – Bruno Bronosky 13 July 2017 в 16:50
  • 5
    См. stackoverflow.com/a/45087082/117471 , чтобы понять, почему это создает bash, вложенный в sh, и поэтому его следует избегать. – Bruno Bronosky 13 July 2017 в 17:10

Проверьте команду SHELL . По умолчанию оболочка на Linux - это ["/ bin / sh", "-c"]

RUN "source file"      # translates to: RUN /bin/sh -c "source file"

Вы можете изменить оболочку по умолчанию, используя SHELL, которая меняет оболочку, используемую для последующих команд RUN в Dockerfile

SHELL ["/bin/bash", "-c"]

Теперь оболочка по умолчанию изменилась, и вам не нужно явно определять ее в каждой инструкции RUN

RUN "source file"    # now translates to: RUN /bin/bash -c "source file"

. Дополнительная заметка: вы также можете добавить --login который запускает оболочку входа. Это означает, что ~/.bachrc, например, будет прочитан, и вам не нужно явно указывать его перед вашей командой

25
ответ дан Ahmad Abdelghany 19 August 2018 в 00:29
поделиться
  • 1
    Великий указатель на использование --login - просто понял это сам – mattexx 16 August 2017 в 02:09

У меня была та же проблема, и для выполнения установки pip внутри virtualenv мне пришлось использовать эту команду:

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"

Надеюсь, это поможет.

37
ответ дан Andrea Grandi 19 August 2018 в 00:29
поделиться

Согласно https://docs.docker.com/engine/reference/builder/#run по умолчанию [Linux] для RUN является /bin/sh -c. Кажется, вы ожидаете багизмов, поэтому вы должны использовать «форму exec» для RUN, чтобы указать вашу оболочку.

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

В противном случае, используя «оболочечную форму» RUN и указав другую оболочку приводит к вложенным оболочкам.

# 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"]

Если у вас более 1 команды, которым требуется другая оболочка, вы должны прочитать https://docs.docker.com/engine/reference/builder/ #shell и измените оболочку по умолчанию, поставив ее перед вашими командами RUN:

SHELL ["/bin/bash", "-c"]

Наконец, если вы поместили что-либо в файл .bashrc пользователя root, который вам нужен, вы можете добавьте флаг -l в команду SHELL или RUN, чтобы сделать его оболочкой входа и убедитесь, что она получена.

Примечание. Я намеренно игнорировал тот факт, что это бессмысленно для источника сценарий как единственная команда в RUN.

104
ответ дан Bruno Bronosky 19 August 2018 в 00:29
поделиться
  • 1
    Здравствуй! Можете ли вы описать, что делает этот скрипт? Я также хочу запустить мой Dockerfile с / bin / bash для изображения Debian. Кажется, что вы удаляете / bin / sh. Это хороший способ метода? – Lanti 25 June 2015 в 10:47
  • 2
    Это можно немного упростить: ln -snf /bin/bash /bin/sh – apottere 30 September 2015 в 20:41
  • 3
    @ user1442219 это заменяет интерпретатор команд по умолчанию от sh до bash – Bhargav Nanekalva 19 October 2015 в 15:15
  • 4
    ln -s /bin/bash /bin/sh Это ужасная идея. ubuntu цели / bin / sh для тире по какой-либо причине. тире - это полностью posix-оболочка, которая на порядки выше, чем bash. привязка / bin / sh к bash резко снизит производительность вашего сервера. cite: wiki.ubuntu.com/DashAsBinSh – xero 9 May 2016 в 18:35
  • 5
    Это грязный хак, а не решение. Если ваш скрипт запускается оболочкой sh, но вы хотите bash, правильным решением является либо процесс sh вызывать bash как одноразовый, например. bash -c 'source /script.sh && …', или , вы можете даже дойти до того, что полностью избегаете бахизмов (например, source) и вместо этого предпочитаете использовать только действительные эквиваленты POSIX, например. . /script.sh. (Запомните пробел после .!) Наконец, если ваш скрипт является исполняемым (не просто исходным), never заставляет ваш скрипт лежать в #!/bin/sh shebang, если он не является sh-совместимым. Вместо этого используйте #!/bin/bash. – Mark G. 13 September 2016 в 13:56
  • 6
    Это АКТУАЛЬНЫЙ правильный ответ. Автор выбранного ответа stackoverflow.com/a/25086628/117471 , кажется, только прочитал первый пример в документации, на которую вы ссылаетесь. Кажется, они не читали следующий абзац, который вы цитировали. – Bruno Bronosky 13 July 2017 в 16:39
  • 7
    SHELL ["/bin/sh", "-c", "-l"], поэтому он источник ~ / .bashrc и т. д., если у вас есть настройки среды из базового контейнера – MortenB 17 August 2018 в 17:38
  • 8
    @MortenB, но вы указали (typoed?) /bin/sh, который не решит проблему bash, которая не используется. Кроме того, когда вы делаете docker build, маловероятно, что вам понадобится что-то полезное в .bashrc пользователя root. Но, если вы помещаете что-то там раньше в Dockerfile (например, возможно, JAVA_HOME, то да. В своем ответе я напишу об этом. – Bruno Bronosky 17 August 2018 в 17:51
  • 9

Я закончил тем, что поместил свой материал env в .profile и мутировал SHELL что-то вроде

SHELL ["/bin/bash", "-c", "-l"]

# Install ruby version specified in .ruby-version
RUN rvm install $(<.ruby-version)

# Install deps
RUN rvm use $(<.ruby-version) && gem install bundler && bundle install

CMD rvm use $(<.ruby-version) && ./myscript.rb
0
ответ дан mattexx 19 August 2018 в 00:29
поделиться

Если вы используете Docker 1.12 или новее, просто используйте SHELL!

Short Answer:

general:

SHELL ["/bin/bash", "-c"] 

для python vituralenv :

SHELL ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]

Длительный ответ:

из https://docs.docker.com/engine/reference/builder/#/shell

SHELL ["executable", "parameters"]

Команда SHELL позволяет использовать оболочку по умолчанию, используемую для оболочки команд команд для переопределения. Стандартная оболочка в Linux - это ["/ bin / sh", "-c"], а в Windows - ["cmd", "/ S", "/ C"]. Инструкция SHELL должна быть записана в форме JSON в файле Docker.

Инструкция SHELL особенно полезна в Windows, где есть две обычно используемые и совершенно разные родные оболочки: cmd и powershell, а также альтернативные оболочки включая sh.

Инструкция 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

Следующие инструкции могут быть затронуты инструкцией SHELL, когда форма оболочки из них используется в файле Docker: RUN, CMD и ENTRYPOINT.

Следующие пример - общий шаблон, найденный в Windows, который можно оптимизировать с помощью инструкции SHELL:

...
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
...

Команда, вызываемая докером, будет:

cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

Это неэффективно для две причины. Во-первых, вызывается ненужный командный процессор cmd.exe (aka shell). Во-вторых, для каждой инструкции RUN в форме оболочки требуется дополнительная команда powershell-command, префиксная команда.

Чтобы сделать это более эффективным, можно использовать один из двух механизмов. Один из них - использовать форму JSON команды RUN, такую ​​как:

...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...

. Хотя форма JSON недвусмысленная и не использует ненужный cmd.exe, для этого требуется более многословие, цитируя и убегая. Альтернативным механизмом является использование команды SHELL и формы оболочки, что делает более естественным синтаксис для пользователей Windows, особенно в сочетании с директивой анализа парсера:

# 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'

Результат:

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>

Инструкция SHELL также может использоваться для изменения способа работы оболочки. Например, используя SHELL cmd / S / C / V: ON | OFF в Windows, можно было бы модифицировать семантику расширения замедленной среды.

Инструкция SHELL может также использоваться в Linux, если требуется чередование оболочки такие как zsh, csh, tcsh и другие.

Функция SHELL была добавлена ​​в Docker 1.12.

12
ответ дан Mithril 19 August 2018 в 00:29
поделиться

Самый простой способ - использовать оператор точки вместо источника, что эквивалентно команде bash source:

Вместо:

RUN source /usr/local/bin/virtualenvwrapper.sh

Использовать :

RUN . /usr/local/bin/virtualenvwrapper.sh
36
ответ дан mixja 19 August 2018 в 00:29
поделиться

Это может произойти, потому что source является встроенным в bash, а не двоичным где-то в файловой системе. Является ли ваше намерение использовать сценарий, который вы используете для изменения контейнера после этого?

1
ответ дан Paul Morie 19 August 2018 в 00:29
поделиться
  • 1
    Сценарий обновляет контейнер, но, честно говоря, я пытался сделать что-то, что не имело смысла, поэтому обошел проблему. – Hugo Rodger-Brown 18 December 2013 в 18:15

Если вы просто пытаетесь использовать pip для установки чего-то в virtualenv, вы можете изменить PATH env, чтобы сначала просмотреть папку bin в виртуальном каталоге

ENV PATH="/path/to/venv/bin:${PATH}"

Затем любые команды pip install, которые следуют в файле Docker, сначала найдут / path / to / venv / bin / pip и будут использовать это, которое будет установлено в этот virtualenv, а не системный python.

0
ответ дан shadfc 19 August 2018 в 00:29
поделиться

Основываясь на ответах на этой странице, я бы добавил, что вы должны знать, что каждый оператор RUN работает независимо от других с помощью /bin/sh -c и, следовательно, не будет получать каких-либо окружающих vars, которые, как правило, будут получены в оболочках входа.

Лучший способ, который я нашел до сих пор, - добавить скрипт в /etc/bash.bashrc, а затем вызвать каждую команду в качестве входа в bash.

RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
RUN /bin/bash --login -c "your command"

Вы могли бы, например, установить и настроить virtualenvwrapper, создать виртуальный env, активировать его при использовании входа в bash, а затем установить ваши модули python в это env:

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 ..."

Чтение руководства в файлах загрузки bash помогает понять, что получается, когда.

18
ответ дан TomDotTom 19 August 2018 в 00:29
поделиться
  • 1
    Прохладный, на основе вашего решения, что я только что проиллюстрировал, было: ADD env-file /etc/profile.d/installerenv.sh RUN /bin/bash --login -c 'env' RUN /bin/bash -c 'rm /etc/profile.d/installerenv.sh' Если в случае использования добавляется больше переменных среды инъекции в перспективу построения докеров, как и у меня, я бы рекомендовал взглянуть на docs.docker.com/compose/yml/#env-file тоже. – daniel.kahlenberg 28 July 2015 в 10:26
  • 2
    Проблема в этом, я считаю, заключается в том, что вы не завершаете кеширование результатов каждой команды RUN, а это значит, что вы не можете установить множество зависимостей проекта, а затем скопировать исходный код и воспользоваться преимуществами преимущества промежуточного кэширования Docker. Он будет переустанавливать все зависимости проекта каждый раз. – erewok 15 June 2016 в 21:57
  • 3
    Используйте /etc/bashrc для Redhat вместо /etc/bash.bashrc, как указано выше (для Ubuntu) – Jordan Gee 15 November 2017 в 06:49
  • 4
    Я использовал /root/.bashrc для centos. – schmudu 7 February 2018 в 17:42
  • 5

У меня также были проблемы с запуском source в файле Dockerfile

. Это отлично работает для создания контейнера Docker CentOS 6.6, но давало проблемы в контейнерах Debian

RUN cd ansible && source ./hacking/env-setup

. как я это решал, может быть, не изящный способ, но это то, что сработало для меня

RUN echo "source /ansible/hacking/env-setup" >> /tmp/setup
RUN /bin/bash -C "/tmp/setup"
RUN rm -f /tmp/setup
3
ответ дан vikas027 19 August 2018 в 00:29
поделиться

Возможно, вы захотите запустить bash -v, чтобы посмотреть, что происходит.

Я бы сделал следующее вместо игры с символическими ссылками:

RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc

2
ответ дан vimdude 19 August 2018 в 00:29
поделиться
108
ответ дан Bruno Bronosky 30 October 2018 в 12:10
поделиться