【问题标题】:Java's System.getRuntime().exec not behaving as if the shell user called itJava 的 System.getRuntime().exec 的行为不像 shell 用户调用它
【发布时间】:2012-07-04 13:36:39
【问题描述】:

我正在 HP-UX 机器上从控制台运行 java 应用程序。在其中,我生成一些报告,压缩它们,然后通过电子邮件发送它们。一切正常,除了电子邮件。

我正在使用邮件二进制文件从命令行发送邮件。由于它是 HP-UX,它与标准的 GNU sendmail 有点不同。

这是我用来发送邮件的代码:

    public static void EmailReports(String[] recipients, String reportArchive, String subject){
        SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
        String today = dateFormat.format(new Date());

        File tempEmailFile;
        BufferedWriter emailWriter;
        try {
            tempEmailFile = File.createTempFile("report_email_" + today, "msg");
            emailWriter = new BufferedWriter(new FileWriter(tempEmailFile));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Failed to send email. Could not create temporary file.");
            return;
        }

        try {
            emailWriter.write("SUBJECT: " + subject + "\n");
            emailWriter.write("FROM: " + FROM + "\n");
            emailWriter.write(BODY + "\n"); 
            emailWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Failed to send email. Could not write to temporary file.");
        }

        //read the archive in
        try {
            FileInputStream archiveIS = new FileInputStream(new File(reportArchive));
            OutputStream archiveEncoder = MimeUtility.encode(new FileOutputStream(tempEmailFile, true), "uuencode", Zipper.getArchiveName(reportArchive));

            //read archive
            byte[] buffer = new byte[archiveIS.available()];    //these should never be more than a megabyte or two, so storing it in memory is no big deal.
            archiveIS.read(buffer);

            //encode archive
            archiveEncoder.write(buffer);

            //close both
            archiveIS.close();
            archiveEncoder.close();

        } catch (FileNotFoundException e) {
            System.out.println("Failed to send email. Could not find archive to email.");
            e.printStackTrace();
        } catch (MessagingException e) {
            System.out.println("Failed to send email. Could not encode archive.");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Failed to send email. Could not encode archive.");
        }
        System.out.println("Sending '" + subject + "' email.");     

        try {
            Process p = Runtime.getRuntime().exec("mail me@example.com < " + tempEmailFile.getAbsolutePath());
            System.out.println("mail me@example.com < " + tempEmailFile.getAbsolutePath());

            StringBuffer buffer = new StringBuffer();
            while(p.getErrorStream().available() > 0){
                buffer.append((char) p.getErrorStream().read());
            }

            System.out.println("STDERR: " + buffer.toString());

            buffer = new StringBuffer();
            while(p.getInputStream().available() > 0){
                buffer.append((char) p.getInputStream().read());
            }

            System.out.println("STDOUT: " + buffer.toString());
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Failed to send email. Could not get access to the shell.");
        }
    }

当我运行程序并发送电子邮件时,我收到一封空白电子邮件,没有主题、没有正文、没有附件,它来自 HP-UX 框中的 user@hostname,而不是来自FROM.

但是,当我运行它所运行的同一行时(请参阅我调用 exec 后打印的命令),我会从正确的用户那里收到正确的电子邮件,其中包含主题、正文和附件。

STDOUT 和 STDERR 都是空的。这几乎就像我正在发送邮件一个空白文件,但是当我在调用 exec 之前打印文件时,它就在那里。

这是怎么回事?

编辑:尝试:

使用 Ksh:

    try {
        String cmd = "mail me@example.com.com < " + tempEmailFile.getAbsolutePath();            
        Runtime.getRuntime().exec(new String[] {"/usr/bin/ksh", cmd});

    } catch (IOException e) {
        e.printStackTrace();
        System.out.println("Failed to send email. Could not get access to the shell.");
    }

