Передача сложного сценария оболочки через docker exe c sh - c "..." - PullRequest
0 голосов
/ 05 марта 2020

У меня есть скрипт, который отлично работает в sh на хосте linux, а также внутри альпийского контейнера. Но когда я пытаюсь выполнить это, используя docker exec <containerID> sh -c "<script>", он плохо себя ведет. Функция скрипта заключается в выводе материала, подобного ps.

systick=$(getconf CLK_TCK); for c in /proc/*/cmdline; do d=$(dirname $c); name=$(grep Name: $d/status); pid=$(basename $d); uid=$(grep Uid: $d/status); uid=$(echo ${uid#Uid:} | xargs); uid=${uid%% *}; user=$(grep :$uid:[0-9] /etc/passwd); user=${user%%:*}; cmdline=$(cat $c|xargs -0 echo); starttime=$(($(awk '{print $22}' $d/stat) / systick)); uptime=$(awk '{print int($1)}' /proc/uptime); elapsed=$(($uptime-$starttime)); echo $pid $user $elapsed $cmdline; done

EDIT: sh -c "<script>" имеет такое же поведение.

Ответы [ 2 ]

2 голосов
/ 05 марта 2020

Вы не можете запустить этот скрипт из docker exec, потому что переменные будут интерполированы до того, как они будут отправлены в контейнер (т. Е. Вы будете получать значения с вашего локального компьютера, а не из контейнера).

Для того, чтобы запустить его, как вы sh, вам нужно заменить $ на \$ для каждого вхождения $ в вашем скрипте.

Что может работать лучше, так это поместите ваш скрипт в файл, затем сопоставьте файл с местоположением в контейнере, используя -v (т.е. -v script.sh:/path/to/script.sh), и вызовите скрипт через docker exec /path/to/script.sh

1 голос
/ 18 марта 2020

Часть 1: Рабочий ответ

Рабочий однострочный (цитируется для использования Docker)

getProcessDataDef='shellQuoteWordsDef='"'"'shellQuoteWords() { sq="'"'"'"'"'"'"'"'"'"; dq='"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'; for arg; do printf "'"'"'"'"'"'"'"'"'%s'"'"'"'"'"'"'"'"' " "$(printf '"'"'"'"'"'"'"'"'%s\n'"'"'"'"'"'"'"'"' "$arg" | sed -e "s@${sq}@${sq}${dq}${sq}${dq}${sq}@g")"; done; printf '"'"'"'"'"'"'"'"'\n'"'"'"'"'"'"'"'"'; }'"'"'; shellQuoteNullSeparatedStream() { xargs -0 sh -c "${shellQuoteWordsDef};"'"'"' shellQuoteWords "$@"'"'"' _; }; getProcessData() { systick=$(getconf CLK_TCK); for c in /proc/*/cmdline; do d=${c%/*}; pid=${d##*/}; name=$(awk '"'"'/^Name:/ { print $2 }'"'"' <"$d"/status); uid=$(awk '"'"'/^Uid:/ { print $2 }'"'"' <"$d"/status); pwent=$(getent passwd "$uid"); user=${pwent%%:*}; cmdline=$(shellQuoteNullSeparatedStream <"$c"); starttime=$(awk -v systick="$systick" '"'"'{print int($22 / systick)}'"'"' "$d"/stat); uptime=$(awk '"'"'{print int($1)}'"'"' /proc/uptime); elapsed=$((uptime-starttime)); echo "$pid $user $elapsed $cmdline"; done; }; getProcessData'
sh -c "$getProcessDataDef"  # or docker exec <container> sh -c "$getProcessDataDef"

Рабочий однострочник (перед кавычками / экранированием)

