首页 > 解决方案 > 在这个简单的 Docker 包装脚本示例中,如何正确传递包含空格的当前工作目录路径?

问题描述

当当前工作目录不包含空格时,我的 Docker 包装脚本会按预期工作,但是当它包含空格时会出现错误。

我已经简化了一个示例,以使用我能找到的最小的官方 Docker 映像和一个众所周知的 GNU 核心实用程序。当然这个例子不是很有用。在我的真实世界用例中,打包了一个更复杂的环境。

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

例如,您可以将其保存在某处/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未实施。包装脚本必须适应解析参数并将路径参数安装到容器中。

你能明白为什么当当前工作目录路径包含空格时这不起作用吗?可以做些什么来纠正它?

解决方案:

@david-maze 的回答解决了这个问题。请参阅: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 "$@"

标签: bashdockerpathwhitespacewrapper

解决方案


如果您的目标是以当前主机用户身份在当前主机目录上运行进程,您会发现使用主机进程更容易、更安全,而不是像 Docker 这样故意试图对您隐藏这些东西的隔离层。对于您所展示的内容,我将跳过 Docker 并运行

#!/bin/sh
ls "$@"

node_modules大多数软件在没有 Docker 的情况下安装起来相当简单,要么使用像 APT 这样的包管理器,要么使用像 Python 的虚拟环境和 Node 的目录这样的文件系统级隔离。如果您正在编写此脚本,那么 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 "$@"

像 ksh、bash 和 zsh 这样的一些 shell 具有数组类型,但这些 shell 可能不会出现在每个系统或环境中(例如,您的 busybox 映像没有这些类型)。exec您还可以考虑选择一种可以更明确地将单词传递给类型调用的高级脚本语言。


推荐阅读