java调用cmd&sh命令

发布于:2024-12-06 ⋅ 阅读:(221) ⋅ 点赞:(0)

Java调用Windows命令

Java调用CMD命令

Java调用CMD命令,轻松实现系统级操作!

java如何执行bat脚本,并监控执行结果

Java中如何执行多条shell/bat命令

Java 执行Shell脚本指令

java调用shell脚本并传递参数

java调用shell并传参

java执行shell/CMD命令

Java调用CMD命令

在Java开发中,有时候需要与操作系统的命令行交互,执行特定的CMD命令或脚本。然而,处理编码问题是调用CMD命令时常遇到的挑战。正确处理编码可以避免输出结果的乱码,确保程序的稳定运行。本文将介绍Java中调用CMD命令的方法,并重点解决编码问题,同时提供一些实用技巧和注意事项。

一、Java调用CMD命令的基本方法

Java通过Runtime类或ProcessBuilder类提供了调用CMD命令的能力。这两个类都允许Java程序启动一个新的进程并执行特定的命令,通过标准输入输出流进行数据交换。Runtime类适用于简单的命令执行,ProcessBuilder类则更加灵活,适用于复杂的命令和参数传递。

二、使用Runtime类调用CMD命令

Runtime类提供了exec()方法来执行CMD命令。该方法接收一个String类型的命令字符串,并返回一个Process对象,通过这个对象可以获取命令执行的结果。

示例代码:

import java.io.*;

public class CmdExecutionExample {
    public static void main(String[] args) {
        try {
            // 调用CMD命令
            String command = "ipconfig";
            Process process = Runtime.getRuntime().exec(command);

            // 获取命令输出结果
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "GBK")); // 设置编码为GBK
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待命令执行完成
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

三、使用ProcessBuilder类调用CMD命令

ProcessBuilder类相比Runtime类更加灵活,可以通过链式调用设置命令和参数,同时还可以设置工作目录、环境变量等。

示例代码:

import java.io.*;

public class CmdExecutionExample2 {
    public static void main(String[] args) {
        try {
            // 调用CMD命令
            ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", "ipconfig"); // /c参数表示执行后关闭CMD窗口
            processBuilder.redirectErrorStream(true); // 将错误输出流与标准输出流合并
            Process process = processBuilder.start();

            // 获取命令输出结果
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "GBK")); // 设置编码为GBK
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待命令执行完成
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

四、解决编码问题的注意事项

  • 输出编码:在调用CMD命令时,应确保输出流的编码与CMD命令输出的编码一致。例如,如果CMD命令输出使用GBK编码,Java程序应使用相同的编码读取输出流。
  • 输入编码:如果Java程序需要传递参数给CMD命令,应该确认参数的编码与CMD命令支持的编码一致,以避免参数乱码。

五、Java调用CMD命令的实用技巧

