【问题标题】:In Java, is there a way to obtain the command line parameters even if main() didn't save them?在 Java 中,有没有办法获取命令行参数,即使 main() 没有保存它们?
【发布时间】:2010-11-06 17:01:56
【问题描述】:

我们有一个带有 main() 的程序,它解析某些 CLP,但不会将它们保存在任何地方。 然后,我有自己的插件代码,需要访问原始 CLP(因此我可以传输更多参数)。但是,我无法更改 main()

I saw that there is apparently a way to do this in C#,我正在寻找 Linux 上的等效 Java 解决方案。

更新:显然,我知道 main() 是如何工作的。不幸的是,我无法更改现有应用程序或调用它的方式(CLP 除外)。我只能通过沙盒插件代码访问。我的问题是是否有办法获取调用 JVM 的命令行(而不是带有 -D 的环境变量)。

【问题讨论】:

  • 我在 java.lang.Runtime 或 System 中看不到任何内容。
  • 我猜想.NET 使用操作系统特定的方法来检索当前进程的 CLP。

标签: java command-line-arguments


【解决方案1】:

除了以某种方式在 main 中执行此操作外,我认为您唯一的其他选择是降到操作系统级别并执行一些命令来获取参数。

在 linux 上,正在运行的进程的命令行参数存储在 /proc/pid/cmdline

因此,要获得它们,您必须找到进程 ID。见这里:

How can a Java program get its own process ID?

然后使用这个打开的 /proc/pid/cmdline 并解析它。此文件的格式和 c 中的示例在这里:

http://www.unix.com/unix-advanced-expert-users/86740-retrieving-command-line-arguments-particular-pid.html

最好将这两个调用封装在一个从 java 调用的 shell 脚本中。

请注意,这将是非常不可移植的并且有点hacky。但如果需要必须...

【讨论】:

  • 请注意,此解决方案受到限制,因为 Linux 会截断它保存的命令行。 (我相信它是可配置的,但默认为几 K。)由于 Java 命令行由于包含类路径而通常很长,因此很容易达到这个限制。
【解决方案2】:

一旦您意识到 Java 的 main 方法只是另一个以字符串数组作为参数的静态方法,解决方案就很简单了。

创建一个存储 CLP 的新类,然后调用旧类。稍后,您可以使用新类访问您的 CLP:

import NotToBeChangedMainClass;

public MyMainClass {
  public static final String[] ARGUMENTS;
  public static void main(String ... args) {
    ARGUMENTS = args;
    NotToBeChangedMainClass.main(args);
  }

}

最后,将任何外部调用者(例如任何批处理文件)更改为使用 MyMainClass 而不是 NotToBeChangedMainClass。如果您使用的是可运行的 jar 或类似的东西,则需要更改相应的配置文件。

【讨论】:

  • 显然,我知道 main() 是如何工作的。不幸的是,我无法更改现有应用程序或调用它的方式(CLP 除外)。我只能通过沙盒插件代码访问。
【解决方案3】:

创建自己的主类。保存参数。打电话给老main

在命令行上使用System.getProperty-Dkey=value 可能更容易(在主类名或-jar 之前)。

【讨论】:

  • 不幸的是,我无法更改 main()... 使用 -D 执行此操作也是一个问题,因为我只能控制标准 clps... :(
【解决方案4】:

如果您别无选择,您绝对必须保留所有现有类名及其确切名称(as stated in your comment 我之前的回答),那么您必须使用 AspectJ。

假设我们有这个类:

public class UnmodifyableClassWithMain {
  public static void main(String[] args) {
    System.out.println("In main");
    new ClassUsingArgumentRegistry();
  }
}

首先,您需要保存命令行参数的东西。为简单起见,我将使用带有静态字段的简单类:

public class ArgumentRegistry {
  public static String[] ARGS;
}

然后,您需要定义一个 Aspect 来拦截对 main 的调用并存储参数。

public aspect StoreArgumentsOfMain {

  /**
   * This pointcut intercepts all calls to methods called main with a string array as
   * argument.
   */
  pointcut mainMethod(String[] arguments): execution(void main(String[])) && args(arguments);

  /**
   * Before the original main method gets called, store the arguments in the registry.
   */
  before(String[] arguments): mainMethod(arguments) {
    System.out.println("Storing arguments");
    ArgumentRegistry.ARGS = arguments;
  }

}

为了试用,我还创建了一个 ClassUsingArgumentRegistry:

public class ClassUsingArgumentRegistry {

  public ClassUsingArgumentRegistry() {
    System.out.println("Arguments: " + java.util.Arrays.toString(ArgumentRegistry.ARGS));
  }

}

就是这样。如果我启用 AspectJ 的编译时编织并使用“java UnmodifyableClassWithMain Foo Bar Baz”运行结果,我会得到以下输出:

Storing arguments
In main
Arguments: [foo, bar, baz]

【讨论】:

    【解决方案5】:

    请注意,此解决方案非常有限,因为 Linux 会截断它保存的命令行。由于 Java 命令行通常有很长的类路径,这是一个非常现实的问题。

    这是实现 Pablojim 给出的答案的 Java 代码。

    package test;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class Test {
      public static List<String> getLinuxCmdline(int maxBytesToRead) throws IOException {
        List<String> result = new ArrayList<>();
        String pid = new File("/proc/self").getCanonicalFile().getName();
        File cmdlineFile = new File("/proc/" + pid + "/cmdline");
        final int growBy = 1024;
        try (FileInputStream is = new FileInputStream(cmdlineFile);) {
          byte[] data = new byte[Math.min(growBy, maxBytesToRead)];
          int totalRead = 0; 
          while (totalRead < maxBytesToRead) {
            int read = is.read(data, totalRead, data.length - totalRead);
            if (read > 0) {
              totalRead += read;
              if (data.length == totalRead) {
                data = Arrays.copyOf(data, Math.min(data.length + growBy, maxBytesToRead));
              }
            } else {
              break;
            }
          }
          int start = 0;
          int scan = 0;
          while (scan < totalRead) {
            if (data[scan] == 0) {
              result.add(new String(Arrays.copyOfRange(data, start, scan)));
              start = scan + 1;
            }
            scan++;
          }
          if (scan - start > 0) result.add(new String(Arrays.copyOfRange(data, start, scan)));        }
        return result;
      }
    
      public static void main(String[] args) throws IOException {
        System.out.println(getLinuxCmdline(Integer.MAX_VALUE));
      }
    }
    

    在 Eclipse 中使用参数“foo bar”运行它会为我提供:

    [/usr/lib/jvm/java-8-oracle/bin/java, -Dfile.encoding=UTF-8, -classpath, /home/mab/workspace/test/bin, test.Test, foo, bar]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-14
      • 2021-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多