【问题标题】:erasing terminal output on linux在Linux上擦除终端输出
【发布时间】:2011-01-13 13:37:30
【问题描述】:

我正在编写一个带有状态栏的命令行程序,很像 wget。

我面临的主要问题是:如何删除已经发送到 stdout/stderr 的内容?

我的想法是:使用退格字符 '\b' 并删除我发送的输出。这是最好的方法吗?这是唯一的方法吗?有没有更好的办法?

PS:我不想使用像 ncurses 这样的东西。请使用普通的旧 C。

谢谢


编辑:

我也可以上升和/或下降吗?示例:我有 10 行输出,我想将第 3 行从 Doing ABC 更改为 ABC: Done。我该怎么做?

另外,谁能发布更多关于什么是 VT102 字符的详细信息?它的能力是什么?如果您有任何链接,请在此发布良好的链接。

谢谢

【问题讨论】:

标签: c linux terminal stdout


【解决方案1】:

基本的格式控制字符是退格 (\b)、制表符 (\t)、换行 (\n) 和回车 (\r)。如果您需要更多,那么您可以使用 ANSI X3.64 / ISO/IEC 6429 / ECMA-48 转义序列;大多数现代终端和模拟器至少可以识别the VT100 subset。使用 ncurses 的一个优点是它会查找特定终端的功能,因此即使您的终端使用不同的转义序列集,它也能正常工作。

