让我们有一个简单的Java应用程序,我有以下代码来扫描用户输入:
Scaner scan = new Scanner(System.in);
System.out.print("Enter the value: ");
int value = scan.nextInt();
System.out.println("You entered: " + value);
当我手动运行它时,按预期我会看到我输入的内容,示例运行将是:
Enter the value: 3
You entered: 3
但是,当我有一个包含内容的文件时
3
我希望将程序stdout重定向到一个文件:
java Test < INPUT > OUTPUT
我得到的只是cat OUTPUT
:
Enter the value: You entered: 3
由于输入的值没有显示在标准输出上,这是正确的,它们不在stdout上,我知道,我看到它们是因为我手动输入它到我的终端。但是当我像这样重定向标准输出时,我怎么能看到它们?
修改
我可以看到代码正在制作read(0,
系统调用。
$ (strace -ff -e trace=read -e read 2>&1 java Test < INPUT) | grep "read(0,"
[pid 7297] read(0, "3\n", 8192) = 2
有没有更好的方法来拦截它?
编辑2:
我的意思是,当我使用java < INPUT > OUTPUT
Enter the value: 3
You entered: 3
答案 0 :(得分:2)
编写另一个程序,让我们称之为驱动程序,在驱动程序中调用new Process()
,在此过程中执行此程序,将stdin连接到stdout并将其连接到驱动程序。只要进程写入Enter new value:
,就让驱动程序从输入文件中发送值等。让驱动程序打印输入和输出。
另一个选择是驱动程序在strace
(特定于Linux)中监视进程,监视read(0, ...)
次调用。
这是Perl(rtrace.pl
)中的一个解决方案,使用strace时使用0.1秒作为超时:
#! /usr/bin/perl -w
use integer;
use strict;
$^F = 0x7fffffff; # Disable close-on-exec on pipe().
my $input_filename = shift;
my $if;
die if !open($if, '<', $input_filename);
my($r,$w);
die if !pipe($r,$w);
my($sr,$sw);
die if !pipe($sr,$sw);
my $pid=fork();
die if !defined $pid;
if (!$pid) {
close($if);
close($w);
close($sr);
if (fileno($r)) { die if !open STDIN, '<&', $r; close($r); }
die if !exec 'strace', '-o', '/proc/self/fd/' . fileno($sw), '-s', '4',
'-e', 'trace=read', '--', @ARGV;
}
close($r);
close($sw);
{ my $old = select($w); $| = 1; select($old); }
$| = 1;
my($s,$got);
L: for (;;) {
die if !defined($got = sysread($sr, $s, 4096));
last if !$got;
if ($s =~ /^read\(0, \Z(?!\n)/m) {
{ my $rin = '';
vec($rin, fileno($sr), 1) = 1;
$got = select($rin, undef, undef, .1);
next L if $got;
}
$s = <$if>;
if (!length($s)) {
close($w);
close($if);
} else {
print $s;
print $w $s;
}
}
}
die if $pid != waitpid($pid, 0);
exit $?;
使用它:
$ chmod +x rtrace.pl
$ ./rtrace.pl INPUT java Test >OUTPUT
您可能希望将-ff
添加到Java的strace
参数列表中。
请注意,如果行很长,可能会出现死锁问题。可以通过在sysread
之后添加输入缓冲来解决此问题。
答案 1 :(得分:2)
您无法修改提供的代码,但可以修改启动方式。
我提供的是一个假设System.in发生变化的黑客攻击。我做了什么:
1)我使用单main
方法制作了一个测试程序,其中包含您发布的代码。我推出了它:
$ java -cp dist/Test.jar test.Test <input
Enter the value: You entered: 3
2)我写了一个java程序
代码:
package redirecter;
import java.io.IOException;
import java.io.InputStream;
public class Redirecter {
public static void main(String[] args) {
final InputStream oldIn = System.in;
InputStream hackedIn = new InputStream() {
@Override
public int read() throws IOException {
int b = oldIn.read();
System.out.write(b);
return b;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int res = oldIn.read(b, off, len);
System.out.write(b, off, res);
return res;
}
};
System.setIn(hackedIn);
test.Test.main(args);
}
}
3)我用一种新的方式启动旧代码:
$ java -cp dist/Test.jar:../Redirecter/dist/Redirecter.jar redirecter.Redirecter <input
Enter the value: 3
You entered: 3
未编辑提供的代码;它刚刚以棘手的方式发布。保存了所有参数和jvm选项。如果您有多个入口点,则可以使用反射而不是简单调用test.Test.main(args);
错误点:如果有人在提供的代码中编辑System.in
,那么黑客攻击将会被破坏。但在使用标准输入之前,它似乎是安全的。
答案 2 :(得分:1)
未编译/测试(尚未):
class EchoInputStream extends InputStream {
private InputStream in;
private OutputStream echoOut;
public void EchoInputStream(InputStream in, OutputStream echoOut) {
this.in = in;
this.echoOut = out;
}
// Reads the next byte of data from in and echos to echoOut
public int read() {
int r = in.read();
echoOut.write(r);
return r;
}
// Reads a bytes from in, stores them into the buffer array b and echos to echoOut.
int read(byte[] b) {
in.read(b);
echoOut.write(b);
}
// Reads up to len bytes of data from in into array b and echos them to echoOut.
int read(byte[] b, int off, int len) {
in.read(b, off, len);
echoOut.write(b, off, len);
}
void reset() {
in.reset();
}
// for other abstract methods, simply redirect to in
}
使用:
Scaner scan = new Scanner(new EchoInputStream(System.in, System.out));
System.out.print("Enter the value: ");
int value = scan.nextInt();
System.out.println("You entered: " + value);
答案 3 :(得分:1)
这是我的答案:
将此脚本另存为wrap.sh
#!/bin/bash
while read -s input_line
do
raw_out=$(echo $input_line | $1)
header=${raw_out%%:*}:
echo -e ${raw_out/$header/$header$input_line\\n}
done < ${2:-/proc/${$}/fd/0}
chmod +x wrap.sh
使用它:./wrap.sh 'java yourprog' inputfile.txt
输入文件的数量与您想要的数量相同,每行一个。
请记住围绕'java yourprog'
如果您想以交互方式使用它,您仍然可以(只是省略输入文件):
$ ./wrap.sh 'java yourprog'
Enter number:12345
You entered: 12345
您也可以像这样重定向:./wrap.sh 'java yourprog' > outfile.txt
,但输入视为不可见,仅存在于outfile.txt
。
最后的话:
要求:您的计划需要提出一个问题,例如“输入数字:”或“输入值:”(就像在您的代码中一样)包含一个冒号!
用我的玩具C编程测试它 - 没有必要启动java所以它看起来更漂亮:)
这当然可以进行更多的按摩,但它提供了您所要求的基本功能。
答案 4 :(得分:0)
我认为bash / zsh中的命名管道,协同处理或进程替换可以解决您的问题。这是使用进程替换的bash中的一个hacky变通方法。基本上,您可以一次循环输入一行,将其重定向到EXE,再次输出到输出。睡眠确保一切都正确排序。
exec 6> >(./TEST.exe)
sleep 1
while read x; do
echo $x
sleep 1
echo $x >&6
sleep 1
done < INPUT.txt