【问题标题】:Bash PS1 prompt breaks line wrapping when generating ANSI escapes by an external program当外部程序生成 ANSI 转义时,Bash PS1 提示符会换行
【发布时间】:2019-12-31 16:43:04
【问题描述】:

我正在考虑使用 C++ 脚本制作自定义 bash 提示符。计划是从 .bash_profile 中的 PS1 声明中调用脚本,如下所示:

PS1='$(bashprompt.out)'

这部分工作得很好。我遇到的问题是 ANSI 序列正在打印到终端。您可以在下图中看到这一点,它过早地返回到行首。

我正在以如下格式定义颜色:

#define DGRAYORANGE "\033[48;5;202;38;5;243m"

在 bash 脚本中,我知道避免此问题的方法是使用 \[ \] 将颜色表示为非打印(就像这里解释的 https://www.funtoo.org/Prompt_Magic),但这在 C++ 中不起作用。

提示应该是这样的:

这是用于制作彩色提示的 C++ 代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <string>
#include <iostream>

#define ARROW ""
#define DGRAYORANGE "\033[48;5;202;38;5;243m"
#define ORANGEDGRAY "\033[38;5;202;48;5;243m"
#define DGRAYDEFAULT "\033[38;5;243;48;5;0m"
#define WHITEDGRAY "\033[38;5;255;48;5;243m"
#define WHITEORANGE "\033[38;5;255;48;5;202m"
#define RESET "\033[0m"


using namespace std;

void PrintPrompt(string, string, string);
string FormatCWD(char *);



int main() {
    int buffersize = 256;
    char hostname[buffersize];
    char cwd[buffersize];
    struct passwd* pw;

    gethostname(hostname,sizeof(hostname));
    pw = getpwuid(getuid());
    getcwd(cwd,sizeof(cwd));

    string stringCwd = FormatCWD(cwd);
    string stringUsername = pw->pw_name;
    string stringHostname = hostname;

    PrintPrompt(stringCwd,stringHostname,stringUsername);
}

void PrintPrompt(string cwd, string hostname, string username){
    string cwdString = cwd;
    cout << WHITEDGRAY << username <<
            DGRAYORANGE << ARROW << " " <<
            WHITEORANGE << hostname <<
            ORANGEDGRAY << ARROW << " " <<
            WHITEDGRAY << cwd << DGRAYDEFAULT <<
            ARROW << RESET << " " << endl;
}

string FormatCWD(char * cwd) {
    string stringCwd = cwd;

    int size = stringCwd.length();
    int slashCount = 0;
    int slashIndex = 0;
    for (int i=0; i<size; i++) {
        if (stringCwd.at(i) == '/') {
            slashIndex = i;
            slashCount++;
        }
    }

    string outputCwd = "";
    if (slashIndex != 0) {
        for (int i=slashIndex+1; i<size; i++) {
            outputCwd += stringCwd.at(i);
        }
    }

    return outputCwd;
}

有没有办法通过将转义序列标记为非打印(如在 bash 中)来解决此问题?感谢你们提供的任何帮助。

【问题讨论】:

  • 当您说“正在将 ANSI 打印到终端”时,您是什么意思。我在图像中看到的只是一些不错的颜色。这不是你想要的吗?这张图片有什么问题?
  • 您能否展示您的代码、预期的行为以及其中哪些部分不起作用?
  • 我添加了一个提示应该是什么样子的示例。 link 此链接解释了 ANSI 序列如何需要被 \[ \] 包围,因此在打印时不考虑它们的长度。我还添加了源代码
  • 啊,所以问题是 包装没有按照您的预期工作。根据您的研究,您期望它是因为您需要非打印字符。我现在明白了。重新投票。
  • @cnmcferren 使用“c++”程序而不是 bash 脚本的任何理由。会更容易更新 - 无需编译/安装二进制文件?更不用说它更容易调试。

标签: c++ bash ansi ansi-escape


【解决方案1】:

首先请注意,\[\] 是由 Bash 本身而非控制台解释的,因此它们必须明确存在于 PS1 值中,而不是在生成实际提示的程序的输出中。

现在,您希望计算非转义字符,而不计算颜色。为此,您可以将程序修改为具有两种模式:

  • 彩色输出(用于实际提示)
  • 纯文本输出(用于字符计数)

然后你可以使用类似的东西

PS1='$(/tmp/test)\[\r$(/tmp/test c)\]'

这适用于修改为以下内容的代码:

void PrintPrompt(string cwd, string hostname, string username, bool colors){
    string cwdString = cwd;
    cout << (colors ? WHITEDGRAY  : "") << username <<
            (colors ? DGRAYORANGE : "") << ARROW << " " <<
            (colors ? WHITEORANGE : "") << hostname <<
            (colors ? ORANGEDGRAY : "") << ARROW << " " <<
            (colors ? WHITEDGRAY  : "") << cwd << (colors ? DGRAYDEFAULT : "") <<
            ARROW << (colors ? RESET : "") << endl;
}

colors 被传递为例如argc&gt;1.

我们使用\r 转义序列转到行首,使我们能够用花哨的颜色覆盖我们已经打印的非彩色文本,使 Bash 认为附加的文本和转义序列(包括回车)不要占用任何空间。

截图:

【讨论】:

    猜你喜欢
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-23
    • 2015-08-14
    • 1970-01-01
    • 2014-09-10
    • 2012-04-16
    相关资源
    最近更新 更多