我不知道是否应该在 StackOverflow 或 AskUbuntu 上发布此问题,根据我目前的理解,问题在于我对 Ubuntu 中的权限和执行权限/处理缺乏了解,而不是 Java 编程方法,因此我认为这是需要询问的地方。
在成功执行编译为 的 Java 项目中生成的终端命令时commandUbuntu.jar
,我发现一个命令没有按我想象的那样执行。我安装了 taskwarrior 2.5,并尝试使用 Java 创建自定义用户定义属性 (UDA)。命令是:
printf 'y\n' | sudo task config uda.newTestSort.type numeric
printf 'y\n' | sudo task config uda.newTestSort.label nTSort
再生产 Taskwarrior 可以通过以下方式安装:
sudo apt update && sudo apt upgrade
sudo apt install task
您可以使用以下方式添加 2 个任务:
task add this is the test task one description
task add test task two
附件是一个为运行这 2 个命令而创建的 MWE,可以.jar
在 eclipse 中通过单击将其编译为:file>export>select node Java>Select Runnable JAR file>Click next>Launch Configuration:
CommandLinux - LearnToSayYesToLinux >Chose an export location and name, e.g.
c:/commandLinux.jar`>将所需的库打包到生成的 JAR 中>单击完成。
然后它可以在 Ubuntu 中使用以下命令运行:
cd /mnt/c/commandLinux.jar
java -jar commandLinux.jar
梅威瑟:
package learnToSayYesToLinux;
import java.io.*;
import java.util.ArrayList;
public class CommandLinux {
public static void main(String[] args) {
// TODO Auto-generated method stub
//Test create a custom UDA
createUDA("abstractSort","aSort","numeric");
System.exit(0);
}
/**
* Method creates a taskwarrior user defined Attribute if the data type is correct
* Thows error datatype is not correct.
* TODO: write proper exception
* @param udaName
* @param label
* @param type
*/
private static void createUDA(String udaName, String label,String type) {
char vd = (char)124; //vertical dash: |
char bs = (char)92; //backslash: \
String[] commands = new String[2];
//Check if the datatype is correct for taskwarrior:
if (type.equals("numeric") || type.equals("string") || type.equals("date") || type.equals("duration")){
commands[0]="printf 'y"+bs+"n' "+vd+" sudo task config uda."+udaName+".type "+type;
commands[1]="printf 'y"+bs+"n' "+vd+" sudo task config uda."+udaName+".label "+ label;
runCommands(commands[0], false);
runCommands(commands[1], false);
System.out.println("Ran:"+commands[0]);
System.out.println("Ran:"+commands[1]);
//Trow exception if the datatype is not correct.
}else {
try {
throw new Exception();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static ArrayList<ArrayList<String>> runCommands(String command,boolean ignoreOutput) {
String s = null;
String outputLines=null;
ArrayList<String> goodExecutionOutput=new ArrayList<String>();
ArrayList<String> errorExecutionOutput=new ArrayList<String>();
ArrayList<ArrayList<String>> returnLists = new ArrayList<ArrayList<String>>();
try {
// run the Unix "task nice0" command
Process p = Runtime.getRuntime().exec(command);
BufferedReader brGood = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader brError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
// get output
if (!ignoreOutput) {
while ((s = brGood.readLine()) != null) {
System.out.println("Adding:"+s);
goodExecutionOutput.add(s);
}
// get the error message
while ((s = brError.readLine()) != null) {
errorExecutionOutput.add(s);
}
}
}
catch (IOException e) {
System.out.println("Error: ");
e.printStackTrace();
System.exit(-1);
}
//Merge outputLists and return
returnLists.add(goodExecutionOutput);
returnLists.add(errorExecutionOutput);
return returnLists;
}
}
结果
如果文件执行该命令,.jar
则返回:
ationAndSystems/Taskwarrior/customSortServerV4$ java -jar testCommand.jar
Adding:'y
Adding:'
Adding:'y
Adding:'
Ran:printf 'y\n' | sudo task config uda.newTestSort.type numeric
Ran:printf 'y\n' | sudo task config uda.newTestSort.label nTSort
如果我手动运行该命令,它会返回:
$ printf 'y\n' | sudo task config uda.newTestSort.type numeric
Are you sure you want to change the value of 'uda.newTestSort.type' from 'numeric' to 'numeric'? (yes/no) Config file /home/a/.taskrc modified.
$ printf 'y\n' | sudo task config uda.newTestSort.label nTSort
Are you sure you want to change the value of 'uda.newTestSort.label' from 'nTSort' to 'nTSort'? (yes/no) Config file /home/a/.taskrc modified.
该结果的解释/测试在“验证”中讨论。
结果尝试 2 正如评论中所建议的:将 MWE 的第 36 行和 37 行修改为:
commands[0]="yes yes "+vd+" sudo task config uda."+udaName+".type "+type;
commands[1]="yes yes "+vd+" sudo task config uda."+udaName+".label "+ label;
产生无限重复(直到发生 Stack Overflow):
Adding:yes | sudo task config uda.testSortA.type numeric
如果我boolean ignoreOutput
在调用方法时将其设置为 false,runCommands()
它将返回:
Ran:yes yes | sudo task config uda.testSortA.type numeric
Ran:yes yes | sudo task config uda.testSortA.label tSortA
经过验证,我得出结论,该命令并没有有效地添加新的 UDA。
确认 如果我在 Ubuntu 16.04 中手动输入它们,它们可以正常工作。我通过输入以下内容进行测试:
sudo task 2 modify newTestSort:29
如果 UDA 尚不存在于 taskwarrior 中,它将把该语句解释为:“修改任务 2 的描述”。但是,如果 UDAnewTestSort
确实存在并且类型为 ,numeric
它将把任务 2 uda newTestSort
(标记为nTSort
)设置为 2。这两个结果在命令 的输出中读取sudo task 2
,第一次是在创建 UDA 之前,通过检查任务描述是否已更改为 来验证 UDA 尚不存在;第二次是在输入上面列出的 2 个 UDA 创建命令之后,通过检查新 UDA 是否以值 29 列出来newTestSort:29
验证 UDA 确实已创建。nTSort
问题: 如何使用 Java 自动回答以下提示中的“是”而不创建其他文件?
您确定要将“uda.newTestSort.type”的值从“numeric”更改为“numeric”吗?(是/否)配置文件 /home/a/.taskrc 已修改。
目前的理解
lesmana 的回答解释垂直破折号将破折号右侧的输入与破折号左侧的输出连接起来。它表示的输出yes
是无休止的y
“'”流,因此在命令要求输入后,它会将这些y
“-”流到该问题中。我目前不明白为什么它会停止这样做。
我的另一个疑问是,由于命令是从.jar
文件传递给进程而不是在终端中输入,因此命令的解释方式可能与手动输入时不同。这可能会导致 | 的解释/含义不同。我正在研究 ubuntu 如何从文件中解释命令.jar
。
另外,我检查了文件的权限是否存在问题.jar
,因此我尝试使用以下命令执行第二次尝试的建议命令:
sudo java -jar commandUbuntu.jar
但经过验证后,我得出结论,这不允许从 Java 添加新的 UDA。
答案1
Pepe 在以下已接受的答案下发布了该问题本质的解决方案:https://stackoverflow.com/questions/4157303/how-to-execute-cmd-commands-via-java
根据我目前的理解,命令可以有输入和输出。垂直破折号|
用于管道在终端中。例如,假设你有一个生成输出的命令,例如yes
,那么该输出可以直接链接到另一个命令的输入*。假设另一个命令是我尝试执行的命令,该命令由于以下问题而具有输入:
您确定要将“uda.newTestSort.type”的值从“numeric”更改为“numeric”吗?(是/否)配置文件 /home/a/.taskrc 已修改。
例如:
printf 'yes\n' | task config uda.testSortC.type numeric
在普通终端中输入命令,命令的输出通过yes
管道传输到命令:task config uda.testSortC.type numeric
,这回答了上面的问题。我目前理解这个问题本身是该命令的输出,但仅仅是伴随该命令输入的消息。接收输入yes
会终止无限的输出流yes
,并且命令会按照yes
输入的方式执行。
现在你可以使用以下命令在 Java 中调用某个进程:
Process p = Runtime.getRuntime().exec(command);
这.exec(command)
与在 Terminal^ 中输入命令并不完全相同。区别之一是,如果管道在您使用垂直线用 Java 编写的命令中声明,则管道不起作用|
。要应用管道概念,您需要明确获取命令的输入,并明确将另一个命令的输出插入其中。这可以通过方法中的以下代码完成runCommands()
:
Process p;
try {
p = Runtime.getRuntime().exec(command);
new Thread(new SyncPipe(p.getErrorStream(), System.err)).start();
new Thread(new SyncPipe(p.getInputStream(), System.out)).start();
PrintWriter stdin = new PrintWriter(p.getOutputStream());
stdin.println("yes");
// write any other commands you want here
stdin.close();
int returnCode = p.waitFor();
System.out.println("Return code = " + returnCode);
} catch (IOException | InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
并将以下类添加到您的项目中:
class SyncPipe implements Runnable
{
public SyncPipe(InputStream istrm, OutputStream ostrm) {
istrm_ = istrm;
ostrm_ = ostrm;
}
public void run() {
try
{
final byte[] buffer = new byte[1024];
for (int length = 0; (length = istrm_.read(buffer)) != -1; )
{
ostrm_.write(buffer, 0, length);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
private final OutputStream ostrm_;
private final InputStream istrm_;
}
据我目前了解,p
包含“获取”执行命令的进程,那么该类SyncPipe
实际上是一个管道。进程的输入p
使用连接到管道thread
,然后执行生成输出的新命令stdin
。不知何故,管道然后获取该输出流并将其插入第一个命令的输入流。这个解决方案尽可能明确,以迫使我明确概念,因此它可能仍然包含错误,如果是这样,请评论/编辑。
*(如果该命令有一个输入。我目前不知道所有命令是否都有输入,如果没有,我目前不知道如果将无限输出流(例如)yes
传输到没有输入的命令中会发生什么)
^我目前并不确切知道它们之间有什么区别,这需要我更深入地了解终端中发生了什么、命令是如何处理的以及进程是如何执行的。