man
当您按下时,命令将关闭,q并将控制台恢复到以前的状态。
这个叫什么?
如何让另一个程序以这种方式运行?
答案1
man
正在使用分页程序less
来提供此功能。您可以通过管道传输命令的标准输出和标准错误流来执行相同的操作,如下less
所示:
my_command_here arg1 arg2 |& less
输出的内容my_command_here
将被放入一个易于滚动的屏幕中,您可以使用 退出q。您可以尝试一下,感受一下它的效果ip address help |& less
- 您可以使用箭头键滚动,并使用PgUp和PgDn,然后使用 退出q。
Bash|&
还将错误输出 (stderr) 重定向到管道|
,不同于仅重定向正常输出 (stdout) 的plain 。使用|
,任何错误都会导致混乱的输出,因为它们会出现在终端上,但在 中无法滚动less
。(在 中sh
,您将使用标准... 2>&1 | ...
而不是... |& ...
)。
答案2
我思考 less
和htop
其他具有类似行为的工具使用来自‘ncurses’ 库。
无论如何,编译程序并使用 ncurses 是让程序执行您想要的操作的一种方法。还有其他版本的“curses”库。
NCURSES 编程指南
介绍
1.1. 什么是NCURSES?
您可能想知道,所有这些技术上的胡言乱语到底意味着什么。在上述情况下,每个应用程序都应该查询 terminfo 并执行必要的操作(发送控制字符等)。管理这种复杂性很快就变得困难,这催生了“CURSES”。Curses 是“光标优化”名称的双关语。Curses 库形成了一个使用原始终端代码的包装器,并提供高度灵活和高效的 API(应用程序编程接口)。它提供移动光标、创建窗口、生成颜色、使用鼠标等功能。应用程序无需担心底层终端功能。
那么 NCURSES 是什么?NCURSES 是原始 System V Release 4.0 (SVr4) curses 的克隆版本。它是一个可自由分发的库,与旧版本的 curses 完全兼容。简而言之,它是一个管理应用程序在字符单元终端上的显示的函数库。在本文的其余部分,术语 curses 和 ncurses 可互换使用。
NCURSES 的详细历史可以在源发行版的 NEWS 文件中找到。当前软件包由 Thomas Dickey 维护。您可以通过以下方式联系维护人员:[电子邮件保护]。
1.2. 我们能用 NCURSES 做什么
NCURSES 不仅创建了终端功能的包装器,还提供了一个强大的框架,用于在文本模式下创建美观的 UI(用户界面)。它提供了创建窗口等的功能。它的姊妹库面板、菜单和表单为基本的 curses 库提供了扩展。这些库通常与 curses 一起提供。可以创建包含多个窗口、菜单、面板和表单的应用程序。窗口可以独立管理,可以提供“可滚动性”,甚至可以隐藏。
菜单为用户提供了简单的命令选择选项。表单允许创建易于使用的数据输入和显示窗口。面板扩展了 ncurses 处理重叠和堆叠窗口的功能。
这些只是我们能用 ncurses 做的一些基本事情。随着我们继续前进,我们将看到这些库的所有功能。
编辑:
谢谢 Raffa,你帮助我们找到了如何在 shellscripts 中实现这一点的方法:使用tput
:-)
tput smcup
保存屏幕内容tput rmcup
恢复屏幕内容我发现一个包含指示 curses 库的
htop
我发现一个包含指示 curses 库的
tput
Raffa 发现步骤表明
less
也使用了 curses 库
请参阅此链接:
https://github.com/openbsd/src/blob/master/usr.bin/tput/tput.c
#include <curses.h>
答案3
你怎么能其他程序以这种方式运行?
在 Bash 中
你可以将提示符下输入的内容读入一个变量i
,read
并在读取 1 个字符后让其返回-n 1
,并禁用终端中字符的回显,-s
如下所示:
read -s -n 1 i
然后在循环中使用它,while
如下所示:
#!/bin/bash
while read -s -n 1 i; do
case "$i" in
q) exit
;;
*) echo "Enter q to exit or any other key to print this message again."
;;
esac
done
或者像这样:
#!/bin/bash
while read -s -n 1 i; do
if [ "$i" == "q" ]; then
exit
else
echo "Enter q to exit or any other key to print this message again."
fi
done
如果您将上述代码作为程序运行,即从脚本文件运行,它将恢复控制台,但如果您直接在终端中粘贴并运行它,那么调用exit
将关闭您的终端,而您并不希望这样……所以在这种情况下使用break
调用而不是exit
像这样:
while read -s -n 1 i; do
if [ "$i" == "q" ]; then
break
else
echo "Enter q to exit or any other key to print this message again."
fi
done
或者,如果您需要使用它来读取其他输入...那么,您可以像这样read
将它与 bash 的内置功能一起使用:bind
#!/bin/bash
# Bind the "q" key to run "quit_function" when pressed.
bind_q () { bind -x '"q": quit_function' 2> /dev/null; }
# Unbind the "q" key.
unbind_q () { bind -r "q" 2> /dev/null; }
# Run "unbind_q" then exit.
quit_function () { unbind_q; exit; }
# Start the key binding.
bind_q
while read -e -p "Enter two numbers separated by space to calculate their sum or \"q\" to quit: " num1 num2; do
if [[ $num1 =~ ^[0-9]+$ ]] && [[ $num2 =~ ^[0-9]+$ ]]; then
echo "The sum of $num1 + $num2 is: $(($num1+$num2))"
else
echo "You entered $num1 $num2"
fi
done
有趣的是,@sudodus(谢谢@sudodus) 指出了一个我完全忽略的方面...这实际上是“恢复控制台”,即恢复到程序运行之前的状态...为了满足这个条件,可以研究一个有趣的实用程序,叫做tput
由ncurses-bin 软件包您可以使用它来启动一个新的/辅助终端屏幕,如下所示:
tput smcup
然后运行您想要的任何程序/命令...完成后删除新的/辅助屏幕(及其所有内容)以返回到您原来的终端屏幕(如您离开时一样),如下所示:
tput rmcup
q这可以在 while 循环退出时使用,如下所示(我希望足够接近):
#!/bin/bash
tput smcup # Start a new screen and hide the original one.
while read -s -n 1 i; do
if [ "$i" == "q" ]; then
tput rmcup # Remove the new screen and show the original one.
exit
else
echo "You typed $i"
echo "Enter q to exit or any other key to print this message again."
fi
done
然而,这变得有点复杂,有点违背了简化事情的目的……所以让我们再举一个模仿命令行文本编辑器某些方面的例子来分解它less
(虽然没有less
里面)... 下面的脚本应该能够一次读取 10 行文本文件,并使您能够向前和向后移动,即每次 10 行在两个方向上扫描文件... 为了这个例子,我将使用该/var/lib/dpkg/status
文件(是的,我喜欢在空闲时间阅读它)但是,您可以选择另一个您喜欢的文本文件。
将文件的总行数读入变量,如下所示(可以通过多种方式实现,例如
cat file | wc -l
):tlnum=$(awk 'END {print NR}' /var/lib/dpkg/status)
设置最大行数限制(这样就不会超过文件中的总行数),如下所示:
mlnum=$((tlnum-10))
添加逻辑并将其全部放入脚本中,如下所示(请记住,这只是一个为了科学而快速酝酿的例子,因此非常感谢提出改进建议):
#!/bin/bash
file="/var/lib/dpkg/status"
info () {
clear -x
echo "You typed $i"
echo "Enter o to open the file."
echo "Enter n for next 10 lines."
echo "Enter p for previous 10 lines"
echo "Enter q to exit or any other key to print this message again."
}
tput smcup
info
while read -e -s -n 1 i; do
case "$i" in
o) tlnum=$(awk 'END {print NR}' "$file")
mlnum=$((tlnum-10))
clnum=1
info
echo "---------- Line number $clnum"
awk -v clnum="$clnum" 'NR >= clnum && NR <= clnum+10 {print }' "$file"
echo "----------"
;;
n) [ "$clnum" -lt "$mlnum" ] && clnum=$((clnum+10))
info
echo ">>>>>>>>>> Line number $clnum"
awk -v clnum="$clnum" 'NR >= clnum && NR <= clnum+10 {print }' "$file"
echo "----------"
;;
p) [ "$clnum" -ge 10 ] && clnum=$((clnum-10))
info
echo "<<<<<<<<<< Line number $clnum"
awk -v clnum="$clnum" 'NR >= clnum && NR <= clnum+10 {print }' "$file"
echo "----------"
;;
q) tput rmcup
exit
;;
*) info
;;
esac
done