简单来说,我怎样才能让 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