shellQuoteWordsDef='shellQuoteWords() { sq="'"'"'"; dq='"'"'"'"'"'; for arg; do printf "'"'"'%s'"'"' " "$(printf '"'"'%s\n'"'"' "$arg" | sed -e "s@${sq}@${sq}${dq}${sq}${dq}${sq}@g")"; done; printf '"'"'\n'"'"'; }'; shellQuoteNullSeparatedStream() { xargs -0 sh -c "${shellQuoteWordsDef};"' shellQuoteWords "$@"' _; }; getProcessData() { systick=$(getconf CLK_TCK); for c in /proc/*/cmdline; do d=${c%/*}; pid=${d##*/}; name=$(awk '/^Name:/ { print $2 }' <"$d"/status); uid=$(awk '/^Uid:/ { print $2 }' <"$d"/status); pwent=$(getent passwd "$uid"); user=${pwent%%:*}; cmdline=$(shellQuoteNullSeparatedStream <"$c"); starttime=$(awk -v systick="$systick" '{print int($22 / systick)}' "$d"/stat); uptime=$(awk '{print int($1)}' /proc/uptime); elapsed=$((uptime-starttime)); echo "$pid $user $elapsed $cmdline"; done; }; getProcessData "$@"

Что пошло в одну строку

shellQuoteWordsDef='shellQuoteWords() { sq="'"'"'"; dq='"'"'"'"'"'; for arg; do printf "'"'"'%s'"'"' " "$(printf '"'"'%s\n'"'"' "$arg" | sed -e "s@${sq}@${sq}${dq}${sq}${dq}${sq}@g")"; done; printf '"'"'\n'"'"'; }'

shellQuoteNullSeparatedStream() {
  xargs -0 sh -c "${shellQuoteWordsDef};"' shellQuoteWords "$@"' _
}

getProcessData() {
  systick=$(getconf CLK_TCK)
  for c in /proc/*/cmdline; do
    d=${c%/*}; pid=${d##*/}
    name=$(awk '/^Name:/ { print $2 }' <"$d"/status)
    uid=$(awk '/^Uid:/ { print $2 }' <"$d"/status)
    pwent=$(getent passwd "$uid")
    user=${pwent%%:*}
    cmdline=$(shellQuoteNullSeparatedStream <"$c")
    starttime=$(awk -v systick="$systick" '{print int($22 / systick)}' "$d"/stat)
    uptime=$(awk '{print int($1)}' /proc/uptime)
    elapsed=$((uptime-starttime))
    echo "$pid $user $elapsed $cmdline"
  done
}

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

Для облегчения чтения и редактирования приведенная выше функция выглядит следующим образом:

# This is the function we're including in our code passed to xargs in-band above:
shellQuoteWords() {
  sq="'"; dq='"'
  for arg; do
    printf "'%s' " "$(printf '%s\n' "$arg" | sed -e "s@${sq}@${sq}${dq}${sq}${dq}${sq}@g")"
  done
  printf '\n'
}

Часть 2. Как был создан этот ответ

Python имеет отличную функцию shlex.quote() (или pipes.quote() в Python 2) может использоваться для генерации версии строки в кавычках. В этом контексте это можно использовать следующим образом:

Python 3.7.6 (default, Feb 27 2020, 15:15:00)
[Clang 7.1.0 (tags/RELEASE_710/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> s = r'''
... shellQuoteWords() {
...   sq="'"; dq='"'
...   for arg; do
...     printf "'%s' " "$(printf '%s\n' "$arg" | sed -e "s@${sq}@${sq}${dq}${sq}${dq}${sq}@g")"
...   done
...   printf '\n'
... }
... '''
>>> import shlex
>>> print(shlex.quote(s))
'
shellQuoteWords() {
  sq="'"'"'"; dq='"'"'"'"'"'
  for arg; do
    printf "'"'"'%s'"'"' " "$(printf '"'"'%s\n'"'"' "$arg" | sed -e "s@${sq}@${sq}${dq}${sq}${dq}${sq}@g")"
  done
  printf '"'"'\n'"'"'
}
'

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

s='
shellQuoteWords() {
  sq="'"'"'"; dq='"'"'"'"'"'
  for arg; do
    printf "'"'"'%s'"'"' " "$(printf '"'"'%s\n'"'"' "$arg" | sed -e "s@${sq}@${sq}${dq}${sq}${dq}${sq}@g")"
  done
  printf '"'"'\n'"'"'
}
'
eval "$s"
shellQuoteWords "hello world" 'hello world' "hello 'world'" 'hello "world"'

... и получить полностью допустимый вывод.

Тот же процесс был выполнен для генерации строки, которая соответствует определению getProcessData.

...