Docker exec 使用通配符时报 No such file or directory
Docker exec 用星号列文件时报 No such file or directory,但进入容器后正常。区分宿主机与容器的通配符展开,用 sh -c 处理,并检查无匹配项、隐藏文件和引用范围。
容器里明明有文件,进入容器后执行 ls /tmp/sth/* 也能列出,换成下面的命令却找不到文件:
docker exec demo-app ls '/tmp/sth/*'
ls: cannot access '/tmp/sth/*': No such file or directory
报错路径里仍带着星号,是判断问题的线索。docker exec 启动的是指定程序,并不会自动在容器里套一层 shell。直接启动 ls 时,它收到的可能就是 /tmp/sth/* 这个字面参数,而非一组已经匹配好的文件名。
下面针对正在运行的 Linux 容器,容器内有 sh 和 ls;宿主机示例按 Linux、macOS 的 POSIX 风格 shell 编写。demo-app 是容器名占位值,/tmp/sth 是容器内目录,使用时替换为实际值。Windows 的命令提示符和 PowerShell 有各自的引号规则,不要直接照搬外层单引号写法。
先确认目录,不带星号列一次
docker exec demo-app ls -la /tmp/sth
这一步直接把目录交给容器内的 ls,不需要 shell 展开通配符。如果目录能列出,而且包含本来要匹配的文件,就可以继续检查命令解析方式。
如果这里也报目录不存在,应核对容器名、容器内路径和挂载位置;如果报权限拒绝,则检查执行身份及目录权限。单凭含星号的报错,不能认定文件确实不存在,也不能认定一定只是引号问题。比较“进入容器”和“外部执行”两种方式时,应使用同一个容器、相同执行用户和相同绝对路径,避免把工作目录变化混进来。
让容器里的 shell 展开星号
在宿主机的 POSIX 风格 shell 中执行:
docker exec demo-app sh -c 'ls -ld /tmp/sth/*'
外层单引号先让宿主机把整段命令原样作为一个参数传给 Docker。容器内的 sh -c 再解析这段文本,在容器的文件系统里匹配星号,并把匹配到的路径交给 ls。这里加 -d,让匹配到的子目录也按目录本身显示,方便确认匹配清单;如需继续查看子目录内容,再按实际需要调整。
不要改成下面这样:
docker exec demo-app sh -c 'ls -ld "/tmp/sth/*"'
这次星号在容器 shell 解析时仍被双引号包住,不再用于文件名匹配。外层引号负责保护整段命令,内层引号决定容器 shell 如何解释路径,两层作用不同。
目录含空格时,可以只引用目录部分,把星号留在容器 shell 的引号外:
docker exec demo-app sh -c 'ls -ld "/tmp/my files/"*'
也不要只给整个命令加引号,却省略 sh -c:Docker 需要一个可执行程序名;"ls /tmp/sth/*" 这样的整串文本不会自动拆成程序与参数。-t 只是分配伪终端,不能代替 shell 做通配符展开,列目录也不需要为了这件事加上它。
加了 sh -c 仍然报错的情况
如果 sh 自身无法启动,先确认镜像是否包含该程序。精简镜像可能没有 shell,也可能没有 ls。不要为了列几个文件就改造正在运行的生产容器;已知完整文件名时,可以用镜像中现有工具接收明确路径,或者在经过准备的诊断环境中检查。
如果 shell 已经启动,而错误还是带星号,需确认目标模式确有匹配项。POSIX shell 的文件名展开在没有匹配时会保留模式本身,因此“仍有星号”既可能是没有展开,也可能是展开后未找到匹配。目录读取或路径搜索权限不足也可能影响匹配,应结合前面的无通配符目录检查判断。
普通 * 不匹配以点开头的文件名,也不会递归穿过路径分隔符。目录只有隐藏文件时,前面的 ls -la 能看到它们,而 /tmp/sth/* 仍可能没有结果。此时按具体文件名检查,不要为了消除报错随意扩大模式,更不要立即把列文件命令换成删除命令。
宿主机端也应保留外层引用。未引用的星号可能在 Docker 启动前就被宿主机 shell 处理;结果取决于宿主机上的文件和 shell 设置,不能保证会传到容器。直接给路径加引号只能阻止宿主机展开,要在容器里匹配,仍需要显式启动能够展开它的程序。
确认修复只改变了匹配位置
核对 sh -c 输出中的完整路径,确认它们属于目标容器目录,且与不带通配符的目录清单一致。文件系统若正在变化,两次清单可能不同,应结合文件创建或清理时间判断。修复标准是列出了预期匹配项,而不只是终端没有错误文字。
上述检查不修改容器文件,不需要重建容器或放宽权限。若把改法写入运维脚本,保留修改前版本,并先用列目录命令确认范围;出现非预期匹配时,恢复原脚本并重新核对引用。sh -c 会解释一整段命令,不要把不可信输入直接拼进这段文本,尤其不要将尚未核对的通配符用于批量覆盖或删除。


