禁止 GPG“从文件描述符 0 读取密码”消息

禁止 GPG“从文件描述符 0 读取密码”消息

简单来说,我怎样才能让 GPG 不打印该消息?以下是我使用的命令:

echo "test input" > test.in
echo "test" | gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in
echo "test" | gpg -q -d --passphrase-fd 0 test.enc > test.out

并运行它:

$ echo "test input" > test.in 
$ echo "test" | gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in
Reading passphrase from file descriptor 0    
$ echo "test" | gpg -q -d --passphrase-fd 0 test.enc > test.out
Reading passphrase from file descriptor 0

编辑:重定向 stderr 似乎不起作用

$ echo "test" | gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in 2> /dev/null
Reading passphrase from file descriptor 0    

答案1

要了解发生了什么,一种方法是跟踪所涉及的系统调用。执行此操作的实用程序因平台而异。在 Solaris 上,您可以使用桁架. 在 Linux 上(如我的示例),您可以使用斯特拉斯


为了进行跟踪,我们将使用的命令更改为:

echo "test" | gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in 2> /dev/null

到:

echo "test" | strace gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in 2>trace_output.txt


第一个引人注目的有趣之处(虽然有点不相关)是 gpg 在从 stdin 获取输入密码时会重复执行单字节读取。这有时是代码效率低下的明显迹象 - 但在这种情况下,这可能不是什么大问题:

read(0, "t", 1)                         = 1
read(0, "e", 1)                         = 1
read(0, "s", 1)                         = 1
read(0, "t", 1)                         = 1
read(0, "\n", 1)                        = 1

关于日志消息输出的更多相关内容都在这里:

open("/dev/tty", O_RDWR)                = 3
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(5, 0), ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0
write(3, "Reading passphrase from file des"..., 45) = 45
write(3, "\10\10\10   \n", 7)           = 7

这就是我们在退出前(它没有明确关闭)听到的有关文件描述符 3 的全部信息。

依次看一下这些:

  • open("/dev/tty", O_RDWR) = 3

    这将打开文件 /dev/tty,用于读写。返回值(一个供以后使用的新文件描述符)为 3。

    /dev/tty 是当前控制终端的同义词。您可以通过运行以下命令查看此特殊文件实际引用的设备:$ tty

  • fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(5, 0), ...}) = 0

    gpg 使用它来查找刚刚用文件描述符 3 打开的文件。花括号中的内容是返回的内容(一个填充的结构体 stat,其中 5, 0 表示这是一个特别文件)。

  • ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0

    这是在输出之前操纵控制终端的属性。

  • write(3, "Reading passphrase from file des"..., 45) = 45

    write(3, "\10\10\10 \n", 7) = 7

    这些更加直接。gpg 成功地将该文本(其中一些在 strace 输出中被缩写)写入终端。


所以 - 这是你的答案.. gpg 将此日志消息直接写入 /dev/tty(控制终端的同义词),因此你将无法以与 stdout 或 stderr 相同的方式将其重定向。

那里解决这个问题的办法是。您可以在执行 gpg 之前断开控制终端。

这是一个完成这一任务的简短程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char* argv[])
{
  int rc, fd;
  if (argc != 2)
  {
    fprintf(stderr, 
     "Provide command line arg to execute after TIOCNOTTY\n");
    return EXIT_FAILURE;
  }
  fd = open("/dev/tty", O_RDWR);
  if (fd < 0)
  {
    fprintf(stderr, 
     "Failed to open controlling terminal: %s\n",
     strerror(errno));
    return EXIT_FAILURE;
  }
  rc = ioctl(fd, TIOCNOTTY);
  if (rc == -1)
  {
    fprintf(stderr,
     "Failed TIOCNOTTY ioctrl: %s\b",
     strerror(errno));
    return EXIT_FAILURE;
  }
  return system(argv[1]);
}

应该有一个现有的实用程序可以执行上述操作,但我找不到。

如果您要编译该代码并调用生成的可执行文件notty,那么您可以执行以下操作:

echo "test" | notty "gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in"

这应该会抑制消息,但保持 stdout 和 stderr 完好无损。不过,还不清楚还会抑制什么(您需要查看 gpg 源代码才能查看以这种方式输出的其他内容)。

答案2

--batch是答案,但是即使 stderr 被重定向,我也不知道它是如何输出的......

答案3

虽然 user30579 的回答对于理解该问题很有用,但让我们将其精简到实际解决问题所需的最低限度:

只需使用 GPG 内置的 --no-tty 开关。例如:

echo "test" | gpg --no-tty -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in

相关内容