AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / computer / 问题 / 1547549
Accepted
loopbackbee
loopbackbee
Asked: 2020-05-01 19:45:59 +0800 CST2020-05-01 19:45:59 +0800 CST 2020-05-01 19:45:59 +0800 CST

在跟随模式/滚动过去缓冲区结束后取消阻止“更少”

  • 772

我less用来捕捉直播节目的输出。使用F激活跟随模式,显示实时输出。但是,在进入跟随模式后(或者,更一般地说,滚动经过缓冲的输出,G例如使用 ),我无法再控制less了。

Ctrl-c在这里工作,但不幸的是它杀死了另一个程序。

有没有办法less在滚动缓冲区后重新获得控制?

less pipe
  • 1 1 个回答
  • 202 Views

1 个回答

  • Voted
  1. Best Answer
    Kamil Maciorowski
    2020-05-02T06:31:44+08:002020-05-02T06:31:44+08:00

    简单案例

    在我的 Debian 9 中,如果我运行less以读取常规文件,例如:

    less /etc/fstab
    

    然后通过F(ie Shift+ F) 然后我会看到

    Waiting for data... (interrupt to abort)
    

    这表明中断在设计上是“撤消”的正确方法F。确实Ctrl+C做到了这一点。


    你的情况

    我less用来捕捉直播节目的输出。

    我猜是这样的:

    program1 | less
    # or
    program1 | program2 | … | less
    

    现在问题出现了(Ctrl+C杀死了另一个程序),因为发生了以下情况:

    1. shell 将管道的每个部分(即less,每个programN)放在一个进程组中,其 PGID(进程组 ID)等于第一个命令(即)的 PI​​D(进程 ID program1)。
    2. shell 通知终端这个 PGID 现在是前台进程组。
    3. 当您点击Ctrl+C时,终端发送SIGINT到前台进程组。
    4. 实际上less ,每次programN接收SIGINT。

    请参阅此答案和此博客条目。这个问题中有一个与此相关的有趣案例。

    一些非常基本的 shell(例如posh)可能会运行shell 本身的进程组中的所有内容。这不会影响问题(less 以及每次programN接收SIGINT),但会使某些解决方案无效。您的外壳很可能“足够复杂”。在我的 Debian 中,甚至shfrombusybox已经足够复杂了。


    解决方案

    您想要less且less仅接收SIGINT. 可能性:

    1. 仅发送SIGINT至less:

       kill -s SIGINT $pid_of_less
      

    where$pid_of_less表示正确less进程的 PID(您需要以某种方式找到它)。如果您确定less唯一less允许您发出信号的权利,或者如果您不介意向其他less进程发出信号,那么这是最简单的方法:

        killall -s SIGINT less
    

    此解决方案应始终有效。它不需要您事先修改管道。

    您可以在单独的外壳(单独的控制台)中运行kill(或killall)或(如果您的外壳允许这样做)使用Ctrl+挂起管道Z,运行您需要的任何内容,最后使用fg.

    1. 让一切都less免疫SIGINT。几种可能:

       sh -c 'trap "" SIGINT; program1' | less
       sh -c 'trap "" SIGINT; exec program1' | less
       sh -c 'trap "" SIGINT; program1 | program2 | …' | less
      
       ( trap "" SIGINT; program1 ) | less
       ( trap "" SIGINT; exec program1 ) | less
       ( trap "" SIGINT; program1 | program2 | … ) | less
      

    注意事项和怪癖:

    • sh -c …需要额外引用级别的示例。如果您的原始行中有引号,它们可能会带来不便。如果在主 shell 中应该扩展任何内容,那么单引号不能包含它(外引号很重要)。它可能会变得复杂。

    • sh -c …与require 语法兼容的示例sh。如果您的原始行使用sh无法理解的语法或工具(如内置),那么您应该考虑其他可能性。

    • 带有子外壳 ( ) 的示例( … )使用您使用的任何外壳。它很可能会trap "" SIGINT根据需要理解和工作,但一般情况下可能不会。

    • 每个都programN可以自己注册一个处理程序SIGINT。注意less做到了,所以我们甚至可以从一个SIGINT被忽略的shell启动它:

           ( trap "" SIGINT; program1 | less )
           # similarly with other examples
      

      它仍然能够对Ctrl+做出反应C。但是,如果另一个程序再次使自己变得脆弱,那么它将破坏目的。

    • AnyprogramN可能不会在您退出时终止less。可能是因为

      • 它还不知道它写入的管道已关闭,因为自从管道关闭以来它没有尝试写入任何内容,因此SIGPIPE尚未生成(这很正常,比较我的这个答案);
      • 或按设计它不会退出SIGPIPE;
      • 或者它是一个循环,它运行确实 exit on 的命令SIGPIPE,但循环并不关心并一次又一次地运行它们。

      在这种情况下,最简单的恢复方法是点击Ctrl+ C,但显然如果你对此programN免疫,SIGINT那么这将不起作用。您将需要使用SIGTERM其他信号或其他信号将其杀死。请注意,仅杀死子外壳或附加sh外壳不会杀死其子外壳。向(子)shell 的整个进程组发出信号是一个好方法,如下所示:

           kill -- -$pgid
      

      where$pgid表示正确的进程组 ID(-就在之前告诉kill目标一个组,而不仅仅是一个进程)。$pgid如果子shell首先在管道中,则将是(子)shell的PID。

      如果在交互式 shell 中启用了作业控制,则不需要知道任何 ID。例如在 Bash 中点击Ctrl+Z然后kill %+。这应该会杀死整个工作。

    1. 将所有内容都less放在前台进程组之外。

    2. 与setsid:

          setsid -w program1 | less
          # or
          setsid -w sh -c 'program1 | program2 | …' | less
      

      和以前一样,任何退出programN时都可能不会终止。less和以前一样,如果发生这种情况,您将需要用信号杀死它。不同的是,这一次SIGINT可以是信号。另一方面,这个技巧kill %-不起作用。

    3. 使用进程替换,如果你的 shell 支持的话。我将为 Bash 详细说明。

          # in Bash
          less -f <(program1)
          # or
          less -f <(program1 | program2 | …)
      

      不幸的是,当您退出时, anyprogramN可能不会终止。less但是请注意<( )原始 shell 的进程组中的所有内容,因此一旦 shell 再次将其自己的进程组注册为前台组,您就可以使用+发送SIGINT给它。在脚本中,这可能是个问题,但在交互式 shell 中,当您在退出后得到提示时按+就足够了。是的,在这种情况下+将发送到一些似乎在后台工作的进程(但它们属于当前的前台进程组,所以正式地它们在前台)。CtrlCCtrlClessCtrlCSIGINT

      您可以自动执行此操作:kill 0将向其自己的进程组发出信号,因此kill在 shell 的进程组中运行就足够了,这非常容易。像这样:

          # in Bash
          less -f <(program1); kill -s SIGINT 0
          # or
          less -f <(program1 | program2 | …); kill -s SIGINT 0
      

      上面的解决方案非常优雅。请记住kill,此处仅用于指示本来会保留的程序。如果您的程序无论如何都自行退出,那么您可能根本无法运行kill。另一方面,如果shell 的进程组中有其他exec 3< <(whatever)程序(例如,你做了),那么kill 0也会向它们发出信号。

      关于 Zsh 的注意事项:我的测试表明,在 Zsh 中您可以使用less -f <( … )并阻止Ctrl+C发送SIGINT到程序,就像在 Bash 中一样;但是 Zsh 将在另一个进程组(不是 shell 的进程组)中运行它们,所以这个技巧kill 0不起作用。

      另请注意,这不起作用:

          # not a solution
          less < <(program1)
          # or
          less < <(program1 | program2 | …)
      

      因为这一次里面的所有东西都<( )将正式下降less并共享它的进程组,这将是前台进程组,它将SIGINT在Ctrl+上接收C。之所以会发生这种正式的后代,less是因为less最初是作为一个子shell 开始的,它为自己设置重定向,然后才将自己替换为less(这是shell 运行程序的正常方式)。因此,在进程树中,它会出现less一个产生的外壳program1等(比较这个问题,sshd 据称是产生的sleep)。

      虽然在这种情况下Ctrl+C可以到达 each programN,但 any在没有+的情况下programN退出时可能仍然不会终止。那么这个技巧就行不通了。完全不是解决办法。lessCtrlCkill 0

    4. 使用单独的调用。

      以某种方式单独调用less,因此它是前台进程组中的唯一命令,并且没有其他进程将SIGINT收到Ctrl+ C。您需要命名管道或常规文件将数据program1 …从less.

      1. 创建一个文件:

            mkfifo myfile    # of the type named fifo
            # or
            : > myfile       # of the type regular file
        
      2. 运行program1 …并让它写入文件

      • 要么在一个单独的外壳,单独的终端(不是你要运行的那个less):

                program1 … >> myfile
        
      • 或者在您要运行的终端的后台(请注意,对于运行shell 本身的进程组中的所有内容less的 shell 来说,这不是一个有效的解决方案——真正的基本 shell,还记得吗?):

                program1 … >> myfile &
        

        我用过>>,没有>。对于fifo来说没关系。对于常规文件,它允许您稍后截断文件而不会出现问题(比较这个答案)。

      1. 运行less:

            less -f myfile
            # or
            less < myfile
        

        (-f在常规文件的情况下不需要)。

        现在这个终端中的Ctrl+C只会影响less.

      2. 如果您退出less并且其余部分仍在运行,并且您希望它停止,那么

      • 如果它在单独的终端中运行,Ctrl+C那里;
      • 如果它在同一终端的后台运行: - fg,然后Ctrl+ C, - 或kill -s SIGINT %+,如果支持(如果需要,指定不同的信号), - 或kill -s SIGINT -- -"$!"(如果需要,指定不同的信号)。
      1. 删除文件:

            rm myfile
        

    笔记

    • program1在后台或内部运行<( )会将其标准输入与终端分离。less如果这将是一个问题,那么当程序尝试同时从终端读取时,您会遇到问题。您没有报告任何问题(或者已经小心并program1与终端分离),因此显然该程序没有从终端读取。因此在后台或内部运行<( )不应该破坏任何东西。
    • 在我的测试中,在没有杀死任何东西(每个解决方案)的情况下less成功“撤消”后,最终停止读取数据。F这会阻塞管道的其余部分(通常;写入programN可能被设计为不等待,它可能退出或其他)。这是意料之中的,我是为了经验不足的用户而提到这一点。要解除封锁,我需要F再次通过。但是,如果我使用常规文件作为两者之间的接口programN,less那么程序将不会因为less(文件系统可能已满)而阻塞。
    • 3

