Reputation: 3
I want something where I can do {some command} | red
and it'll output whatever {some command}
would have outputted, but red. The best I've been able to do is
function red()
{
while read text
do
echo -e '\033[31m'$text'\033[0m'
done
}
But this removes all the indentation.
This seems like something that should be easy but I just can't seem to figure it out. I was trying to do it just for fun but now I just need to know, 'cause there has to be a simple way to do this that I'm missing.
EDIT:
I've also tried this in C like so:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* buffer = malloc(8);
int size = 8;
int index = 0;
while ((buffer[index++] = getchar()) != EOF)
{
if (index == size)
{
size *= 2;
buffer = realloc(buffer, size);
}
}
buffer[index] = 0;
printf("%s%s%s", "\\033[31m", buffer, "\\033[0m");
}
But the shell doesn't interpret the escape characters, and so doesn't make the output red.
Upvotes: 0
Views: 102
Reputation: 22227
Writing the escape codes explicitly is IMO cumbersome. I would do something like
red() {
# Send stdin to stdout, but coloured in red, if the
# terminal supports it.
if tput setaf 196 2>/dev/null
then
cat
tput sgr0 # reset the attribute
else
# No support for changing the colour. Just display it
# using the default foreground colour
cat
fi
}
which also works on terminals which are not configured for color.
The magical number 196 comes from this table.
Upvotes: 1
Reputation: 19575
It was only missing a couple things for it to work:
IFS
environment variable, because it was the cause of your discarded indentation. This variable caused the read operation to eat leading tabs and spaces as field separator.read -r
. The -r
flag prevent the interpretation of escaped characters and have a raw read instead.echo -e
. It is a horrible non-standard way of printing \
escaped characters. Use a printf
instead.read
will not handle the last line of text if it does not contain an newline character \n
. The shell read
returns false
in this case after reading the remaining text, and break out of the while
loop without processing the last line not ending with a newline.line
contains a last line of text and print it without a newline for this special last case.Now here it is all fixed and working as you intended to:
#!/usr/bin/env bash
function red() {
while IFS= read -r line; do
printf '\e[31m%s\e[0m\n' "$line"
done
[ -n "$line" ] && printf '\e[31m%s\e[0m' "$line"
}
red
EDIT:
As @Thsutton commented above, you can use cat
in the red
function to insert the ANSI CSI sequences before and after the whole text only, rather than for each line of text. It does not need a while loop, so will undoubtedly be more performant. Although note that the per-line method might give better results if the text stream itself contains ANSI CSI sequences changing the terminal colour.
red() {
printf '\e[31m'
cat
printf '\e[0m'
}
Upvotes: 1