  • 在Windows系统中,CMD命令通常使用GBK编码输出。因此,在调用CMD命令时,常用的编码为GBK。
  • 使用ProcessBuilder类时,可以通过redirectErrorStream(true)方法将错误输出流与标准输出流合并,方便获取完整的输出信息。
  • 建议使用try-with-resources语句来关闭流资源,确保资源的及时释放。

Java调用CMD命令是实现与操作系统交互的一种重要方式。解决编码问题是调用CMD命令时必须关注的重要问题,通过设置正确的编码,可以避免输出结果的乱码,确保程序的稳定运行。在实际应用中,合理使用Runtime类或ProcessBuilder类,处理编码问题,可以实现更灵活、稳健的CMD命令调用。

Java调用CMD命令,轻松实现系统级操作!

在Java中,你可以使用Runtime.getRuntime().exec()方法来调用系统命令行(cmd)命令。这个方法允许你执行任何外部程序或命令,并可以获取其输出。

下面是一个示例代码,展示如何在Java中调用cmd命令:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class CmdExample {
    public static void main(String[] args) {
        try {
            // 构建命令
            String command = "cmd /c dir"; // 这里以列出当前目录为例
            // 执行命令
            Process process = Runtime.getRuntime().exec(command);
            
            // 读取命令的输出
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            
            // 等待命令执行完成
            int exitCode = process.waitFor();
            System.out.println("Exited with code: " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

详解

  1. 构建命令

    • String command = "cmd /c dir";:这里我们使用了Windows的cmd命令行工具,并通过/c参数告诉它执行完命令后关闭。dir是列出当前目录下的文件和文件夹的命令。
  2. 执行命令

    • Process process = Runtime.getRuntime().exec(command);:这行代码通过运行时环境执行指定的命令。返回的Process对象可以用来控制进程和获取其输出。
  3. 读取命令的输出

    • 使用BufferedReader包装InputStreamReader来读取进程的标准输出流(即命令的输出)。
    • 循环读取每一行直到没有更多输出。
  4. 等待命令执行完成

    • int exitCode = process.waitFor();:这会阻塞当前线程直到外部进程结束,并返回进程的退出值。通常,0表示正常退出,非0值表示有错误发生。
  5. 异常处理

    • 捕获并处理IOException(输入输出异常)和InterruptedException(中断异常),这些可能在执行命令或等待过程中抛出。

注意事项

  • 确保传递给exec()方法的命令字符串是正确的,并且考虑到操作系统的差异(例如,上述代码在Windows上运行,而在Linux上可能需要修改命令)。
  • 当处理外部进程时,始终考虑安全性,特别是当命令包含来自用户输入的部分时。
  • 对于更复杂的交互,可能需要使用ProcessBuilder类而不是Runtime.exec(),因为ProcessBuilder提供了更多的配置选项和更好的控制。

Java调用Windows命令

Java调用Windows命令 Java调用Windows命令主要用到两个类: java.lang.Runtime

每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。应用程序不能创建自己的 Runtime 类实例。 java.lang.Process

ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获取相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。 对于带有 Process 对象的 Java 进程,没有必要异步或并发执行由 Process 对象表示的进程。

package test;

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * Java调用Windows命令测试
 * @author liuyazhuang
 *
 */
public class Test {
    public static void main(String args[]) {
        testWinCmd();
        dirOpt();
    }

    public static void testWinCmd() {
        System.out.println("------------------testWinCmd()--------------------");
        Runtime runtime = Runtime.getRuntime();
        System.out.println(runtime.totalMemory());
        System.out.println(runtime.freeMemory());
        System.out.println(runtime.maxMemory());
        System.out.println(runtime.availableProcessors());   //处理器数
        try {
            //执行一个exe文件
            runtime.exec("notepad");
            runtime.exec("C:\\Program Files\\Microsoft Office\\OFFICE11\\winword.exe c:\\test.doc");
            //执行批处理
            runtime.exec("c:\\x.bat");
            //执行系统命令
            runtime.exec("cmd /c dir ");
            runtime.exec("cmd /c dir c:\\");

//            //-------------- 文件操作 --------------

            runtime.exec("cmd /c copy c:\\x.bat d:\\x.txt");   //copy并改名
            runtime.exec("cmd /c rename d:\\x.txt x.txt.bak"); //重命名
            runtime.exec("cmd /c move d:\\x.txt.bak c:\\");    //移动
            runtime.exec("cmd /c del c:\\x.txt.bak");          //删除

            //-------------- 目录操作 --------------
            runtime.exec("cmd /c md c:\\_test");          //删除
        } catch (IOException e) {
            e.printStackTrace();
        }
    } 
    /**
     * 执行批处理文件,并获取输出流重新输出到控制台
     */
    public static void dirOpt() {
        System.out.println("------------------dirOpt()--------------------");
        Process process;
        try {
            //执行命令
            process = Runtime.getRuntime().exec("c:\\x.bat");
            //取得命令结果的输出流
            InputStream fis = process.getInputStream();
            //用一个读输出流类去读
            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
            String line = null;
            //逐行读取输出到控制台
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Java ProcessBuilder examples

In Java, we can use ProcessBuilder to call external commands easily :

ProcessBuilder processBuilder = new ProcessBuilder();

// -- Linux --

// Run a shell command
processBuilder.command("bash", "-c", "ls /home/mkyong/");

// Run a shell script
processBuilder.command("path/to/hello.sh");

// -- Windows --

// Run a command
processBuilder.command("cmd.exe", "/c", "dir C:\\Users\\mkyong");

// Run a bat file
processBuilder.command("C:\\Users\\mkyong\\hello.bat");

Process process = processBuilder.start();

1. Ping

1.1 Run an external ping command to ping a website 3 times, and display the output.

package com.mkyong.process;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ProcessBuilderExample1 {

    public static void main(String[] args) {

        ProcessBuilder processBuilder = new ProcessBuilder();

        // Run this on Windows, cmd, /c = terminate after this run
        processBuilder.command("cmd.exe", "/c", "ping -n 3 google.com");

        try {

            Process process = processBuilder.start();

			// blocked :(
            BufferedReader reader =
                    new BufferedReader(new InputStreamReader(process.getInputStream()));

            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            int exitCode = process.waitFor();
            System.out.println("\nExited with error code : " + exitCode);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

Output

Pinging google.com [172.217.166.142] with 32 bytes of data:
Reply from 172.217.166.142: bytes=32 time=10ms TTL=55
Reply from 172.217.166.142: bytes=32 time=10ms TTL=55
Reply from 172.217.166.142: bytes=32 time=10ms TTL=55

Ping statistics for 172.217.166.142:
    Packets: Sent = 3, Received = 3, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 10ms, Maximum = 10ms, Average = 10ms

Exited with error code : 0

2. Ping + Thread

In above example 1.1, the process.getInputStream is “blocking”, it is better to start a new Thread for the reading process, so that it won’t block other tasks.

package com.mkyong.process;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

public class ProcessBuilderExample2 {

    public static void main(String[] args) {

        ExecutorService pool = Executors.newSingleThreadExecutor();

        ProcessBuilder processBuilder = new ProcessBuilder();

        // Run this on Windows, cmd, /c = terminate after this run
        processBuilder.command("cmd.exe", "/c", "ping -n 3 google.com");

        try {

            Process process = processBuilder.start();

            System.out.println("process ping...");
            ProcessReadTask task = new ProcessReadTask(process.getInputStream());
            Future<list<string>> future = pool.submit(task);

            // no block, can do other tasks here
            System.out.println("process task1...");
            System.out.println("process task2...");

            List<string> result = future.get(5, TimeUnit.SECONDS);
            for (String s : result) {
                System.out.println(s);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
    }

    private static class ProcessReadTask implements Callable<list<string>> {

        private InputStream inputStream;

        public ProcessReadTask(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        @Override
        public List<string> call() {
            return new BufferedReader(new InputStreamReader(inputStream))
				.lines()
				.collect(Collectors.toList());
        }
    }

}

Output

process ping...
process task1...
process task2...

Pinging google.com [172.217.166.142] with 32 bytes of data:
Reply from 172.217.166.142: bytes=32 time=11ms TTL=55
Reply from 172.217.166.142: bytes=32 time=10ms TTL=55
Reply from 172.217.166.142: bytes=32 time=10ms TTL=55

Ping statistics for 172.217.166.142:
    Packets: Sent = 3, Received = 3, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 10ms, Maximum = 11ms, Average = 10ms

3. Change Directory

3.1 Change to directory C:\\users and run external dir command to list out all the files.

package com.mkyong.process;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

public class ProcessBuilderExample3 {

    public static void main(String[] args) {

        ProcessBuilder processBuilder = new ProcessBuilder();

        processBuilder.command("cmd.exe", "/c", "dir");
        processBuilder.directory(new File("C:\\users"));

        // can also run the java file like this
        // processBuilder.command("java", "Hello");

        try {

            Process process = processBuilder.start();

            BufferedReader reader =
                    new BufferedReader(new InputStreamReader(process.getInputStream()));

            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            int exitCode = process.waitFor();
            System.out.println("\nExited with error code : " + exitCode);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

Output

 Volume in drive C has no label.
 Volume Serial Number is CE5B-B4C5

 Directory of C:\users

//...

Java 执行Shell脚本指令

一、介绍

有时候我们在Linux中运行Java程序时,需要调用一些Shell命令和脚本。而Runtime.getRuntime().exec()方法给我们提供了这个功能,而且Runtime.getRuntime()给我们提供了以下几种exec()方法:

Process exec(String command)   
在单独的进程中执行指定的字符串命令。   
  
Process exec(String[] cmdarray)   
在单独的进程中执行指定命令和变量。   
  
Process exec(String[] cmdarray, String[] envp)   
在指定环境的独立进程中执行指定命令和变量。   
  
Process exec(String[] cmdarray, String[] envp, File dir)   
在指定环境和工作目录的独立进程中执行指定的命令和变量。   
  
Process exec(String command, String[] envp)   
在指定环境的单独进程中执行指定的字符串命令。   
  
Process exec(String command, String[] envp, File dir)   
在有指定环境和工作目录的独立进程中执行指定的字符串命令。

其中,其实cmdarray和command差不多,同时如果参数中如果没有envp参数或设为null,表示调用命令将在当前程序执行的环境中执行;如果没有dir参数或设为null,表示调用命令将在当前程序执行的目录中执行,因此调用到其他目录中的文件和脚本最好使用绝对路径。各个参数的含义:

  1. cmdarray: 包含所调用命令及其参数的数组。
  2. command: 一条指定的系统命令。
  3. envp: 字符串数组,其中每个元素的环境变量的设置格式为name=value;如果子进程应该继承当前进程的环境,则该参数为 null。
  4. dir: 子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为 null。

细心的读者会发现,为了执行调用操作,JVM会启一个Process,所以我们可以通过调用Process类的以下方法,得知调用操作是否正确执行:

abstract  int waitFor()   
导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。

二、调用Shell脚本

1、获取键盘输入

BufferedReader reader = null;
        try{
            reader \= new BufferedReader(new InputStreamReader(System.in));
            System.out.println("请输入IP:");
            String ip \= reader.readLine();

上述指令基本很常见:

1、创建读入器:BufferReader

2、将数据流载入BufferReader,即InputStreamReader

3、将系统输入载入InputStreamReader中

4、然后利用reader获取数据。

2、构建指令

shell运行脚本指令为 sh **.sh args,其实这个格式与java格式相同。

我的脚本为:

#!/bin/sh
#根据进程名杀死进程
echo "This is a $call"
if [ $# -lt 2 ]
then
   echo "缺少参数:procedure_name和ip"
   exit 1
fi

echo "Kill the $1 process"
PROCESS=`ps -ef|grep $1|grep $2|grep -v grep|grep -v PPID|awk '{ print $2}'`
for i in $PROCESS
do
   echo "Kill the $1 process [ $i ]" 
done

其实所有准备若当,就是无法读取里面的数据,执行shell指令,原因就是:

注意事项:

1.shell脚本必须有执行权限,比如部署后chmod -R 777 /webapps

2.shell文件,必须是UNIX格式,ANSI编码格式,否则容易出问题(可以用notepad++,编辑->文档格式转换,格式->转为ANSI格式(UNIX格式)

3、java程序

/**
 * @author :dongbl
 * @version :
 * @Description:
 * @date :9:19 2017/11/14
 */
public class TestBash {
    public static void main(String [] args){
        BufferedReader reader = null;
        try{
            reader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("请输入IP:");
            String ip = reader.readLine();
            String bashCommand = "sh "+ "/usr/local/java/jdk1.8.0_121/lib/stopffmpeg.sh" + " ffmpeg " + ip;
//            String bashCommand = "chmod 777 " + "/usr/local/java/jdk1.8.0_121/lib/stopffmpeg.sh" ;
//            String bashCommand = "kill -9" + ip;
            System.out.println(bashCommand);
            Runtime runtime = Runtime.getRuntime();
            Process pro = runtime.exec(bashCommand);
            int status = pro.waitFor();
            if (status != 0)
            {
                System.out.println("Failed to call shell's command ");
            }

            BufferedReader br = new BufferedReader(new InputStreamReader(pro.getInputStream()));
            StringBuffer strbr = new StringBuffer();
            String line;            
            while ((line = br.readLine())!= null)
            {                
                strbr.append(line).append("\n");
            }

            String result = strbr.toString();
            System.out.println(result);

        }
        catch (IOException ec)
        {
            ec.printStackTrace();
        }
        catch (InterruptedException ex){
            ex.printStackTrace();

        }
    }
}

其中文件路径为绝对路径,这点需要注意。

三、调试心得

在调试shell脚本执行过程中,反了几个错

1、构建指令不对

1、开始指令为:

 String bashCommand = "/usr/local/java/jdk1.8.0\_121/lib/stopffmpeg.sh "+ " ffmpeg " + ip;
程序报错,权限不足。
2、此时修改为:
String bashCommand = "chmod 777 "+"/usr/local/java/jdk1.8.0\_121/lib/stopffmpeg.sh "+ " ffmpeg " + ip;

记住一定注意空格(格式)但此时依然无法指定脚本指令。3、在发现格式不对后,修改后,还是不行,此时指令为:

String bashCommand = "sh "+ "/usr/local/java/jdk1.8.0\_121/lib/stopffmpeg.sh" + " ffmpeg " + ip;

2、文件格式不对

这是无法读取内容的关键原因,因为sh文件是在Windows系统下生成的,所以需要将格式修改为linux格式的,即(UNIX格式)

此时网上程序可以了,然后看到指令不同,修改指令即可以了。

最后终于调通了,看来格式经验不足啊

四、参考文献

1、java调用shell脚本并传递参数

2、JAVA调用Shell脚本

转载于:https://www.cnblogs.com/polly333/p/7832540.html

本文转自 https://blog.csdn.net/weixin_30892889/article/details/97217607,如有侵权,请联系删除。

通义灵码

Process执行命令如何结束

在 Java 中使用 Process 执行外部命令时,可以通过以下几种方法来结束进程:
1、调用 Process.destroy() 方法

  • 这个方法会强制终止进程。适用于需要立即停止进程的情况。

2、调用 Process.destroyForcibly() 方法:

  • 这个方法会尝试更加强制地终止进程。通常在 destroy() 方法无效时使用。

3、等待进程自然结束: 使用 Process.waitFor() 方法等待进程自然结束。

  • 这个方法会阻塞当前线程,直到进程结束。

4、读取进程的输出流和错误流:

  • 确保进程的输出流和错误流被正确读取,否则进程可能会因为缓冲区满而挂起。
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ProcessExample {

    public static void main(String[] args) {
        try {
            // 创建进程
            Process process = Runtime.getRuntime().exec("your-command-here");

            // 读取进程的输出流和错误流
            StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT");
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR");

            outputGobbler.start();
            errorGobbler.start();

            // 等待进程结束
            int exitCode = process.waitFor();
            System.out.println("Process exited with code: " + exitCode);

            // 如果需要强制终止进程
            // process.destroy();
            // process.destroyForcibly();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class StreamGobbler extends Thread {

    private InputStream inputStream;
    private String type;

    public StreamGobbler(InputStream inputStream, String type) {
        this.inputStream = inputStream;
        this.type = type;
    }

    @Override
    public void run() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(type + ": " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Process执行命令如何正确关闭

在 Java 中使用 Process 执行外部命令时,正确关闭进程非常重要,以确保资源被释放并且不会留下僵尸进程。以下是一些最佳实践和步骤,确保 Process 被正确关闭:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class ShellUtils {

    public static void executeCommand(String command) {
        Process process = null;
        try {
            // 创建进程
            process = Runtime.getRuntime().exec(command);

            // 读取进程的输出流和错误流
            StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT");
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR");

            outputGobbler.start();
            errorGobbler.start();

            // 等待进程结束
            int exitCode = process.waitFor();
            System.out.println("Process exited with code: " + exitCode);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 尝试终止进程
            if (process != null) {
                if (process.isAlive()) {
                    process.destroy();
                    if (process.isAlive()) {
                        process.destroyForcibly();
                    }
                }

                // 如果仍然无法终止,使用操作系统的命令
                if (process.isAlive()) {
                    terminateProcessGroup(process);
                }
            }
        }
    }

    private static void terminateProcessGroup(Process process) {
        try {
            // 获取进程的 PID
            String pid = getProcessId(process);
            if (pid != null) {
                // 终止进程组
                String osName = System.getProperty("os.name").toLowerCase();
                if (osName.contains("windows")) {
                    // Windows 系统
                    Runtime.getRuntime().exec("taskkill /F /T /PID " + pid);
                } else {
                    // Unix 系统
                    Runtime.getRuntime().exec("kill -9 -" + pid);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String getProcessId(Process process) {
        try {
            Field field = process.getClass().getDeclaredField("pid");
            field.setAccessible(true);
            return field.get(process).toString();
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}

class StreamGobbler extends Thread {

    private InputStream inputStream;
    private String type;

    public StreamGobbler(InputStream inputStream, String type) {
        this.inputStream = inputStream;
        this.type = type;
    }

    @Override
    public void run() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(type + ": " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

cmd /c 的含义

cmd /c 是 Windows 命令行中的一个选项,用于执行指定的命令并立即返回控制权。具体来说:

  • cmd:启动 Windows 命令解释器(命令提示符)。
  • /c:表示执行指定的命令后立即退出命令提示符。

详细解释

  1. cmd

    • 启动 Windows 命令提示符(Command Prompt)。
    • 这是一个命令行解释器,用于执行各种命令和脚本。
  2. /c

    • 指定命令提示符在执行完指定的命令后立即退出。
    • 语法格式为 cmd /c <command>,其中 <command> 是你希望执行的具体命令。

示例

假设你有一个命令 dir,用于列出当前目录下的文件和文件夹。你可以使用 cmd /c dir 来执行这个命令:

cmd /c dir

这条命令会启动命令提示符,执行 dir 命令,然后立即退出命令提示符。

为什么使用 cmd /c

  1. 命令组合

    • 有时需要组合多个命令或使用命令行的特殊功能(如管道、重定向等),cmd /c 可以帮助实现这一点。
  2. 兼容性

    • 在某些情况下,直接执行某些命令可能会遇到路径或环境变量的问题,使用 cmd /c 可以确保命令在正确的环境中执行。
  3. 立即退出

    • 使用 /c 选项可以确保命令执行完毕后立即退出命令提示符,不会阻塞当前的 Java 进程。

总结

cmd /c 是一个非常有用的命令行选项,特别是在需要执行复杂命令或组合命令时。在你的代码中,它确保 ffmpeg 命令在一个独立的命令提示符窗口中运行,并且在命令执行完毕后立即退出,不会阻塞当前的 Java 进程。

"sh", "-c" 的含义

"sh", "-c" 是在 Unix/Linux 系统中用于启动 shell 并执行指定命令的参数。具体来说:

  • sh:启动 Bourne shell(或兼容的 shell,如 Bash)。
  • -c:表示执行指定的命令字符串,然后退出 shell。

详细解释

  1. sh

    • sh 是 Unix/Linux 系统中的一个命令行解释器,通常指的是 Bourne shell(Bourne shell 的文件通常是 /bin/sh)。
    • 它可以执行脚本文件或命令行中的命令。
  2. -c

    • -csh 的一个选项,表示从命令行参数中读取命令字符串并执行。
    • 语法格式为 sh -c <command>,其中 <command> 是你希望执行的具体命令。

示例

假设你有一个命令 ls,用于列出当前目录下的文件和文件夹。你可以使用 sh -c ls 来执行这个命令:

sh -c "ls"

这条命令会启动一个 shell,执行 ls 命令,然后退出 shell。

在 Java 中的使用

在你的代码中,"sh", "-c" 用于启动 shell 并执行指定的命令。这样可以确保命令在 shell 环境中正确解析和执行,特别是当命令包含特殊字符或需要环境变量时。

String[] commandList = new String[]{"sh", "-c", "your-command-here"};

为什么使用 "sh", "-c"

  1. 命令组合

    • 有时需要组合多个命令或使用 shell 的特殊功能(如管道、重定向等),sh -c 可以帮助实现这一点。
  2. 环境变量

    • 使用 sh -c 可以确保命令在正确的 shell 环境中执行,包括环境变量的设置。
  3. 立即退出

    • 使用 -c 选项可以确保命令执行完毕后立即退出 shell,不会阻塞当前的 Java 进程。

示例代码

以下是一个示例,展示了如何在 Java 中使用 "sh", "-c" 来执行命令:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class CmdExecutionExample2 {

    public static void main(String[] args) {
        String[] commandList = new String[]{"sh", "-c", "ls"};

        try {
            Process process = new ProcessBuilder(commandList).start();

            // 读取进程的输出流和错误流
            StreamGobbler stdout = new StreamGobbler(process.getInputStream(), "stdout");
            StreamGobbler errout = new StreamGobbler(process.getErrorStream(), "errout");
            stdout.start();
            errout.start();

            // 等待进程结束
            int exitCode = process.waitFor();
            System.out.println("退出码: " + exitCode);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class StreamGobbler extends Thread {

        private InputStream inputStream;
        private String type;

        public StreamGobbler(InputStream inputStream, String type) {
            this.inputStream = inputStream;
            this.type = type;
        }

        @Override
        public void run() {
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(type + ": " + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

解释

  1. 创建进程
    使用 ProcessBuilder 创建一个进程,命令列表为 {"sh", "-c", "ls"}

  2. 读取输出流和错误流
    使用 StreamGobbler 类分别读取进程的输出流和错误流,确保缓冲区不会满。

  3. 等待进程结束
    使用 process.waitFor() 等待进程自然结束,并获取退出码。

通过这些步骤,可以确保命令在 shell 环境中正确执行,并且进程的输出和错误流被正确读取。

FFmpegShellUtils

public class FFmpegShellUtils {

    // 测试时可以配合命令: tasklist|findstr "ffmpeg", taskkill /f /pid <ffmpeg进程id> , obs拉流 一起观察这个现象
    public static void main(String[] args) {


        //  报错: Cannot run program "...": CreateProcess error=2, 系统找不到指定的文件
        // String[] commandList = new String[]{"ffmpeg -f gdigrab -i desktop -vcodec libx264 -f flv rtmp://192.168.134.3:8002/flv/test"};


        // 这样写15s后无法结束ffmpeg进程
        // String[] commandList = new String[]{"cmd", "/c" , "ffmpeg -f gdigrab -i desktop -vcodec libx264 -f flv rtmp://192.168.134.3:8002/flv/test"};
        // 这样写15s后无法结束ffmpeg进程
        // String[] commandList = new String[]{"cmd", "/c" , "ffmpeg", "-f", "gdigrab", "-i", "desktop", "-vcodec", "libx264", "-f", "flv", "rtmp://192.168.134.3:8002/flv/test"};
        // 这样写只是输出了ffmpeg的版本号
        // String[] commandList = new String[]{"ffmpeg", " -f gdigrab -i desktop -vcodec libx264 -f flv rtmp://192.168.134.3:8002/flv/test"};

        // 这样可以正常启动ffmpeg, 并在15s后关闭退出
        String[] commandList = new String[]{"ffmpeg", "-f", "gdigrab", "-i", "desktop", "-vcodec", "libx264", "-f", "flv", "rtmp://192.168.134.3:8002/flv/test"};

        ProcessBuilder processBuilder = new ProcessBuilder(commandList);

        try {
            Process process = processBuilder.start();

            // 需要命令的输出内容读取出来, 否则进程可能会因为缓冲区满而挂起
            StreamGobbler stdout = new StreamGobbler(process.getInputStream(), "stdout");
            StreamGobbler errout = new StreamGobbler(process.getErrorStream(), "errout");
            stdout.start();
            errout.start();

            /*new Thread(() -> {
                try {
                    System.out.println("开始sleeping");
                    TimeUnit.SECONDS.sleep(15);
                    System.out.println("结束sleeping");

                    System.out.println("开始关闭" + process.isAlive());

                    process.getOutputStream().close();

                    process.destroy();
                    // process.destroyForcibly();
                    System.out.println("关闭成功" + process.isAlive());
                } catch (InterruptedException | IOException e) {
                    e.printStackTrace();
                }
            }).start();*/
            // 可以在此处等待process结束, 但是需要先开启上面这个线程来结束process, 就可以结束ffmpeg
            /*int exitCode = process.waitFor();
            System.out.println("退出码: "+ exitCode);*/

            System.out.println("开始sleeping");
            TimeUnit.SECONDS.sleep(15);
            System.out.println("结束sleeping");

            System.out.println("开始关闭" + process.isAlive());

            process.getOutputStream().close();

            process.destroy();
            // process.destroyForcibly();
            System.out.println("关闭成功" + process.isAlive());
            process = null;

            LockSupport.park();

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

    }

}

ShellUtils

public class ShellUtils {

    public static void main(String[] args) {

        //=====windows=====

        // 输出ip地址
        // ShellUtils.exec("ipconfig");

        // 弹出记事本, 关闭弹出的记事本, 这个命令才会结束
        // ShellUtils.exec("notepad");
        // 弹出记事本, 关闭弹出的记事本, 这个命令才会结束
        // ShellUtils.exec("cmd", "/c", "notepad");

        // Cannot run program "dir": CreateProcess error=2, 系统找不到指定的文件
        // ShellUtils.exec("dir");
        // 输出目录
        // ShellUtils.exec("cmd", "/c", "dir");
        // 输出ip地址
        // ShellUtils.exec("cmd", "/c", "ipconfig");
        // ShellUtils.exec("cmd", "/c", "ping www.baidu.com");

        // 运行ffmpeg
        // ShellUtils.exec("ffmpeg", "-f", "gdigrab", "-i", "desktop", "-vcodec", "libx264", "-f", "flv", "rtmp://192.168.134.3:8002/flv/test");

        // =====linux=====
        // 可以通过ProcessBuilder设置工作目录
        ShellUtils.exec("sh", "-c", "ls");

    }

    public static String exec(String... commandList) {

        ProcessBuilder processBuilder = new ProcessBuilder(commandList);
        try {
            System.out.println("准备执行命令: " + String.join(" ", commandList));
            // 创建进程
            Process process = processBuilder.start();

            // 读取进程的输出流和错误流
            StreamGobbler stdout = new StreamGobbler(process.getInputStream(), "stdout");
            StreamGobbler errout = new StreamGobbler(process.getErrorStream(), "errout");

            System.out.println("准备输出");
            stdout.start();
            errout.start();

            System.out.println("开始等待结果");
            // 等待进程结束
            int exitCode = process.waitFor();
            System.out.println("执行结果:" + exitCode);

            // 关闭输入流
            process.getOutputStream().close();

            stdout.join();
            errout.join();
            System.out.println("输出完毕");

            if (exitCode == 0) {
                System.out.println("执行成功");
                return stdout.output.toString();
            } else {
                System.out.println("执行失败");
                return errout.output.toString();
            }

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
            throw new RuntimeException("运行发生错误");
        }


    }

}

public class StreamGobbler extends Thread {

    // 获取是否为windows系统
    private static boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows");

    private InputStream inputStream;

    private String type;

    StringBuilder output = new StringBuilder();

    public StreamGobbler(InputStream inputStream, String type) {
        this.inputStream = inputStream;
        this.type = type;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + "-进入输出, type=" + type);
            String charset = isWindows ? "GBK" : "UTF-8";
            BufferedReader br =  new BufferedReader(new InputStreamReader(inputStream, charset));
            String line = null;
            while ((line = br.readLine()) != null) {
                System.out.println(Thread.currentThread().getName() + "-线程输出, type=" + type + ", line=>" + line);
                output.append(line);
                output.append(System.lineSeparator());
                TimeUnit.MILLISECONDS.sleep(100);
            }
            System.out.println(Thread.currentThread().getName() + "-线程输出结束, type=" + type);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                System.out.println(Thread.currentThread().getName() + "-关闭流发生错误, type=" + type);
                e.printStackTrace();
            }
        }
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到