Java - 从终端运行 jar 可以工作,但双击会中断功能

Posted

技术标签:

【中文标题】Java - 从终端运行 jar 可以工作,但双击会中断功能【英文标题】:Java - Running jar from terminal works but double clicking breaks functionality 【发布时间】:2017-02-05 09:37:13 【问题描述】:

我正在尝试通过 JavaFX 制作的 GUI 启动命令行应用程序。 当我在我的 IDE 中运行该程序时,一切正常,但是当我尝试通过双击编译的 .jar 文件来运行它时,它就不行了。

我发现这可能是因为 PATH 或 Classpath 或工作目录或类似的东西与您从终端运行时不同。

public Button startButton;
public Button clearButton;
public Button minButton;
public Button plusButton;
public Button clearFieldButton;
public Button clearLogButton;
public TextField linkField;
public TextField qualField;
public TextArea errorField;
public ListView<String> historyField;

private String os;

public void initialize()
    os = System.getProperty("os.name");
    linkField.setText(null);
    qualField.setText(null);
    getHistory();
    setButtonActions();
    Runnable run = new Runnable() 
        @Override
        public void run() 
            linkField.requestFocus();
        
    ;
    Platform.runLater(run);


private void setButtonActions()
    historyField.setOnMouseClicked(new EventHandler<MouseEvent>() 
        @Override
        public void handle(MouseEvent mouseEvent) 
            if(mouseEvent.getClickCount() == 2)
                String qual;
                if(qualField.getText() == null)
                    qual = "best";
                
                else
                    qual = qualField.getText();
                
                startStream(historyField.getSelectionModel().getSelectedItem(), qual);
            
        
    );
    clearButton.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent actionEvent) 
            clearHistory();
        
    );
    minButton.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent actionEvent) 
            delLast();
        
    );
    plusButton.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent actionEvent) 
            writeHistory(linkField.getText());
        
    );
    startButton.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent actionEvent) 
            startStream(linkField.getText(),qualField.getText());
        
    );
    clearFieldButton.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent actionEvent) 
            linkField.clear();
            qualField.clear();
        
    );
    clearLogButton.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent actionEvent) 
            errorField.clear();
        
    );


private void delLast()
    LinkedList<String> temp = new LinkedList<>();
    try(BufferedReader br = new BufferedReader(new FileReader("history.txt")))
        String line;
        while((line = br.readLine()) != null)
            temp.add(line);
        
        temp.removeLast();
        clearHistory();
        for (String s : temp) 
            writeHistory(s);
        
     catch (IOException e) 
        e.printStackTrace();
    


private void clearHistory()
    try(BufferedWriter br = new BufferedWriter(new FileWriter("history.txt")))
        br.write("");
        getHistory();
     catch (IOException e) 
        e.printStackTrace();
    


private void getHistory()
    if(!new File("history.txt").exists())
        try(BufferedWriter wr = new BufferedWriter(new FileWriter("history.txt"))) 
            wr.write("");
         catch (IOException e) 
            e.printStackTrace();
        
    
    else
        historyField.getItems().clear();
        ArrayList<String> history = new ArrayList<>();
        try(BufferedReader br = new BufferedReader(new FileReader("history.txt")))
            String line;
            while((line = br.readLine()) != null)
                history.add(line);
            
         catch (IOException e) 
            e.printStackTrace();
        
        historyField.getItems().addAll(history);
    


private boolean checkIfInFile(String link)
    try(BufferedReader br = new BufferedReader(new FileReader("history.txt")))
        String line;
        while ((line = br.readLine()) != null)
            if(line.equals(link))
                return true;
            
        
        return false;
     catch (IOException e) 
        e.printStackTrace();
    
    return false;


private void writeHistory(String link)
    if(link != null)
        try(PrintWriter print = new PrintWriter(new FileWriter("history.txt", true))) 
            print.append(link).append("\n");
         catch (IOException e) 
            e.printStackTrace();
        
        getHistory();
    


private void startStream(String link, String quality)
    errorField.setText("");
    String command = "";
    if(!checkIfInFile(link))
        writeHistory(link);
    
    if(!os.contains("win"))
        ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", "/usr/bin/which streamlink");
        Map<String, String> env = pb.environment();
        env.put("PATH",System.getenv("PATH"));
        errorField.appendText(env.get("PATH") + "\n");
        try(BufferedReader br = new BufferedReader(new InputStreamReader(pb.start().getInputStream()))) 
            command = br.readLine();
            errorField.appendText(command);
         catch (IOException e) 
            e.printStackTrace();
        
        if(quality == null)
            command = command + " " + link;
        
        else if(link != null)
            command = command + " " + link + " " + quality;
        
    
    else
        if(link == null)
            command = "streamlink";
        
        else if(quality == null)
            command = "streamlink " + link;
        
        else
            command = "streamlink " + link + " " + quality;
        
    
    try 
        ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", command);
        Process proc = pb.start();
        final InputStream in = proc.getInputStream();
        Thread outputThread = new Thread(new Runnable() 
            @Override
            public void run() 
                try(BufferedReader br = new BufferedReader(new InputStreamReader(in)))
                    String read;
                    while((read = br.readLine()) != null)
                        errorField.appendText(read + "\n");
                    
                 catch (IOException e) 
                    e.printStackTrace();
                
            
        );
        outputThread.start();
     catch (IOException e) 
        e.printStackTrace();
    