【讨论】:

    【解决方案2】:

    您必须记住,就常规的 stdio 例程而言,stdout 只是一个字节流,没有固有的显示特性;这取决于目标设备,它可以是任何东西,从普通的 VT100 式终端到硬拷贝终端,再到单张纸打印机,再到绘图仪等等。

    IMO,您最好使用像 ncurses 这样的库,而不是尝试将您自己的显示管理代码与 VT100 转义代码一起破解,即使对于像这样相对简单的任务也是如此。我知道你想坚持使用“plain old C”,但这是一项超出plain old C 范围的任务。

    【讨论】:

    • 你怎么知道输出的 tty 理解 vt100 代码?同意 curses(3)(或变体)库。对于这样的事情,这很简单
    【解决方案3】:

    使用 '\r' 返回到行首并可能重写整行。

    寻找 VT102 控制序列 - 这些是字符序列 ESC ... 来控制您的终端。

    【讨论】:

    • 你的回答完全解决了我原来的问题,虽然我很想诅咒,现在我已经阅读了约翰的回答..
    【解决方案4】:

    还有可能使用Ncurses,这是一个Textual UI 库,这种行为应该有一些支持。但是,这样的事情可能有点过头了。

    【讨论】:

    • 尽管您的答案是正确的,但提问者决定他们宁愿不使用它。
    【解决方案5】:

    您自己的解决方案略有不同:

    您还可以打印一个回车符 (\r),它会将您返回到行首。

    【讨论】:

      【解决方案6】:

      这是一个 bash 的进度条。

      function gauge()
      {
              progress="$1"
              total="$2"
              width=`tput cols`
              let gwidth=width-7
      
              if [ "$total" == "0" ]; then
                      percent=100
              else
                      set +e
                      let percent=progress*100/total;
                      set -e
              fi
      
              set +e
              let fillcount=percent*gwidth/100
              let nofillcount=gwidth-fillcount
              set -e
      
              fill="";
              if [ "$fillcount" -gt "0" ]; then
                      for i in `seq $fillcount`; do
                              fill="$fill""|"
                      done
              fi;
              nofill=""
              if [ "$nofillcount" -gt "0" ]; then
                      for i in `seq $nofillcount`; do
                              nofill="$nofill"" ";
                      done
              fi
              echo -e -n "\r[""$fill""$nofill""] ""$percent""%";
      }
      

      【讨论】:

      • 不想要 bash。想要c。没有狂欢。只有c。 c.
      • 或者……你可以阅读它,然后思考你阅读的内容。您可能会意识到它会告诉您它是如何工作的,并且稍微考虑一下您就可以提出自己的 c 实现。只是一个想法。
      【解决方案7】:

      关于进度条:像这样?

      #include <stdio.h>
      #include <unistd.h>
      
      typedef enum
      {
          false=0,
          true=!false
      } bool;
      
      typedef struct
      {
          /* Start delimiter (e.g. [ )*/
          char StartDelimiter;
          /* End Delimiter (e.g. ] )*/
          char EndDelimiter;
          /* Central block (e.g. = )*/
          char Block;
          /* Last block (e.g. > ) */
          char CurBlock;
          /* Width of the progress bar (in characters) */
          unsigned int Width;
          /* Maximum value of the progress bar */
          double Max;
          /* True if we have to print also the percentage of the operation */
          bool PrintPercentage;
          /* True if the bar must be redrawn;
             note that this must be just set to false before the first call, the function then will change it by itself.  */
          bool Update;
      } ProgressBarSettings;
      
      /* Prints/updates the progress bar */
      void PrintProgressBar(double Pos, ProgressBarSettings * Settings);
      /* Inits the settings of the progress bar to the default values */
      void DefaultProgressBar(ProgressBarSettings * Settings);
      
      int main()
      {
          int i;
          /* Init the bar settings */
          ProgressBarSettings pbs;
          DefaultProgressBar(&pbs);
          pbs.Max=200;
          pbs.Width=60;
          printf("Progress: ");
          /* Show the empty bar */
          PrintProgressBar(0,&pbs);
          for(i=0;i<=pbs.Max;i++)
          {
              /* Wait 50 msec */
              usleep(50000);
              /* Update the progress bar */
              PrintProgressBar(i,&pbs);
          }
          puts(" Done");
          return 0;
      }
      
      /* Inits the settings of the progress bar to the default values */
      void DefaultProgressBar(ProgressBarSettings * Settings)
      {
          Settings->StartDelimiter='[';
          Settings->EndDelimiter=']';
          Settings->Block='=';
          Settings->CurBlock='>';
          Settings->PrintPercentage=true;
          Settings->Update=false;
          Settings->Max=100;
          Settings->Width=40;
      }
      
      /* Prints/updates the progress bar */
      void PrintProgressBar(double Pos, ProgressBarSettings * Settings)
      {
          /* Blocks to print */
          unsigned int printBlocks=(unsigned int)(Settings->Width*Pos/Settings->Max);
          /* Counter */
          unsigned int counter;
          /* If we are updating an existing bar...*/
          if(Settings->Update)
          {
              /* ... we get back to its first character to rewrite it... */
              for(counter=Settings->Width+2+(Settings->PrintPercentage?5:0);counter;counter--)
                  putchar('\b');
          }
          else
              Settings->Update=true; /* next time we'll be updating it */
          /* Print the first delimiter */
          putchar(Settings->StartDelimiter);
          /* Reset the counter */
          counter=Settings->Width;
          /* Print all the blocks except the last; in the meantime, we decrement the counter, so in the end we'll have
             the number of spaces to fill the bar */ 
          for(;printBlocks>1;printBlocks--,counter--)
              putchar(Settings->Block);
          /* Print the last block; if the operation ended, use the normal block, otherwise the one for the last block */
          putchar((Settings->Max==Pos)?Settings->Block:Settings->CurBlock);
          /* Another block was printed, decrement the counter */ 
          counter--;
          /* Fill the rest of the bar with spaces */
          for(;counter;counter--)
              putchar(' ');
          /* Print the end delimiter */
          putchar(Settings->EndDelimiter);
          /* If asked, print also the percentage */
          if(Settings->PrintPercentage)
              printf(" %3d%%",(int)(100*Pos/Settings->Max));
          /* Flush the output buffer */
          fflush(stdout);
      };
      

      注意:unistd.h 和 usleep 只是为了伪造一个操作的进度,进度条码本身只是使用标准库。它对输出流的唯一假设是 \b 实际上到达了前一个写入的字符。我在 Windows 和 Linux 上成功尝试过(使用 gnome-terminal),不知道它是否不能在某些终端模拟器上正常工作。 抱歉,cmet 数量过多,我是为另一个论坛写的,我需要向 C 新手解释每一行代码。

      【讨论】:

        猜你喜欢
        • 2014-11-08
        • 2013-06-05
        • 2010-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-19
        • 1970-01-01
        • 2013-05-09
        相关资源
        最近更新 更多