问题
I need to pipe a text argument to the stdin of a command launched with Apache Commons Exec (for the curious, the command is gpg and the argument is the passphrase to the keystore; gpg does not have an argument to provide the passphrase explicitly, only to accept it from stdin).
In addition, I need this to support both Linux and Windows.
In a shell script I'd do
cat mypassphrase|gpg --passphrase-fd
or
type mypassphrase|gpg --passphrase-fd
but type doesn't work on Windows as it's not an executable but a command built into the command interpreted (cmd.exe).
The code not working (for the above reason) is below. To spawn an entire shell for this is too ugly, I was looking for a more elegant solution. Unfortunately, there are some incompatibility problems between the BouncyCastle library and PGP so I cannot use a fully programmatic solution in the (very short) time I have.
Thanks in advance.
CommandLine cmdLine = new CommandLine("type");
cmdLine.addArgument(passphrase);
cmdLine.addArgument("|");
cmdLine.addArgument("gpg");
cmdLine.addArgument("--passphrase-fd");
cmdLine.addArgument("0");
cmdLine.addArgument("--no-default-keyring");
cmdLine.addArgument("--keyring");
cmdLine.addArgument("${publicRingPath}");
cmdLine.addArgument("--secret-keyring");
cmdLine.addArgument("${secretRingPath}");
cmdLine.addArgument("--sign");
cmdLine.addArgument("--encrypt");
cmdLine.addArgument("-r");
cmdLine.addArgument("recipientName");
cmdLine.setSubstitutionMap(map);
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(cmdLine);
回答1:
You cannot add a pipe argument (|
) because the gpg
command won't accept that. It's the shell (e.g. bash
) that interprets the pipe and does special processing when you type that commandline into the shell.
You can use ByteArrayInputStream
to manually send data to the standard input of a command (much like bash
does when it sees the |
).
Executor exec = new DefaultExecutor();
CommandLine cl = new CommandLine("sed");
cl.addArgument("s/hello/goodbye/");
String text = "hello";
ByteArrayInputStream input =
new ByteArrayInputStream(text.getBytes("ISO-8859-1"));
ByteArrayOutputStream output = new ByteArrayOutputStream();
exec.setStreamHandler(new PumpStreamHandler(output, null, input));
exec.execute(cl);
System.out.println("result: " + output.toString("ISO-8859-1"));
This should be the equivalent of typing echo "hello" | sed s/hello/goodbye/
into a (bash
) shell (though UTF-8 may be a more appropriate encoding).
回答2:
Hi to do this i will use a little helper class like this: https://github.com/Macilias/Utils/blob/master/ShellUtils.java
basically you can than simulate the pipe usage like shown here before without calling the bash beforehand:
public static String runCommand(String command, Optional<File> dir) throws IOException {
String[] commands = command.split("\\|");
ByteArrayOutputStream output = null;
for (String cmd : commands) {
output = runSubCommand(output != null ? new ByteArrayInputStream(output.toByteArray()) : null, cmd.trim(), dir);
}
return output != null ? output.toString() : null;
}
private static ByteArrayOutputStream runSubCommand(ByteArrayInputStream input, String command, Optional<File> dir) throws IOException {
final ByteArrayOutputStream output = new ByteArrayOutputStream();
CommandLine cmd = CommandLine.parse(command);
DefaultExecutor exec = new DefaultExecutor();
if (dir.isPresent()) {
exec.setWorkingDirectory(dir.get());
}
PumpStreamHandler streamHandler = new PumpStreamHandler(output, output, input);
exec.setStreamHandler(streamHandler);
exec.execute(cmd);
return output;
}
来源:https://stackoverflow.com/questions/4695664/how-to-pipe-a-string-argument-to-an-executable-launched-with-apache-commons-exec