c++ Bash PS1提示符在由外部程序生成ANSI转义时中断换行

lx0bsm1f  于 2023-04-01  发布在  其他
关注(0)|答案(1)|浏览(135)

我正在考虑使用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中一样)?谢谢你们的帮助。

tzcvj98z

tzcvj98z1#

首先要注意的是,\[\]是由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>1
我们使用\r转义序列转到行的开头,使我们能够用彩色文本覆盖已经打印的非彩色文本,使Bash认为额外的文本和转义序列(包括回车)不占用任何空间。
截图:

相关问题