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

通过/proc/{pid}/fd/0使用stdin向java-jar发送命令,java,linux,bash,stdin,minecraft,Java,Linux,Bash,Stdin,Minecraft,我试图使用/proc/{pid}/fd/0向minecraft服务器jar发送命令,但服务器不执行该命令 要复制我试图做的事情,您可以在基于Debian的机器上(也可能是其他Linux发行版)执行此操作 我用什么来测试这一点: Ubuntu 14.04 (使用1.8进行测试) OpenJDK运行时环境(与默认的jre headless一起安装) 第一控制台: 响应:[…服务器启动并等待输入] say hi 响应:[19:52:23][Server thread/INFO]:[Server]

我试图使用/proc/{pid}/fd/0向minecraft服务器jar发送命令,但服务器不执行该命令

要复制我试图做的事情,您可以在基于Debian的机器上(也可能是其他Linux发行版)执行此操作

我用什么来测试这一点:

  • Ubuntu 14.04
  • (使用1.8进行测试)
  • OpenJDK运行时环境(与默认的jre headless一起安装)
第一控制台:

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

say hi
响应:[19:52:23][Server thread/INFO]:[Server]您好

第二控制台:

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

echo "say hi2" >> /proc/$(pidof java)/fd/0
一切看起来都很好,直到我切换回第一个控制台。我可以看到文本“say hi2”,但服务器尚未识别它。我可以在第一个控制台中再次编写另一个命令,就好像从第二个控制台输入的文本根本不存在一样

为什么会这样?更重要的是,如何以适当的方式使用/proc/{pid}/fd/0将命令发送到JavaJAR文件

我不知道这是否是我不知道的某种Java东西,我是否可以在执行服务器时使用一些标志或其他东西,或者问题出在服务器jar本身


我知道可以使用screen、tail-f或某种服务器包装器来完成这项工作,但这不是我想要的。我想以某种方式使用此方法发送命令。

这不是Java的东西。你所尝试的根本是行不通的

像这样测试它:

控制台1:

$cat
这基本上会在你点击“return”时回显你在上面键入的任何内容

控制台2:查找
cat
命令的进程号。假设是NNN。做:

$echo Something>/proc/NNN/fd/0
切换回控制台1。您将在控制台输出上看到“Something”,但它没有回声

为什么??做

$ls-l/proc/NNN/fd
你也许能理解。所有三个描述符,0代表
stdin
,1代表
stdout
和2代表
stderr
实际上都是符号链接,都指向同一个伪终端从设备(pts),即与第一个终端关联的pts

因此,基本上,当您向它写入时,实际上是向控制台输出写入,而不是向其输入写入。如果您从该文件中读取,您可能会在第一个控制台中窃取一些本应发送到进程的输入(您正在争夺该输入)。这就是字符设备的工作原理

/proc的文档说明:

/proc/[pid]/fd/

这是一个子目录,每个文件包含一个条目 进程已打开的,以其文件描述符命名,以及 它是指向实际文件的符号链接。因此,0是 标准输入,1个标准输出,2个标准误差,依此类推 开

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

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

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

$mkfifo myfifo
运行java程序:

$java-jar minecraft\u server.jar nogui
打开另一个控制台。写

$cat>myfifo
现在开始打字。切换到第一个控制台。您将看到服务器正在执行您的命令


不过要注意你的文件结尾。多个进程可以写入同一个
fifo
,但一旦最后一个进程关闭它,您的服务器将在其标准输入上收到EOF。

可以绕过这样一个事实,即当进程结束时,命名管道“关闭”。您可以通过在另一个进程中打开命名管道的文件描述符来实现这一点

#! /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
#/bin/bash
#tac按相反顺序打印文件(tac->cat)
cmd=“tac”
#创建名为pipe的fifo
mkfifo管
#在当前进程的文件描述符3上打开管道
执行3管道
bash-c“
#子进程继承所有文件描述符。因此必须从子进程运行cmd-
#这允许我们关闭fd,因此cmd不会继承fd 3,而是允许fd 3
#在父进程上保持打开状态。
执行官3>&-
#启动cmd并将命名管道重定向到其stdin
$cmd<管道
" &
#将数据写入管道
回声你好>管道
回声世界>管道
#整理前的短暂等待
睡眠0.1
#完成数据写入,因此关闭父进程(此)上的fd 3
执行官3>&-
#tac现在知道它将不再接收数据,所以它打印数据并退出
#数据是无缓冲的,因此所有数据都会立即接收。试着用“猫”来代替看。
#清理管道
rm管道

也许您可以通过在启动过程时将socat实例管道化到过程的stdin中,然后使用该路径注入命令来实现最终目标?这可能会解决关闭问题:while[1];做猫myfifo;完成你的计划
#! /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