任何帮助将不胜感激。

编辑:忘了说双击 Windows 效果很好。

编辑 2:**我刚刚发现“真正的”问题是什么。如果您通过终端或 bash 脚本运行,这是 echo $PATH: '/Users/Vermeulen/.jenv/shims:/opt/local/bin:/opt/local/sbin‌​:/Library/Frameworks‌​/ Python.framework/Ve‌​rsions/3.5/bin:/usr/‌​local/bin:/usr/bin:/‌​bin:/usr/sbin:/sbin:‌​/usr/local/bin:/opt /‌​X11/bin:/usr/X11/bi' 当你双击 jar 时 echo $PATH 的结果是:'/usr/bin:/bin:/usr/sbin:/sbin' **

【问题讨论】:

一般情况下,您应该始终为 Java 程序提供 .bat/.sh 启动脚本。 我想我必须这样做,因为我不知道如何解决 $PATH 问题。谢谢你的回答。 【参考方案1】:

你能分享一下 jar 中的 Manifest.mf 文件吗?里面应该有正确设置的主类。 please check this

其次是check this as well。我希望 jdk 已经在属性选项卡中正确设置。

【讨论】:

【参考方案2】:

在我的 linux 机器上双击使用我的主目录作为工作目录。因此,如果您尝试查找一些与您的 jar 文件相关的文件,它将失败。

要解决此问题,您不能使用包含

的 shell 脚本启动 jar
java -jar myjarfile.jar

您应该能够双击 shell 脚本(取决于您的 linux 发行版)

如果你想通过代码解决问题,试试这个 sn-p:

CodeSource src = Main.class.getProtectionDomain().getCodeSource();
File pathToJar = new File(src.getLocation().toURI());
System.out.println(pathToJar.getAbsolutePath());

它返回主类文件的目录或 jar 文件的完整路径。取决于您如何启动程序。 使用此信息查找您的文件。

---------------- 更新 1

你没有准确描述你想要做什么。 如果要为外部命令设置 PATH 等环境变量,请使用 ProcessBuilder。

ProcessBuilder builder = new ProcessBuilder("myprogramm", "firstArg", "secondArg");
Map<String, String> environment = builder.environment();
environment.put("PATH", environment.get("PATH") + ":/my/path");
Process process = builder.start();
OutputStream outputStream = process.getOutputStream();
process.waitFor();

更多信息请阅读ProcessBuilder。

【讨论】:

感谢您的回答,但我真的不知道如何使用此信息来解决我的问题。抱歉:/ 我刚刚发现“真正的”问题是什么。如果您通过终端或 bash 脚本运行,这是 echo $PATH: '/Users/Vermeulen/.jenv/shims:/opt/local/bin:/opt/local/sbin:/Library/Frameworks/Python 的结果。框架/版本/3.5/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/X11/ bi' 当你双击 jar 时 echo $PATH 的结果是:'/usr/bin:/bin:/usr/sbin:/sbin' 感谢您提供的额外信息,但问题是,我想运行安装在 Linux 和 OS X 上不同位置的命令行程序。我想执行“which”命令找到它,但是双击时安装路径不在PATH变量中,但是在通过控制台运行jar时。双击时我希望 PATH 变量与通过控制台运行时相同。我将编辑我的帖子以显示程序的整个代码。 我想 which 除了在 PATH 变量中搜索每个目录之外什么都不做。如果您的系统因为 PATH 中缺少程序目录而找不到程序,which 也可以找到它。可以尝试使用其他 shell 而不是 /bin/bash 来执行您的程序。 /bin/sh 例如

以上是关于Java - 从终端运行 jar 可以工作,但双击会中断功能的主要内容,如果未能解决你的问题,请参考以下文章

.Net/C#·运行报错缺少XXX文件,但双击无法跳转缺少位置

java -jar在Linux下运行问题

我用eclipse导出的runnable jar不能双击执行,但在命令行可以用java -jar ***.jar运行,这是怎么回事?

如何通过双击 Windows 7 64 位来运行 .jar 文件?

java打包成jar文件后还是不能双击运行

runnable jar双击打开,提示,A java exception has occured;但通过 DOS下 java -jar *.jar运行成功