通过/ proc / {pid} / fd / 0使用stdin向java -jar发送命令

时间:2014-11-05 19:03:55

标签: java linux bash stdin minecraft

我正在尝试使用/ proc / {pid} / fd / 0向Minecraft服务器jar发送命令,但服务器不执行该命令。

要复制我正在尝试做的事情,你可以在基于Debian的机器上执行此操作(可能还有其他Linux分发)。

我用它来测试这个:

  • Ubuntu 14.04
  • minecraft_server.jar(以1.8测试)
  • OpenJDK运行时环境(使用default-jre-headless安装)

第一个控制台:

$ java -jar minecraft_server.jar nogui

响应:[...服务器启动并等待输入]

say hi

回复:[19:52:23] [服务器主题/信息]:[服务器]嗨

第二个控制台:

现在,当我切换到第二个控制台时,服务器仍然在第一个写入中运行:

echo "say hi2" >> /proc/$(pidof java)/fd/0

一切顺利,直到我切换回第一个控制台。我可以看到文字“说hi2”,但服务器还没有认出来。我可以再次在第一个控制台中编写另一个命令,就好像从第二个控制台输入的文本甚至都不存在一样。

这是为什么?更重要的是,如何以正确的方式使用/ proc / {pid} / fd / 0将命令发送到java jar文件?

我不知道这是否是某种我不知道的Java事物,如果我可以在执行服务器时使用某些标志或某些东西,或者它是服务器jar本身就是问题。

我知道你可以使用screen,tail -f或某种服务器包装来实现这一目标,但这不是我所追求的。我想以某种方式使用这种方法发送命令。

2 个答案:

答案 0 :(得分:4)

它不是Java的东西。你正在尝试的是根本不可行的。

像这样测试:

控制台1:

 $ cat

一旦你点击"返回"这将基本上回应你在它上面输入的任何内容。

Console2:查找cat命令的进程号。让我们说它是NNN。做:

$ echo Something > /proc/NNN/fd/0

切换回Console1。你会看到" Something"在控制台输出上,但没有回应。

为什么呢?做

$ ls -l /proc/NNN/fd

你可能会理解。所有三个描述符,stdin为0,stdout为1,stderr为2,实际上是符号链接,并且都指向相同的伪终端从属(pts)设备,这是相关的pts与你的第一个终端。

所以基本上,当你写它时,你实际上写入控制台输出,而不是它的输入。如果您从该文件中读取,您可以窃取一些应该在第一个控制台中进入该进程的输入(您正在竞争此输入)。这就是角色设备的工作原理。

/ proc的文档说:

  

/proc/[pid]/fd/

     

这是一个子目录,每个文件包含一个条目   进程已打开,由其文件描述符命名,和   这是实际文件的符号链接。因此,0是   标准输入,1个标准输出,2个标准误差等   上。

所以这些不是该过程打开的实际文件描述符。它们只是指向文件(或者在本例中为字符设备)的链接,其名称指示它们在给定进程中附加到哪个描述符。他们的主要职责是告诉您该流程是重定向其文件描述符还是已打开任何新文件描述符,以及它们指向哪些资源。

但是如果你想要另一种方法,你可以使用fifo - 命名管道。

通过执行以下操作创建一个fifo:

$ mkfifo myfifo

运行你的java程序:

$ java -jar minecraft_server.jar nogui < myfifo

打开另一个控制台。写

$ cat > myfifo

现在开始输入内容。切换到第一个控制台。您将看到您的服务器正在执行您的命令。

请注意你的文件结尾。有几个进程可以写入相同的fifo,但只要最后一个进程关闭它,您的服务器就会在其标准输入上收到EOF。

答案 1 :(得分:0)

当进程结束时,可以绕过命名管道“关闭”这一事实。您可以通过在另一个进程中将文件描述符保持为命名管道来执行此操作。

#! /bin/bash

# tac prints a file in reverse order (tac -> cat)
cmd="tac"

# create fifo called pipe
mkfifo pipe
# open pipe on the current process's file descriptor 3
exec 3<>pipe

bash -c "
    # child process inherits all file descriptors. So cmd must be run from a sub-
# process. This allows us to close fd so cmd does not inherit fd 3, but allow fd 3
# to remain open on parent process.
    exec 3>&-
    # start your cmd and redirect the named pipe to its stdin
    $cmd < pipe
" &

# write data to pipe
echo hello > pipe
echo world > pipe

# short wait before tidy up
sleep 0.1
# done writing data, so close fd 3 on parent (this) process
exec 3>&-
# tac now knows it will receive no more data so it prints its data and exits
# data is unbuffered, so all data is received immediately. Try `cat` instead to see.
# clean up pipe
rm pipe