相关问题

  • `less`:多个命令,包括搜索

  • [Echo !file!|findstr] 中的管道错误

  • 如果输出是通过管道传输的,为什么 make 会忽略转义序列

  • mpv:如何播放从标准输入传输的 URL 列表

  • 如果内容可以在 less 命令中显示在一个屏幕中,则打印内容并退出

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何减少“vmmem”进程的消耗?

    • 11 个回答
  • Marko Smith

    从 Microsoft Stream 下载视频

    • 4 个回答
  • Marko Smith

    Google Chrome DevTools 无法解析 SourceMap:chrome-extension

    • 6 个回答
  • Marko Smith

    Windows 照片查看器因为内存不足而无法运行?

    • 5 个回答
  • Marko Smith

    支持结束后如何激活 WindowsXP?

    • 6 个回答
  • Marko Smith

    远程桌面间歇性冻结

    • 7 个回答
  • Marko Smith

    子网掩码 /32 是什么意思?

    • 6 个回答
  • Marko Smith

    鼠标指针在 Windows 中按下的箭头键上移动?

    • 1 个回答
  • Marko Smith

    VirtualBox 无法以 VERR_NEM_VM_CREATE_FAILED 启动

    • 8 个回答
  • Marko Smith

    应用程序不会出现在 MacBook 的摄像头和麦克风隐私设置中

    • 5 个回答
  • Martin Hope
    CiaranWelsh 如何减少“vmmem”进程的消耗? 2020-06-10 02:06:58 +0800 CST
  • Martin Hope
    Jim Windows 10 搜索未加载,显示空白窗口 2020-02-06 03:28:26 +0800 CST
  • Martin Hope
    v15 为什么通过电缆(同轴电缆)的千兆位/秒 Internet 连接不能像光纤一样提供对称速度? 2020-01-25 08:53:31 +0800 CST
  • Martin Hope
    fixer1234 “HTTPS Everywhere”仍然相关吗? 2019-10-27 18:06:25 +0800 CST
  • Martin Hope
    andre_ss6 远程桌面间歇性冻结 2019-09-11 12:56:40 +0800 CST
  • Martin Hope
    Riley Carney 为什么在 URL 后面加一个点会删除登录信息? 2019-08-06 10:59:24 +0800 CST
  • Martin Hope
    zdimension 鼠标指针在 Windows 中按下的箭头键上移动? 2019-08-04 06:39:57 +0800 CST
  • Martin Hope
    jonsca 我所有的 Firefox 附加组件突然被禁用了,我该如何重新启用它们? 2019-05-04 17:58:52 +0800 CST
  • Martin Hope
    MCK 是否可以使用文本创建二维码? 2019-04-02 06:32:14 +0800 CST
  • Martin Hope
    SoniEx2 更改 git init 默认分支名称 2019-04-01 06:16:56 +0800 CST

热门标签

windows-10 linux windows microsoft-excel networking ubuntu worksheet-function bash command-line hard-drive

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve