文章目录
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();
}
}
}
详解
构建命令:
String command = "cmd /c dir";
:这里我们使用了Windows的cmd
命令行工具,并通过/c
参数告诉它执行完命令后关闭。dir
是列出当前目录下的文件和文件夹的命令。
执行命令:
Process process = Runtime.getRuntime().exec(command);
:这行代码通过运行时环境执行指定的命令。返回的Process
对象可以用来控制进程和获取其输出。
读取命令的输出:
- 使用
BufferedReader
包装InputStreamReader
来读取进程的标准输出流(即命令的输出)。 - 循环读取每一行直到没有更多输出。
- 使用
等待命令执行完成:
int exitCode = process.waitFor();
:这会阻塞当前线程直到外部进程结束,并返回进程的退出值。通常,0表示正常退出,非0值表示有错误发生。
异常处理:
- 捕获并处理
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,表示调用命令将在当前程序执行的目录中执行,因此调用到其他目录中的文件和脚本最好使用绝对路径。各个参数的含义:
- cmdarray: 包含所调用命令及其参数的数组。
- command: 一条指定的系统命令。
- envp: 字符串数组,其中每个元素的环境变量的设置格式为name=value;如果子进程应该继承当前进程的环境,则该参数为 null。
- 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格式)
此时网上程序可以了,然后看到指令不同,修改指令即可以了。
最后终于调通了,看来格式经验不足啊
四、参考文献
转载于: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
:表示执行指定的命令后立即退出命令提示符。
详细解释
cmd
:- 启动 Windows 命令提示符(Command Prompt)。
- 这是一个命令行解释器,用于执行各种命令和脚本。
/c
:- 指定命令提示符在执行完指定的命令后立即退出。
- 语法格式为
cmd /c <command>
,其中<command>
是你希望执行的具体命令。
示例
假设你有一个命令 dir
,用于列出当前目录下的文件和文件夹。你可以使用 cmd /c dir
来执行这个命令:
cmd /c dir
这条命令会启动命令提示符,执行 dir
命令,然后立即退出命令提示符。
为什么使用 cmd /c
命令组合:
- 有时需要组合多个命令或使用命令行的特殊功能(如管道、重定向等),
cmd /c
可以帮助实现这一点。
- 有时需要组合多个命令或使用命令行的特殊功能(如管道、重定向等),
兼容性:
- 在某些情况下,直接执行某些命令可能会遇到路径或环境变量的问题,使用
cmd /c
可以确保命令在正确的环境中执行。
- 在某些情况下,直接执行某些命令可能会遇到路径或环境变量的问题,使用
立即退出:
- 使用
/c
选项可以确保命令执行完毕后立即退出命令提示符,不会阻塞当前的 Java 进程。
- 使用
总结
cmd /c
是一个非常有用的命令行选项,特别是在需要执行复杂命令或组合命令时。在你的代码中,它确保 ffmpeg
命令在一个独立的命令提示符窗口中运行,并且在命令执行完毕后立即退出,不会阻塞当前的 Java 进程。
"sh", "-c"
的含义
"sh", "-c"
是在 Unix/Linux 系统中用于启动 shell 并执行指定命令的参数。具体来说:
sh
:启动 Bourne shell(或兼容的 shell,如 Bash)。-c
:表示执行指定的命令字符串,然后退出 shell。
详细解释
sh
:sh
是 Unix/Linux 系统中的一个命令行解释器,通常指的是 Bourne shell(Bourne shell 的文件通常是/bin/sh
)。- 它可以执行脚本文件或命令行中的命令。
-c
:-c
是sh
的一个选项,表示从命令行参数中读取命令字符串并执行。- 语法格式为
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"
命令组合:
- 有时需要组合多个命令或使用 shell 的特殊功能(如管道、重定向等),
sh -c
可以帮助实现这一点。
- 有时需要组合多个命令或使用 shell 的特殊功能(如管道、重定向等),
环境变量:
- 使用
sh -c
可以确保命令在正确的 shell 环境中执行,包括环境变量的设置。
- 使用
立即退出:
- 使用
-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();
}
}
}
}
}
解释
创建进程:
使用ProcessBuilder
创建一个进程,命令列表为{"sh", "-c", "ls"}
。读取输出流和错误流:
使用StreamGobbler
类分别读取进程的输出流和错误流,确保缓冲区不会满。等待进程结束:
使用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();
}
}
}
}