[英]In this simple Docker wrapper script example, how may one correctly pass a current working directory path which contains spaces?
My Docker wrapper script works as intended when the current working directory does not contain spaces, however there is a bug when it does.当当前工作目录不包含空格时,我的 Docker 包装器脚本按预期工作,但是当它包含时存在一个错误。
I have simplified an example to make use of the smallest official Docker image I could find and a well known GNU core utility.我简化了一个示例,以使用我能找到的最小的官方 Docker 映像和一个众所周知的 GNU 核心实用程序。 Of course this example is not very useful.
当然这个例子不是很有用。 In my real world use case, a much more complicated environment is packaged.
在我的实际用例中,打包了一个更复杂的环境。
Docker Wrapper Script: Docker 包装脚本:
#!/usr/bin/env bash
##
## Dockerized ls
##
set -eux
# Only allocate tty if one is detected
# See https://stackoverflow.com/questions/911168/how-to-detect-if-my-shell-script-is-running-through-a-pipe
if [[ -t 0 ]]; then
DOCKER_RUN_OPTIONS+="-i "
fi
if [[ -t 1 ]]; then
DOCKER_RUN_OPTIONS+="-t "
fi
WORK_DIR="$(realpath .)"
DOCKER_RUN_OPTIONS+="--rm --user=$(id -u $(logname)):$(id -g $(logname)) --workdir=${WORK_DIR} --mount type=bind,source=${WORK_DIR},target=${WORK_DIR}"
exec docker run ${DOCKER_RUN_OPTIONS} busybox:latest ls "$@"
You can save this somewhere as /tmp/docker_ls
for example.例如,您可以将其保存为
/tmp/docker_ls
。 Remember to chmod +x /tmp/docker_ls
记得
chmod +x /tmp/docker_ls
Now you are able to use this Dockerized ls in any path which contains no spaces as follows:现在,您可以在任何不包含空格的路径中使用此 Dockerized ls,如下所示:
/tmp/docker_ls -lah
/tmp/docker_ls -lah | grep 'r'
Note that /tmp/docker_ls -lah /path/to/something
is not implemented.请注意,
/tmp/docker_ls -lah /path/to/something
未实现。 The wrapper script would have to be adapted to parse parameters and mount the path argument into the container.包装器脚本必须适应解析参数并将路径参数安装到容器中。
Can you see why this would not work when current working directory path contains spaces?当当前工作目录路径包含空格时,您能明白为什么这不起作用吗? What can be done to rectify it?
可以做些什么来纠正它?
Solution:解决方案:
@david-maze's answer solved the problem. @david-maze 的回答解决了这个问题。 Please see: https://stackoverflow.com/a/55763212/1782641
请参阅: https : //stackoverflow.com/a/55763212/1782641
Using his advice I refactored my script as follows:根据他的建议,我重构了我的脚本如下:
#!/usr/bin/env bash
##
## Dockerized ls
##
set -eux
# Only allocate tty if one is detected. See - https://stackoverflow.com/questions/911168
if [[ -t 0 ]]; then IT+=(-i); fi
if [[ -t 1 ]]; then IT+=(-t); fi
USER="$(id -u $(logname)):$(id -g $(logname))"
WORKDIR="$(realpath .)"
MOUNT="type=bind,source=${WORKDIR},target=${WORKDIR}"
exec docker run --rm "${IT[@]}" --user "${USER}" --workdir "${WORKDIR}" --mount "${MOUNT}" busybox:latest ls "$@"
If your goal is to run a process on the current host directory as the current host user, you will find it vastly easier and safer to use a host process, and not an isolation layer like Docker that intentionally tries to hide these things from you.如果您的目标是作为当前主机用户在当前主机目录上运行一个进程,您会发现使用主机进程更容易和更安全,而不是像 Docker 这样故意试图向您隐藏这些东西的隔离层。 For what you're showing I would just skip Docker and run
对于您所展示的内容,我将跳过 Docker 并运行
#!/bin/sh
ls "$@"
Most software is fairly straightforward to install without Docker, either using a package manager like APT or filesystem-level isolation like Python's virtual environments and Node's node_modules
directory.大多数软件在没有 Docker 的情况下安装起来相当简单,要么使用 APT 之类的包管理器,要么使用 Python 的虚拟环境和 Node 的
node_modules
目录之类的文件系统级隔离。 If you're writing this script then Docker is just getting in your way.如果您正在编写此脚本,那么 Docker 就会妨碍您。
In a portable shell script there's no way to make “a list of words” in a way that keeps their individual wordiness.在可移植的 shell 脚本中,没有办法以保持它们各自冗长的方式制作“单词列表”。 If you know you'll always want to pass some troublesome options then this is still fairly straightforward: include them directly in the
docker run
command and don't try to create a variable of options.如果你知道你总是想传递一些麻烦的选项,那么这仍然相当简单:将它们直接包含在
docker run
命令中,不要尝试创建选项变量。
#!/bin/sh
RM_IT="--rm"
if [[ -t 0 ]]; then RM_IT="$RM_IT -i"; fi
if [[ -t 1 ]]; then RM_IT="$RM_IT -t"; fi
UID=$(id -u $(logname))
GID=$(id -g $(logname))
# We want the --rm -it options to be expanded into separate
# words; we want the volume options to stay as a single word
docker run $RM_IT "-u$UID:$GID" "-w$PWD" "-v$PWD:$PWD" \
busybox \
ls "$@"
Some shells like ksh, bash, and zsh have array types, but these shells may not be present on every system or environment (your busybox image doesn't have any of these for example).一些 shell,如 ksh、bash 和 zsh 具有数组类型,但这些 shell 可能不会出现在每个系统或环境中(例如,您的 busybox 映像没有任何这些)。 You also might consider picking a higher-level scripting language that can more explicitly pass words into an
exec
type call.您还可以考虑选择一种更高级的脚本语言,它可以更明确地将单词传递到
exec
类型调用中。
I'm taking a stab at this to give you something to try: Change this:我正在尝试这个给你一些尝试:改变这个:
DOCKER_RUN_OPTIONS+="--rm --user=$(id -u $(logname)):$(id -g $(logname)) --workdir=${WORK_DIR} --mount type=bind,source=${WORK_DIR},target=${WORK_DIR}"
To this:对此:
DOCKER_RUN_OPTIONS+="--rm --user=$(id -u $(logname)):$(id -g $(logname)) --workdir=${WORK_DIR} --mount type=bind,source='${WORK_DIR}',target='${WORK_DIR}'"
Essentially, we are putting the ' in there to escape the space when the $DOCKER_RUN_OPTIONS variable is evaluated by bash on the 'exec docker' command.本质上,当 $DOCKER_RUN_OPTIONS 变量由 'exec docker' 命令上的 bash 评估时,我们将 ' 放在那里以逃避空间。
I haven't tried this - it's just a hunch / first shot.我还没有尝试过——这只是一种预感/第一次尝试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.