你如何制作一个 shell 函数,你可以通过管道将其输出为红色?
How do you make a shell function that you can pipe into to make the output red?
我想要一些我可以做的事情 {some command} | red
它会输出 {some command}
会输出的任何内容,但红色。我能做的最好的是
function red()
{
while read text
do
echo -e '3[31m'$text'3[0m'
done
}
但这会删除所有缩进。
这似乎应该很容易,但我似乎无法弄清楚。我只是为了好玩而尝试这样做,但现在我只需要知道,因为必须有一种我所缺少的简单方法来做到这一点。
编辑:
我也在 C 中这样尝试过:
#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");
}
但是 shell 不解释转义字符,因此不会使输出变为红色。
它只缺少一些东西才能工作:
- 首先你要中和I内部F场S分离器
IFS
环境变量,因为它是你丢弃缩进的原因。此变量导致读取操作将前导制表符和空格作为字段分隔符。
- 接下来您要
read -r
。 -r
标志阻止转义字符的解释,而是读取 raw。
- 最后,千万不要用
echo -e
。这是打印 \
转义字符的可怕的非标准方式。请改用 printf
。
- 此外,
read
上的循环将不会处理文本的最后一行,如果它不包含换行符 \n
。本例中的 shell read
returns false
在读取剩余文本后,跳出 while
循环而不处理最后一行不以 a 结尾的行换行符。
如果 line
包含最后一行文本并在没有换行符的情况下打印它,用于测试这种特殊的最后一种情况。
现在一切都已修复并按您的预期工作:
#!/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
编辑:
与一样,您可以在red
函数中使用cat
只在整个文本前后插入ANSI CSI序列,而不是每行文本。它不需要 while 循环,因此无疑会更高效。尽管请注意,如果文本流本身包含更改终端颜色的 ANSI CSI 序列,则每行方法可能会提供更好的结果。
red() {
printf '\e[31m'
cat
printf '\e[0m'
}
显式编写转义码在我看来很麻烦。我会做类似
的事情
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
}
也适用于未配置颜色的终端。
神奇的数字196来自thistable.
我想要一些我可以做的事情 {some command} | red
它会输出 {some command}
会输出的任何内容,但红色。我能做的最好的是
function red()
{
while read text
do
echo -e '3[31m'$text'3[0m'
done
}
但这会删除所有缩进。
这似乎应该很容易,但我似乎无法弄清楚。我只是为了好玩而尝试这样做,但现在我只需要知道,因为必须有一种我所缺少的简单方法来做到这一点。
编辑:
我也在 C 中这样尝试过:
#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");
}
但是 shell 不解释转义字符,因此不会使输出变为红色。
它只缺少一些东西才能工作:
- 首先你要中和I内部F场S分离器
IFS
环境变量,因为它是你丢弃缩进的原因。此变量导致读取操作将前导制表符和空格作为字段分隔符。 - 接下来您要
read -r
。-r
标志阻止转义字符的解释,而是读取 raw。 - 最后,千万不要用
echo -e
。这是打印\
转义字符的可怕的非标准方式。请改用printf
。 - 此外,
read
上的循环将不会处理文本的最后一行,如果它不包含换行符\n
。本例中的 shellread
returnsfalse
在读取剩余文本后,跳出while
循环而不处理最后一行不以 a 结尾的行换行符。
如果line
包含最后一行文本并在没有换行符的情况下打印它,用于测试这种特殊的最后一种情况。
现在一切都已修复并按您的预期工作:
#!/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
编辑:
与red
函数中使用cat
只在整个文本前后插入ANSI CSI序列,而不是每行文本。它不需要 while 循环,因此无疑会更高效。尽管请注意,如果文本流本身包含更改终端颜色的 ANSI CSI 序列,则每行方法可能会提供更好的结果。
red() {
printf '\e[31m'
cat
printf '\e[0m'
}
显式编写转义码在我看来很麻烦。我会做类似
的事情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
}
也适用于未配置颜色的终端。
神奇的数字196来自thistable.