当当前工作目录不包含空格时,我的 Docker 包装脚本按预期工作,但是当它包含空格时存在错误。
我简化了一个例子,以利用我能找到的最小的官方Docker镜像和一个众所周知的GNU核心实用程序。当然,这个例子不是很有用。在我的真实用例中,打包了一个更复杂的环境。
码头工人包装脚本:
#!/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 "$@"
例如,您可以将其保存在某处作为/tmp/docker_ls
。记得chmod +x /tmp/docker_ls
现在,您可以在任何不包含空格的路径中使用此 Dockerized ls,如下所示:
/tmp/docker_ls -lah
/tmp/docker_ls -lah | grep 'r'
请注意,未实现/tmp/docker_ls -lah /path/to/something
。包装器脚本必须适应解析参数并将 path 参数挂载到容器中。
你能明白为什么当当前工作目录路径包含空格时这不起作用吗?可以做些什么来纠正它?
溶液:
@david迷宫的回答解决了这个问题。请参阅:https://stackoverflow.com/a/55763212/1782641
根据他的建议,我重构了我的脚本,如下所示:
#!/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 "$@"
如果您的目标是以当前主机用户的身份在当前主机目录上运行进程,您会发现使用主机进程要容易得多,更安全,而不是像 Docker 这样故意试图向您隐藏这些事情的隔离层。 对于您展示的内容,我会跳过 Docker 并运行
#!/bin/sh
ls "$@"
大多数软件在没有Docker的情况下安装起来相当简单,要么使用像APT这样的包管理器,要么使用文件系统级别的隔离,如Python的虚拟环境和Node的node_modules
目录。 如果你正在编写这个脚本,那么Docker只是妨碍了你。
在可移植的 shell 脚本中,无法以保持其个人冗长的方式制作"单词列表"。 如果你知道你总是想传递一些麻烦的选项,那么这仍然相当简单:将它们直接包含在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 "$@"
一些 shell(如 ksh、bash 和 zsh)具有数组类型,但这些 shell 可能并非存在于每个系统或环境中(例如,您的 busybox 映像没有任何这些)。 您还可以考虑选择一种更高级别的脚本语言,该语言可以更明确地将单词传递到exec
类型调用中。
我正在尝试一下,让你尝试一下: 更改此设置:
DOCKER_RUN_OPTIONS+="--rm --user=$(id -u $(logname)):$(id -g $(logname)) --workdir=${WORK_DIR} --mount type=bind,source=${WORK_DIR},target=${WORK_DIR}"
对此:
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' 命令上由 bash 评估 $DOCKER_RUN_OPTIONS 变量时转义空间。
我没有尝试过这个 - 这只是一种预感/第一枪。