在 picocli 中,如何在命令行上设置选项以覆盖 @-file 中的相同选项
In picocli how do you make options on the command line to override the same option in an @-file
我目前在 Java 11 应用程序中使用 picocli 4.7.0-SNAPSHOT 效果很好,该应用程序具有一组复杂的选项,因此我正在使用 @-file 功能。
我试图开始工作的是直接在命令行上指定的选项,以覆盖@-文件中存在的相同选项。因此在命令行上指定的选项优先于@-文件。可以吗
当我尝试 运行 我的测试应用程序时,主要基于 picocli 示例,同时使用命令行选项和 @-file,我从 picocli 得到以下错误以及预期的用法:
myapp --sourceDatabaseType=MySQL @.\myapp.options
option '--sourceDatabaseType' (<sourceDatabaseType>) should be specified only once
然后是预期的使用信息。
让我解释一下这个问题,看看我是否理解正确:
如果最终用户直接在命令行上指定一个选项,则应使用命令行值,而如果未在命令行上指定该选项,则应使用文件中的值。
本质上,您使用的是一个@文件,目的是为一个或多个选项定义default values。
但是,这不是 @-files 的设计目的:picocli 无法区分来自命令行的参数和来自 @-file 的参数。
我建议改用 picocli 的 default provider 机制。
一个想法是使用 built-in PropertiesDefaultProvider
:
import picocli.CommandLine.PropertiesDefaultProvider;
@Command(name = "myapp", defaultValueProvider = PropertiesDefaultProvider.class)
class MyApp { }
PropertiesDefaultProvider
也使用一个文件,该文件中的值仅用于命令行中未指定的选项。
棘手的一点是这个文件的位置。 PropertiesDefaultProvider
在以下位置查找文件:
- 系统指定的路径属性
picocli.defaults.${COMMAND-NAME}.path
- 最终用户的用户主目录中名为
.${COMMAND-NAME}.properties
的文件
(将${COMMAND-NAME}
替换为命令名称,所以对于名为myapp
的命令,系统属性是picocli.defaults.myapp.path
)
为了让最终用户能够指定文件的位置,我们需要在 picocli 完成解析命令行参数之前设置系统 属性。
我们可以使用 @Option
注释的 setter 方法来做到这一点。例如:
class MyApp {
@Option(names = "-D")
void setSystemProperty(Map<String, String> properties) {
System.getProperties().putAll(properties);
}
}
这将允许最终用户使用如下方式调用命令:
myapp --sourceDatabaseType=MySQL -Dpicocli.defaults.myapp.path=.\myapp.options
如果这太冗长,您可以更进一步,创建一个特殊的 -@
选项,以允许用户使用类似这样的命令调用命令:
myapp --sourceDatabaseType=MySQL -@.\myapp.options
此实现将是带注释的 setter 方法,类似于上面的方法:
class MyApp {
@Spec CommandSpec spec; // injected by picocli
@Option(names = "-@")
void setDefaultProviderPath(File path) {
// you could do some validation here:
if (!path.canRead()) {
String msg = String.format("ERROR: file not found: %s", path);
throw new ParameterException(spec.commandLine(), msg);
}
// only set the system property if the file exists
System.setProperty("picocli.defaults.myapp.path", path.toString());
}
}
我目前在 Java 11 应用程序中使用 picocli 4.7.0-SNAPSHOT 效果很好,该应用程序具有一组复杂的选项,因此我正在使用 @-file 功能。
我试图开始工作的是直接在命令行上指定的选项,以覆盖@-文件中存在的相同选项。因此在命令行上指定的选项优先于@-文件。可以吗
当我尝试 运行 我的测试应用程序时,主要基于 picocli 示例,同时使用命令行选项和 @-file,我从 picocli 得到以下错误以及预期的用法:
myapp --sourceDatabaseType=MySQL @.\myapp.options
option '--sourceDatabaseType' (<sourceDatabaseType>) should be specified only once
然后是预期的使用信息。
让我解释一下这个问题,看看我是否理解正确:
如果最终用户直接在命令行上指定一个选项,则应使用命令行值,而如果未在命令行上指定该选项,则应使用文件中的值。
本质上,您使用的是一个@文件,目的是为一个或多个选项定义default values。 但是,这不是 @-files 的设计目的:picocli 无法区分来自命令行的参数和来自 @-file 的参数。
我建议改用 picocli 的 default provider 机制。
一个想法是使用 built-in PropertiesDefaultProvider
:
import picocli.CommandLine.PropertiesDefaultProvider;
@Command(name = "myapp", defaultValueProvider = PropertiesDefaultProvider.class)
class MyApp { }
PropertiesDefaultProvider
也使用一个文件,该文件中的值仅用于命令行中未指定的选项。
棘手的一点是这个文件的位置。 PropertiesDefaultProvider
在以下位置查找文件:
- 系统指定的路径属性
picocli.defaults.${COMMAND-NAME}.path
- 最终用户的用户主目录中名为
.${COMMAND-NAME}.properties
的文件
(将${COMMAND-NAME}
替换为命令名称,所以对于名为myapp
的命令,系统属性是picocli.defaults.myapp.path
)
为了让最终用户能够指定文件的位置,我们需要在 picocli 完成解析命令行参数之前设置系统 属性。
我们可以使用 @Option
注释的 setter 方法来做到这一点。例如:
class MyApp {
@Option(names = "-D")
void setSystemProperty(Map<String, String> properties) {
System.getProperties().putAll(properties);
}
}
这将允许最终用户使用如下方式调用命令:
myapp --sourceDatabaseType=MySQL -Dpicocli.defaults.myapp.path=.\myapp.options
如果这太冗长,您可以更进一步,创建一个特殊的 -@
选项,以允许用户使用类似这样的命令调用命令:
myapp --sourceDatabaseType=MySQL -@.\myapp.options
此实现将是带注释的 setter 方法,类似于上面的方法:
class MyApp {
@Spec CommandSpec spec; // injected by picocli
@Option(names = "-@")
void setDefaultProviderPath(File path) {
// you could do some validation here:
if (!path.canRead()) {
String msg = String.format("ERROR: file not found: %s", path);
throw new ParameterException(spec.commandLine(), msg);
}
// only set the system property if the file exists
System.setProperty("picocli.defaults.myapp.path", path.toString());
}
}