bash - 在这个简单的 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 "$@"
解决方案
如果您的目标是以当前主机用户身份在当前主机目录上运行进程,您会发现使用主机进程更容易、更安全,而不是像 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
您还可以考虑选择一种可以更明确地将单词传递给类型调用的高级脚本语言。
推荐阅读
- css - 移动分辨率和容器的flexlayout问题
- python - 为什么写入文件有效,但写入 HTTP 服务器请求处理程序无效?
- php - Raspberry Pi 是否强大到足以运行 laravel 应用程序?
- apache-spark - *所有* Spark 属性键的列表在哪里?
- c++ - 如何通过另一个向量的方向调整一个向量?
- sql - CSV 文件的 ODBC 驱动程序问题
- javascript - 如何动态更改标题的宽度以确保行长均匀
- c# - 使用 Razor Pages、AJAX 和 Entity Framework Core 添加新的子数据项
- php - Wordpress PHP:将帖子与类别存档上的自定义帖子类型结合起来
- c++ - CNTK:->Forward 或 ->Evaluate crash 在某些 PC 上,而不是在其他 PC 上