【问题标题】:Sending command to java -jar using stdin via /proc/{pid}/fd/0通过 /proc/{pid}/fd/0 使用标准输入向 java -jar 发送命令
【发布时间】:2015-01-02 02:34:46
【问题描述】:

我正在尝试使用 /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] [服务器线程/INFO]:[服务器] hi

第二个控制台:

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

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

在我切换回第一个控制台之前,一切看起来都很好。我可以看到文本“say hi2”,但服务器无法识别它。我可以再次在第一个控制台中编写另一个命令,就好像从第二个控制台输入的文本甚至不存在一样。

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

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

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

【问题讨论】:

  • 也许您可以通过在启动时将 socat 的实例通过管道传输到进程的标准输入,然后使用该路径注入命令来实现您的最终目标?

标签: java linux bash stdin minecraft


【解决方案1】:

当进程结束时,命名管道被“关闭”是可能的。您可以通过在另一个进程中打开命名管道的文件描述符来做到这一点。

#! /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

【讨论】:

    【解决方案2】:

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

    像这样测试它:

    控制台 1:

     $ cat
    

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

    Console2:查找cat 命令的进程号。假设它是NNN。做:

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

    切换回 Console1。您会在控制台输出中看到“Something”,但没有回显。

    为什么?做

    $ ls -l /proc/NNN/fd
    

    你可能明白。所有三个描述符,0 代表 stdin,1 代表 stdout 和 2 代表 stderr 实际上是符号链接,并且都指向同一个伪终端从 (pts) 设备,这是与您的第一个终端关联的 pts。

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

    /proc 的文档说:

    /proc/[pid]/fd/

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

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

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

    通过执行以下操作创建一个先进先出:

    $ mkfifo myfifo
    

    运行你的java程序:

    $ java -jar minecraft_server.jar nogui < myfifo
    

    打开另一个控制台。写

    $ cat > myfifo
    

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

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

    【讨论】:

    • 这样可以解决关闭问题:while [ 1 ] ;做猫 myfifo ;完成 |你的程序
    猜你喜欢
    • 2018-12-05
    • 1970-01-01
    • 1970-01-01
    • 2012-06-26
    • 2017-11-20
    • 2012-12-12
    • 2017-10-21
    • 2016-06-22
    • 2014-03-04
    相关资源
    最近更新 更多