尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Maven exec插件报错Failed to execute goal全解析:从Cannot run program到default-cli

Maven exec插件报错Failed to execute goal全解析:从Cannot run program到default-cli 前几天同事扔过来一段报错Maven 控制台一片红关键信息就一行[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:exec (default-cli) on project demo-service: Command execution failed.下面还跟着Cannot run program node ... No such file or directory这类提示。这种“Failed to execute goal”的报错表面上看是 Maven 插件执行失败但实际上 80% 的情况都不是插件本身坏了而是你没搞清楚 exec 插件到底在帮你执行什么以及它是在哪个目录、哪个环境下执行的。这篇文章就把这个报错彻底拆开从报错含义、常见根因到一步步排查给你一套可以直接照做的方案省得每次遇到都靠重启、清缓存碰运气。这个内容适合所有用 Maven 的 Java 开发者尤其是那些会把前后端构建命令、脚本命令塞进 Maven 流程里的场景。看完你不仅能解决当前的报错还能顺手搞懂mvn exec:java、mvn exec:exec和 default-cli 这几个概念的区别。1. 先搞清楚这个报错到底是什么1.1 报错信息逐段拆解很多同学看到Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:exec (default-cli) on project xxx就直接蒙了其实这段英文就是 Maven 在跟你汇报org.codehaus.mojo:exec-maven-plugin:3.0.0是出问题的插件坐标groupId 是org.codehaus.mojoartifactId 是exec-maven-plugin版本是3.0.0。这是一个很老的插件从 Maven 早期就有专门用来在 Maven 构建前后执行外部命令或 Java 程序。:exec是你调用的插件 goal也就是要执行的目标。exec 插件有java、exec、debug等 goal你命令行敲的什么这里就显示什么。(default-cli)是执行来源。意思是这个 goal 没有被绑定到compile、test之类的 Maven 生命周期阶段而是你直接从命令行敲出来的。比如mvn exec:exec、mvn exec:java就会标记为 default-cli。on project demo-service表示报错发生在哪个 Maven 项目上。如果是多模块项目你能在这里快速定位到具体是哪个子模块挂了。所以整段话翻译过来就是我在demo-service这个项目上用 exec 插件执行目标时失败了。这句话本身只是外层包装真正有用的信息往往在它下面一行。Maven 会把插件内部抛出的异常继续打印比如Error occurred in starting fork, check output in log、Command execution failed、Cannot run program这类。后续所有排查都应该围绕这行内层异常展开。1.2 同名“exec”报错本质是一类问题我注意到最近搜索热度里有一个很像的报错oci runtime exec failed: exec failed: unable to start container process: exe还有个是无法识别 git 命令:exec: git: executable file not found in %path%。这两个虽然场景不同一个在 Docker 容器里一个在 Windows 命令行但和 Maven 的Failed to execute goal本质上是同一件事执行器executor找不到要启动的程序或者程序启动后立刻退出、返回了非零错误码。Docker 里docker exec找不到/bin/bash时会报unable to start container processWindows 上%PATH%没配好时会报executable file not found in %path%Maven exec 插件找不到node、git时会报Cannot run program node。底层逻辑一模一样只是不同的环境把同一个问题包装成了不同文案。所以这篇文章虽然标题是 Maven 的报错但排查思路对所有“exec 类失败”都通用先确认程序在哪再确认它在哪跑最后看它跑起来之后干了什么。2. 常见的触发场景与根因画像2.1 直接执行 exec:java 却找不到主类这是最经典的一种。pom.xml 里只写了依赖没配置 exec 插件的mainClass然后你在命令行敲mvn exec:java这时 Maven 会尝试按默认规则找主类找不到就直接抛异常。核心报错往往长这样Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:java (default-cli) on project demo: The parameters mainClass for goal org.codehaus.mojo:exec-maven-plugin:3.0.0:java are missing or invalid解决办法很简单要么在 pom 里给插件配上mainClass要么在命令行用-Dexec.mainClasscom.example.Main指定。两种方式本质一样只是把参数的来源换了一下。2.2 配置了 executable但命令不在 PATH 里另一种更常见的情况是mvn exec:execpom 里配置了一个executable比如configuration executablegit/executable arguments argumentstatus/argument /arguments /configuration然后执行mvn exec:exec报错Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:exec (default-cli) on project demo: Command execution failed. Cannot run program git (in directory /opt/projects/demo): error2, No such file or directory注意这里Cannot run program git后面的error2, No such file or directory它的意思不是当前目录下没有 git 仓库而是 exec 插件在系统 PATH 环境变量里找不到git这个可执行文件。我在实际环境中遇到过好几种奇怪变体服务器上装了 git但 Maven 进程通过 systemd 启动时 PATH 被裁剪了shell 里明明能执行python3但 exec 插件却报找不到因为 exec 插件用的是 JVM 的ProcessBuilder它不读取你 shell 里的别名和~/.bashrc。还有一种更隐蔽的就是可执行文件存在但权限不对。报错信息会变成Permission denied。这个问题在 Windows 上不太常见但在 Linux、macOS 上非常典型尤其是你自己编译出来的脚本没加执行权限就直接丢给 exec 插件去跑。2.3 命令能跑但工作目录不对还有一种情况命令找到了但执行时报“目录不存在”或者“找不到文件”。[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:exec (default-cli) on project demo: Command execution failed. Cannot run program bash (in directory /root/projects/demo-service/sub-module)...如果你配置过workingDirectory但指向的目录不存在就会看到类似报错。即使没有显式配置Maven 默认会在当前项目模块的根目录下执行命令也就是${project.basedir}。多模块项目里这个目录往往会跟着模块切换如果你的脚本硬编码了相对路径换个模块执行就很容易翻车。2.4 与容器、命令行 exec 报错的场景交叉前面提到热搜里的 Docker exec 报错和 Windows 命令找不到它们和 Maven 这个报错经常会在同一个开发环境里连续轰炸你你写好了一个 Maven 配的 exec 脚本本地 Windows 没问题推到 CI 的 Linux 容器里却报Cannot run program。你在 Dockerfile 里用RUN mvn exec:exec结果容器基础镜像里没装 git 或 node报出跟 OCI exec 类似的程序找不到错误。你在 Windows 上通过cmd /c启动了 MavenMaven 再去启动git结果%PATH%里没有 git 的安装目录报出exec: git: executable file not found in %path%。这就是为什么我建议大家遇到“exec 相关报错”时不要只盯着当前这一条错误消息而是把它归类为“执行外部程序失败”这一类问题。这一类问题的排查路径有高度共性后面第三部分会详细讲。3. 一步步排查与解决直接照做3.1 第一步开 -e 或 -X拿到底层真实异常遇到Failed to execute goal第一个动作不是改 pom而是重新跑一次命令加上-e参数拿到完整堆栈mvn exec:exec -e加上-e后Maven 会打印异常堆栈你一般能在Caused by:后面看到真正的底因。比如Caused by: java.io.IOException: Cannot run program node (in directory /path/to/project): error2, No such file or directory at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)这条信息直接告诉你两件事要执行的是node执行时的工作目录是/path/to/project。这两个信息足够解决大部分问题。如果-e还不够就用-XMaven 会进入调试模式打印非常多内部日志。虽然噪音大但当你怀疑插件版本冲突、依赖解析异常时-X是唯一能看清真相的方式。个人经验是先用-e解决不了再用-X。3.2 第二步检查 pom.xml 里的 exec 插件配置拿到底因之后基本就能判断是不是配置问题了。exec 插件最常见的两种配置场景我整理成可直接复制的模板。第一种用exec:java直接运行一个 Java 类plugin groupIdorg.codehaus.mojo/groupId artifactIdexec-maven-plugin/artifactId version3.0.0/version configuration mainClasscom.example.Main/mainClass arguments argument--server.port8080/argument /arguments /configuration /plugin第二种用exec:exec执行外部命令plugin groupIdorg.codehaus.mojo/groupId artifactIdexec-maven-plugin/artifactId version3.0.0/version configuration executablebash/executable arguments argumentscripts/deploy.sh/argument /arguments workingDirectory${project.basedir}/workingDirectory /configuration /plugin这里有几个容易踩的坑exec:exec和exec:java的配置是独立的你在exec:java里配了mainClass但执行mvn exec:exec时根本不会用到。搞清楚你当前命令行敲的是哪个 goal。arguments标签会把每个argument作为一个独立参数传给程序不要在多个 argument 里塞一个带空格的完整命令那样会出现“命令被拆成两段”的错误。workingDirectory建议统一用${project.basedir}作为根不要写死绝对路径否则换台机器就废了。还有一个特别隐蔽的问题如果 pom 里的配置跟命令行里的-Dexec.executable...或-Dexec.args...混用Maven 会用命令行参数覆盖 pom 配置。我在现场排查过一起“明明 pom 里写的是跑 A 脚本结果控制台跑的是 B 命令”的诡异问题最后发现是 Jenkins 配置里残留了-Dexec.executable。3.3 第三步确认命令是否存在、是否在 PATH 里如果报错信息里有Cannot run program xxx或No such file or directory不要犹豫先去命令行手动验证一下这个命令到底能不能跑。在 Linux、macOS 上执行which git echo $PATH在 Windows 上执行where git echo %PATH%如果命令不存在或者 PATH 不对这就是根因。修改方式有两种在操作系统层面把命令所在目录加入 PATH。在 exec 插件配置里使用命令的绝对路径如executableC:\Program Files\Git\bin\bash.exe/executable。我更推荐第一种因为绝对路径没法跨环境迁移。不过这里也有一个坑Windows 下的路径带空格如果你把整条命令塞进executableexec 插件会直接按这个字符串去找程序容易触发Cannot run program。更安全的做法是executablebash.exe/executable然后依赖 PATH 去定位。另外如果你是在 IDE 里点 Maven 窗口的按钮触发 exec注意 IDE 里的 PATH 可能跟你终端里的完全不一样。IDEA 的 Maven 运行器默认可能只继承 IDE 启动时的环境变量你在终端里export的东西它不一定看得到。遇到“终端能跑IDE 报错”的情况优先检查 IDE 里的 Runner 环境变量设置。3.4 第四步处理多模块项目的工作目录与依赖顺序多模块项目是 exec 插件报错的高发区。我见过最典型的场景是这样的父 pom 配置了 exec 插件期望在某个子模块里执行命令。但 Maven 实际执行的目录可能与你想象的不一致尤其当你执行mvn -pl module-a exec:exec-pl module-a只是把你的构建范围限定在 module-aexec 插件的 workingDirectory 却不一定自动变成 module-a。如果你没有显式配置workingDirectory它默认是当前模块的${project.basedir}这取决于你是怎么聚合构建的。还有一种情况exec 插件要去执行一条命令这条命令依赖前一个模块刚生成的文件。如果你没有配置 Maven 的构建顺序或者直接对单个模块执行 exec依赖文件不存在命令就会失败。这种问题从报错上看非常像“文件找不到”但根因是模块构建顺序不对。解决方式是在父 pom 里把 exec 绑定到某个生命周期阶段并依赖响应的maven-dependency-plugin或其他前置插件。一个比较稳妥的多模块场景配置是plugin groupIdorg.codehaus.mojo/groupId artifactIdexec-maven-plugin/artifactId version3.0.0/version configuration executablebash/executable arguments argument${project.basedir}/scripts/run.sh/argument /arguments workingDirectory${project.parent.basedir}/workingDirectory /configuration /plugin注意这里我用了${project.parent.basedir}目的就是把工作目录放到父目录而不是默认的模块目录。实际用哪个取决于你的脚本期望在哪运行。3.5 第五步最小化复现再逐个放回如果你把上面的步骤都走完了报错还在那就进入“最小化复现”环节。我自己处理过太多这类案子最后的杀手锏永远是先建一个临时目录里面只放一个最简 pom.xml 和一段最小测试命令验证 exec 插件本身能不能工作。最小 pom 示例project modelVersion4.0.0/modelVersion groupIdcom.example/groupId artifactIdexec-test/artifactId version1.0.0/version build plugins plugin groupIdorg.codehaus.mojo/groupId artifactIdexec-maven-plugin/artifactId version3.0.0/version configuration executableecho/executable arguments argumenthello/argument /arguments /configuration /plugin /plugins /build /project然后在目录里执行mvn exec:exec如果这个能跑通说明插件本身没问题接下来把你原项目里的依赖、配置、环境逐条加回来直到复现报错。这个“二分归因”的过程虽然笨但在复杂的多模块、多环境变量场景里效率远高于瞎猜。4. 常见问题速查表与避坑经验4.1 典型报错关键词速查对照表报错关键词或片段根因处理方式The parameters mainClass ... missing or invalid执行exec:java但没配主类配置mainClass或命令行-Dexec.mainClassCommand execution failedCannot run program xxxxxx 不在 PATH或没有执行权限验证命令路径修正 PATH 或改成绝对路径Error occurred in starting fork, check output in logfork 模式下启动子进程失败加上-e看完整堆栈检查内存参数、JVM 路径Cannot run program xxx (in directory ...) ... No such file or directory工作目录不存在或工作目录与命令路径不匹配修正workingDirectory确保目录真实存在exec: git: executable file not found in %path%Windows 下 PATH 未包含 Git 安装目录修改系统 PATH或在插件中指定 git.exe 绝对路径oci runtime exec failed容器内执行程序失败通常是镜像缺程序或命令路径不对进容器检查命令缺什么装什么注意基础镜像差异Performing fork mode ...后一直卡住或无输出exec 插件等待子进程输出子进程可能阻塞了检查命令是否等待输入重定向标准输入输出权限相关Permission denied目标脚本没有执行权限chmod x script.sh或调整文件权限4.2 我踩过的那些坑写出来给你避雷第一点永远不要忽略default-cli这个标记。它意味着你的命令跟生命周期无关也就是说你执行mvn clean install时它不会跑只有手动敲mvn exec:exec才会跑。如果某天你发现 CI 里明明看到 exec 相关日志但你的 pom 根本没配那就是别人在命令行直接调的。这种情况下排查配置意义不大得去查构建脚本、Jenkins 配置、IDE 启动配置。第二点exec 插件默认使用的 shell 不是你终端里的 shell。很多人误以为mvn exec:exec -Dexec.executablenode -v会把node -v当一条完整命令执行实际上 exec 插件不会把字符串丢给/bin/sh去解释它只会按空格拆分参数然后通过ProcessBuilder去启动第一个参数。结果就会报Cannot run program node -v。如果你确实想用 shell 管道、通配符、重定向最省事的方式是把executable设为bash、sh或cmd /c然后把这整段命令作为参数传进去。第三点Windows 下文件路径里的反斜杠和 Maven XML 的转义规则会夹在一起坑你。比如argumentC:\temp\script.bat/argument这个配置里\t会被当成制表符吗在 XML 里确实有这种风险。我的习惯是统一用正斜杠即使是在 Windows 下Java 的ProcessBuilder也能解析正斜杠路径能避免一大半转义问题。第四点如果你用 exec 插件去启动 Spring Boot 应用最好设置一下环境变量和skip条件避免打包时误触发。一个很实用的配置是configuration skip${skip.exec}/skip /configuration然后在执行时加上-Dskip.exectrue就能快速跳过 exec 任务。这个习惯能省下非常多调试时间尤其是在本地反复打包时。4.3 一点额外的心得把“exec 失败”当成信号而不是事故我在处理 Docker 的 OCI runtime exec 报错和 Maven exec 报错时发现很多新手容易陷入一个误区到处搜报错文案把希望寄托在“复制粘贴某段配置就能好”上。实际上这类问题的根源往往非常简单但环境的差异让同一个错误在不同机器上有完全不同的表现。所以我更建议你把精力放在“掌握定位能力”而不是“记住某个修复方案”上。任何时候遇到 exec 失败先回答三个问题它要执行的程序是什么它是在哪个工作目录执行的它执行时继承了什么环境变量把这三个问题一确认大部分报错都能迎刃而解。剩下的那部分就是程序本身运行后返回了非零退出码这种问题就要打开脚本、打开日志去看了跟 Maven 插件本身已经没有关系。我现在排查这类问题习惯先看一眼报错原文里Caused by之前有没有出现in directory有就直接ls那个目录没有就去which那个命令。整个过程基本控制在五分钟以内。你可以试一下这个方法下次再看到Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:exec (default-cli)就不会再慌了。
返回列表