使用标准输入:

    try {
        System.out.println("mail me@example.com < " + tempEmailFile.getAbsolutePath());

        Process p = Runtime.getRuntime().exec("mail me@example.com ");

        FileInputStream inFile = new FileInputStream(tempEmailFile);
        byte[] byteBuffer = new byte[inFile.available()];
        inFile.read(byteBuffer);
        p.getOutputStream().write(byteBuffer);

        inFile.close();
        p.getOutputStream().close();

        StringBuffer buffer = new StringBuffer();
        while(p.getErrorStream().available() > 0){
            buffer.append((char) p.getErrorStream().read());
        }

        System.out.println("STDERR: " + buffer.toString());

        buffer = new StringBuffer();
        while(p.getInputStream().available() > 0){
            buffer.append((char) p.getInputStream().read());
        }

        System.out.println("STDOUT: " + buffer.toString());
    } catch (IOException e) {
        e.printStackTrace();
        System.out.println("Failed to send email. Could not get access to the shell.");
    }

【问题讨论】:

    标签: java sendmail runtime.exec


    【解决方案1】:

    我强烈怀疑问题出在重定向上。这通常由 shell 处理 - 这里没有 shell。

    要么你需要正常执行进程然后获取进程的标准输入流并从Java写入,或者(可能更简单)运行/bin/sh(或其他)让外壳进行重定向。

    【讨论】:

    • 这些都不起作用。甚至没有收到空白电子邮件。 KSH 是正在使用的外壳。
    • @Malfist:好吧,我希望如果操作正确,它们都会起作用。请编辑您的帖子,并举例说明您如何尝试使用 shell。
    • @Malfist:首先,您使用的是available(),并假设将一次性读取整个文件——这总是一个坏主意。对于 ksh 示例,您可能需要传入 -c 以说明其余部分是命令。
    • 什么会导致 available 不返回整个文件?它们的文件通常小于 5 兆字节,因此将整个文件读入内存是安全的。
    • @Malfist:将整个内容读入内存是安全的,但您应该永远假设对read() 的一次调用将读取整个流,或者@ 987654325@ 会告诉你流的总长度。 (我对ByteArrayInputStream 例外,但无论如何都要避免available()。)例如,想象一个文件位于网络文件共享上 - 我可以想象它不会一次性返回所有内容。以这种方式读取文件是一种糟糕的做法 - 有一些非常可靠的方法可以通过循环来完成。
    【解决方案2】:

    尝试执行{ "ksh", "-c", "mail me@example.com &lt; " + etc }-c 选项专门告诉 shell 将下一个参数解析为带有可能重定向等的 shell 命令。如果没有-c,ksh 会遵循启发式方法来决定如何处理其命令行,并且它可能不会按照您希望的方式运行命令。

    【讨论】:

      【解决方案3】:

      分成两行,只是为了更好的可读性:

              String cmd = "mail me@example.com < " + tempEmailFile.getAbsolutePath () ;
              Process p = Runtime.getRuntime().exec (cmd);
      

      这将寻找一个名为"mail me@example.com &lt; " + tempEmailFile.getAbsolutePath () 的程序。它不会进行重定向 - 为此,您必须自己阅读该过程的输出。

      此外,它不会查找路径,因此您可能必须指定整个路径 /usr/bin/mail 或其他任何路径。

      而且你必须拆分命令和参数;改用字符串数组: ("/path/to/prg", "param1", "param2", "foo=bar");

      如果您将脚本作为程序调用,则可以使用重定向,例如

      String cmd = "/usr/bin/mail me@example.com < " + tempEmailFile.getAbsolutePath () ;
      String cmdarr = new String [] {"/bin/bash", "-c", cmd}; 
      Process p = Runtime.getRuntime().exec (cmdarr);
      

      它比你自己从 Java 调用文件重定向更短,更简单,但你失去了对不同错误做出明智反应的能力。

      【讨论】:

      • HP-UX 使用 ksh,是否需要添加额外的参数?
      • @Malfist:“没用”有点含糊。我忘了再次添加邮件的绝对路径。更新中... - 可能需要“-c”将命令作为字符串传递,至少对于 bash。请参阅 ksh 手册如何使用脚本作为字符串调用 ksh 二进制文件。
      猜你喜欢
      • 2012-08-13
      • 2016-10-30
      • 2019-09-21
      • 2014-07-03
      • 1970-01-01
      • 2010-09-21
      • 2020-07-09
      • 2011-01-07
      • 2021-09-14
      相关资源
      最近更新 更多