Passing a complex shell script via docker exec sh -c “…”

痴心易碎 提交于 2020-04-17 22:40:45

问题


I have a script that works fine in sh on a linux host as well as inside an alpine container. But when I try executing that using docker exec <containerID> sh -c "<script>" it misbehaves. The script's function is to output stuff similar to 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>" has the same behavior.


回答1:


Part 1: A Working Answer

A Working One-Liner (Quoted For Use By 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"

A Working One-Liner (Before Quoting/Escaping)

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 "$@"

What Went Into That One-Liner

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
}

What Went Into The Shell-Quoting Helper Used By That One-Liner

To allow easier reading and editing, the function stringified above looks like:

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

Part 2: How That Answer Was Created

Python has an excellent shlex.quote() function (or pipes.quote() in Python 2) that can be used to generate a shell-quoted version of a string. In this context, that can be used as follows:

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'"'"'
}
'

That result is itself a perfectly valid string in shell. That is to say, one can run:

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

...and get completely valid output.

The same process was followed to generate a string that evaluated to the definition of getProcessData.




回答2:


You are not able to run this script from docker exec because the variables will be interpolated before they sent to the container (i.e., you are going to get values from your local machine, not from within the container).

In order to run it as you wish, you need to replace $ with \$ for every occurrence of $ in your script.

What might work better is to put your script into a file, then map the file to a location within the container, using -v (i.e., -v script.sh:/path/to/script.sh), and call the script via docker exec /path/to/script.sh



来源:https://stackoverflow.com/questions/60536120/passing-a-complex-shell-script-via-docker-exec-sh-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!