使用对话框自己的工具:--output-fd标志
如果您阅读对话框的手册页,则有option --output-fd
,它允许您显式设置输出的位置(STDOUT 1,STDERR 2),而不是默认情况下转到STDERR的位置。
在下面,您可以看到我正在运行sample dialog
命令,并明确说明输出必须进入文件描述符1,这使我可以将其保存到MYVAR中。
MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
使用命名管道
具有很多潜在潜力的另一种方法是使用一种称为管道的方法。
#!/bin/bash
mkfifo /tmp/namedPipe1 # this creates named pipe, aka fifo
# to make sure the shell doesn't hang, we run redirection
# in background, because fifo waits for output to come out
dialog --inputbox "This is an input box with named pipe" 40 40 2> /tmp/namedPipe1 &
# release contents of pipe
OUTPUT="$( cat /tmp/namedPipe1 )"
echo "This is the output " $OUTPUT
# clean up
rm /tmp/namedPipe1
user.dz的原始答案和ByteCommander对此的解释都提供了一个很好的解决方案并概述了其作用。不过,我相信更深入的分析可能是有益的,解释为什么它的工作原理。
首先,重要的是要了解两件事:我们要解决的问题是什么以及我们要处理的Shell机制的基础工作是什么。任务是通过命令替换捕获命令的输出。在众所周知的简单概述下,命令替换捕获stdout
命令的,并让其被其他对象重用。在这种情况下,该result=$(...)
部分应将命令指定的任何命令的输出保存...
到名为的变量中result
。
在引擎盖下,命令替换实际上是通过管道实现的,其中有一个子进程(运行的实际命令)和读取进程(将输出保存到变量)。通过简单的系统调用跟踪就可以看出这一点。请注意,文件描述符3是管道的读取端,而4是写入端。对于的子进程echo
,其将stdout
- 写入文件描述符1,该文件描述符实际上是文件描述符4的副本,该文件描述符4是管道的写端。请注意,stderr
这里并没有发挥作用,仅仅是因为它只是一个连接的管道stdout
。
$ strace -f -e pipe,dup2,write,read bash -c 'v=$(echo "X")'
...
pipe([3, 4]) = 0
strace: Process 6200 attached
[pid 6199] read(3, <unfinished ...>
[pid 6200] dup2(4, 1) = 1
[pid 6200] write(1, "X\n", 2 <unfinished ...>
[pid 6199] <... read resumed> "X\n", 128) = 2
[pid 6200] <... write resumed> ) = 2
[pid 6199] read(3, "", 128) = 0
[pid 6200] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=6200, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
让我们再回到原始答案。从现在起,我们知道dialog
将TUI框写入stdout
,回答stderr
,并且在命令替换 中将其传递stdout
到其他位置,我们已经有了解决方案的一部分-我们需要以stderr
将其传递给阅读器进程的方式重新连接文件描述符。这就是2>&1
答案的一部分。但是,我们如何使用TUI box?
那就是文件描述符3出现的地方dup2()
。syscall允许我们复制文件描述符,使它们有效地指向同一位置,但我们可以单独操作它们。具有控制终端的进程的文件描述符实际上指向特定的终端设备。这很明显,如果你这样做
$ ls -l /proc/self/fd
total 0
lrwx------ 1 user1 user1 64 Aug 20 10:30 0 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 1 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 2 -> /dev/pts/5
lr-x------ 1 user1 user1 64 Aug 20 10:30 3 -> /proc/6424/fd
/dev/pts/5
我当前的伪终端设备在哪里?因此,如果我们能以某种方式保存该目的地,我们仍然可以将TUI框写入终端屏幕。就是exec 3>&1
那样 command > /dev/null
例如,当您调用带有重定向的命令时,shell会传递其stdout文件描述符,然后将dup2()
其写入/dev/null
。该exec
命令在整个Shell会话中执行类似于dup2()
文件描述符的操作,因此使任何命令都继承已重定向的文件描述符。与相同exec 3>&1
。3
现在,文件描述符将引用/指向控制终端,并且在该Shell会话中运行的任何命令都将知道它。
因此,当result=$(dialog --inputbox test 0 0 2>&1 1>&3);
发生这种情况时,shell为对话创建了一个管道以进行写入,但2>&1
还将首先使命令的文件描述符2复制到该管道的写入文件描述符中(因此使输出进入管道的末尾并进入变量)。 ,而文件描述符1将被复制到3上。这将使文件描述符1仍指向控制终端,并且TUI对话框将显示在屏幕上。
现在,该过程的当前控制终端实际上有一个简写形式/dev/tty
。因此,无需使用文件描述符就可以简化解决方案,只需简化为:
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"
要记住的关键事项:
- 每个命令都从Shell继承文件描述符
- 命令替换被实现为管道
- 复制的文件描述符将与原始文件描述符指向相同的位置,但是我们可以分别操纵每个文件描述符
也可以看看
mktemp
command创建一个临时文件。