如何让程序用“q”退出并像“man”一样恢复控制台?

pri*_*ner 29 command-line programming

man当您按下该命令时,该命令将关闭q,并将控制台恢复到以前的状态。

这个叫什么?

如何让另一个程序以这种方式运行?

Arr*_*lt3 31

man正在使用寻呼程序less来提供此功能。您可以通过管道传输命令的标准输出和标准错误流来完成相同的操作,less如下所示:

my_command_here arg1 arg2 |& less
Run Code Online (Sandbox Code Playgroud)

无论my_command_here吐出什么,都会被放入一个易于滚动的屏幕中,您可以使用 退出q。通过尝试,您可以很好地感受到它的工作效果ip address help |& less- 您可以使用箭头键和PgUp和进行滚动PgDn,然后使用 退出q

Bash 还将|&错误输出 (stderr) 重定向到 pipeline,这与普通输出 (stdout) 不同,后者|仅重定向正常输出 (stdout)。使用 时|,任何错误都会导致混乱的输出,因为它们会出现在终端上,但在 中无法滚动less。(在 中sh,您可以使用标准... 2>&1 | ...而不是... |& ...)。

  • 您能解释一下命令中的“|&”而不是“|”的作用吗? (12认同)
  • @BrunoPérel它也允许标准错误通过管道([3.2.3 Pipelines](https://www.gnu.org/software/bash/manual/html_node/Pipelines.html)) (12认同)
  • 更重要的是,“|&”是一种羞辱。它不适用于“/bin/sh”(通常)。如果您希望在可移植的 shell 脚本中使用它,请使用 `2>&1` 和普通管道。 (8认同)
  • 也许应该补充一点,按“q”,作为直接后果,只会导致“less”退出;向“less”提供数据的程序注意到管道损坏或关闭——即其标准输出上的写入错误——并且可以以任何它喜欢的方式对此做出反应。 (4认同)
  • `|&` 有点有趣,因为 stderr 的重定向发生在左侧命令中的任何(非管道)重定向之后,这与首先发生的正常管道重定向不同。例如 `(echo output; echo error >&2) 2>/dev/null |& cat` 仍然打印 `output` 和 `error`。 (3认同)
  • @BrunoPérel 管道标准错误,而不仅仅是标准输出。如果忽略它,可能会导致意外结果(无法滚动命令输出)。 (2认同)

sud*_*dus 18

认为 less和具有类似行为的其他工具使用“ncurses”库htop中的工具。

无论如何,编译程序并使用 ncurses 是让程序执行您想要的操作的一种方法。还有其他版本的“curses”库。

NCURSES 编程指南

介绍

1.1. 什么是 NCURSES?

您可能想知道,所有这些技术胡言乱语的重要性是什么。在上面的场景中,每个应用程序都应该查询 terminfo 并执行必要的操作(发送控制字符等)。管理这种复杂性很快就变得困难,这就催生了“诅咒”。Curses 是“游标优化”这个名称的双关语。Curses 库形成了原始终端代码的包装器,并提供高度灵活和高效的 API(应用程序编程接口)。它提供了移动光标、创建窗口、产生颜色、玩鼠标等功能。应用程序无需担心底层终端功能。

那么什么是 NCURSES?NCURSES 是原始 System V Release 4.0 (SVr4) 诅咒的克隆。它是一个可免费分发的库,与旧版本的curses完全兼容。简而言之,它是一个管理应用程序在字符单元终端上显示的函数库。在本文档的其余部分中,术语curses 和ncurses 可以互换使用。

NCURSES 的详细历史记录可以在源发行版的 NEWS 文件中找到。当前包由 Thomas Dickey 维护。您可以通过 bug-ncurses@gnu.org 联系维护人员。

1.2. 我们可以用 NCURSES 做什么

NCURSES 不仅创建了终端功能的包装器,而且还提供了一个强大的框架来在文本模式下创建美观的 UI(用户界面)。它提供了创建窗口等功能。它的姊妹库面板、菜单和表单提供了基本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>
Run Code Online (Sandbox Code Playgroud)


Raf*_*ffa 10

如何让另一个程序以这种方式运行?

在重击中

您可以将提示符下输入的内容读取到变量中iread并使其在读取 1 个字符后返回-n 1,并禁用在终端中回显该字符,-s如下所示:

read -s -n 1  i
Run Code Online (Sandbox Code Playgroud)

然后在循环中使用它,while如下所示:

read -s -n 1  i
Run Code Online (Sandbox Code Playgroud)

或者像这样:

#!/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
Run Code Online (Sandbox Code Playgroud)

如果您将其作为程序运行,即从脚本文件运行,上面的代码将恢复控制台,但如果您直接在终端中粘贴并运行它,则调用exit将关闭您的终端,而您不希望这样......所以使用一个break电话,而不是exit在这种情况下像这样:

#!/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
Run Code Online (Sandbox Code Playgroud)

或者,如果您需要使用read自身来读取其他输入...那么,您可以将它与内置的 bash 一起使用,bind如下所示:

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
Run Code Online (Sandbox Code Playgroud)

有趣的是@sudodus谢谢@sudodus)指出了我完全错过的一个方面...这实际上是“恢复控制台”,即恢复到程序运行之前的先前状态...为了帮助满足这个条件,一个可能会研究一个有趣的实用程序,tput它是由ncurses-bin 包提供的,您可以使用它来启动新的/辅助终端屏幕,如下所示:

tput smcup
Run Code Online (Sandbox Code Playgroud)

然后运行您想要的任何程序/命令...完成后删除新/辅助屏幕(及其所有内容)以返回到原始终端屏幕(如您离开的那样),如下所示:

tput rmcup
Run Code Online (Sandbox Code Playgroud)

这可以在退出qwhile 循环时使用,如下所示(我希望足够接近):

#!/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
Run Code Online (Sandbox Code Playgroud)

然而,这变得有点复杂,有点违背了简化事情的目的……所以让我们用另一个模仿命令行文本编辑器某些方面的例子来分解它less(虽然没有less内部)……以下脚本应该能够一次读取 10 行文本文件,并允许您向前和向后移动,即一次双向扫描文件 10 行...出于本示例的目的,我将使用该/var/lib/dpkg/status文件(是的,我喜欢在空闲时间阅读它)但是,您可以选择您喜欢的另一个文本文件。

  • 将文件中的总行数读入变量,如下所示(可以通过多种方式完成,例如cat file | wc -l):

    tlnum=$(awk 'END {print NR}' /var/lib/dpkg/status)
    
    Run Code Online (Sandbox Code Playgroud)
  • 设置最大行数限制(这样就不会超过文件中的总行数),如下所示:

    mlnum=$((tlnum-10))
    
    Run Code Online (Sandbox Code Playgroud)
  • 添加逻辑并将其全部放入像这样的脚本中(请记住,这只是一个快速编写的示例,仅供科学参考,因此非常感谢建议改进):

tput smcup
Run Code Online (Sandbox Code Playgroud)


小智 7

这个叫什么?

这称为“备用屏幕”。

这可以从 Bash 脚本中使用;请参阅 StackOverflow 中关于在 bash 脚本中使用“备用屏幕”的答案。