
stderr
나는 빨간색이 아닌 다른 색상으로 인쇄 되도록 터미널을 설정하고 싶습니다 stdout
. 이렇게 하면 둘을 더 쉽게 구별할 수 있습니다.
에서 구성하는 방법이 있나요 .bashrc
? 그렇지 않다면 이것이 가능합니까?
노트: 이 문제는 병합되었습니다.다른필요하다 stderr
,stdout
사용자 입력을 에코합니다.에서 출력 예정3가지 색상. 대답은 두 질문 중 하나일 수 있습니다.
답변1
확인하다stderred
. 터미널로 전송된 모든 출력에 색상을 지정하기 위해 호출을 LD_PRELOAD
연결하는 데 사용됩니다 . (기본값은 빨간색입니다.)libc
write()
stderr
답변2
이건 더 어려운 버전이에요화면에 stderr만 표시하고 stdout 및 stderr을 파일에 씁니다..
터미널에서 실행되는 애플리케이션은 단일 채널을 사용하여 통신합니다. 애플리케이션에는 stdout 및 stderr라는 두 개의 출력 포트가 있지만 둘 다 동일한 채널에 연결되어 있습니다.
채널 중 하나를 다른 채널에 연결하고 해당 채널에 색상을 추가한 다음 두 채널을 병합할 수 있지만 이로 인해 두 가지 문제가 발생합니다.
- 병합된 출력은 리디렉션이 없는 경우와 정확히 동일한 순서가 아닐 수 있습니다. 이는 채널 중 하나에 추가된 처리에 (약간) 시간이 걸리기 때문에 색상 채널이 지연될 수 있기 때문입니다. 버퍼링이 있으면 장애가 더욱 악화됩니다.
- 터미널은 색상 변경 이스케이프 시퀀스를 사용하여 표시 색상을 결정합니다(예:
␛[31m
"빨간색 전경으로 전환"을 의미). 즉, stderr에 대한 일부 출력이 표시될 때 stdout에 대한 일부 출력이 도착하면 출력의 색상이 잘못됩니다. (더 나쁜 것은 이스케이프 시퀀스 중간에 채널 스위치가 있으면 쓰레기가 표시된다는 것입니다.)
원칙적으로 두 ptys1을 동시에 수신하고(즉, 다른 채널의 출력을 처리하는 동안 한 채널의 입력을 받아들이지 않고) 적절한 색상 변경 명령을 사용하여 즉시 터미널에 출력하는 프로그램을 작성하는 것이 가능합니다. 터미널과 상호 작용하는 프로그램을 실행할 수 없게 됩니다. 이 방법의 구현에 대해서는 모르겠습니다.
write
또 다른 가능한 방법은 로드된 라이브러리에서 시스템 호출을 호출하는 모든 libc 함수를 연결하여 프로그램이 올바른 색상 변경 순서를 출력하도록 만드는 것입니다.LD_PRELOAD
. 바라보다시길의 답변기존 구현의 경우 또는Stefan Chazeras의 답변혼합 방법의 사용 strace
.
실제로, 해당되는 경우 stderr를 stdout으로 리디렉션하고 패턴 기반 셰이더로 파이핑하는 것이 좋습니다.아야오또는다중 꼬리또는 다음과 같은 특수 목적 셰이더컬러 gcc또는컬러 생산.
의사 터미널 1 개. 버퍼링으로 인해 파이프라인이 작동하지 않습니다. 소스가 버퍼에 쓸 수 있으며 이로 인해 셰이더와의 동기화가 중단됩니다.
답변3
절반의 경우 터미널 드라이버(로컬 에코 포함)에 의해 출력되기 때문에 사용자 입력 색상을 지정하는 것은 어렵습니다. 따라서 이 경우 해당 터미널에서 실행 중인 애플리케이션은 텍스트를 입력하고 출력을 변경하려고 할 때 사용자가 어디에 있는지 알 수 없습니다. 그에 따라 색상을 지정합니다. (커널에 있는) 의사 터미널 드라이버만이 특정 키를 누를 때 일부 문자를 보내는 것을 알고 있으며(xterm과 같은 터미널 에뮬레이터) 터미널 드라이버는 에코를 위해 일부 문자를 다시 보낼 수 있지만 xterm은 로컬 에코인지 여부를 알 수 없습니다. 또는 의사 터미널 슬레이브에 대한 애플리케이션 출력).
그런 다음 터미널 드라이버에 에코를 하지 말라고 지시하는 또 다른 모드가 있지만 이번에는 애플리케이션이 무언가를 출력합니다. 사용자 입력을 에코하는 대신 애플리케이션(예: gdb, bash 등과 같은 readline을 사용하는 애플리케이션)은 이를 stdout 또는 stderr로 보낼 수 있으며, 이는 다른 항목에 대해 출력하는 것과 구별하기 어려울 수 있습니다.
그런 다음 응용 프로그램의 표준 출력과 표준 오류를 구별하기 위해 여러 가지 방법이 있습니다.
이들 중 다수에는 stdout 및 stderr 명령을 파이프로 리디렉션하고 이러한 파이프를 읽어 색상을 지정하는 응용 프로그램이 포함됩니다. 여기에는 두 가지 문제가 있습니다.
- stdout이 더 이상 터미널(예: 파이프)이 아닌 경우 많은 응용 프로그램은 출력 버퍼링을 시작하기 위해 동작을 조정하는 경향이 있습니다. 즉, 출력이 큰 덩어리로 표시됩니다.
- 두 파이프를 모두 처리하는 동일한 프로세스라도 응용 프로그램이 stdout 및 stderr에 쓴 텍스트의 순서가 유지된다는 보장은 없습니다. 둘 다) 응용 프로그램이 stdout 및 stderr에 작성한 텍스트 순서를 유지하여 읽기를 시작할지 여부입니다.
또 다른 접근 방식은 표준 출력과 표준 입력 모두에 색상이 지정되도록 응용 프로그램을 수정하는 것입니다. 이는 종종 불가능하거나 비실용적입니다.
그런 다음 (동적으로 연결된 애플리케이션의 경우) 트릭은 다음 $LD_PRELOAD
과 같은 것을 사용하여 하이재킹하는 것일 수 있습니다.시길의 답변) 응용 프로그램이 무언가를 출력하기 위해 호출하고 stderr 또는 stdout에 무언가를 출력할지 여부에 따라 전경색을 설정하는 코드를 포함하는 출력 함수입니다. 그러나 이는 C 라이브러리와 애플리케이션에서 직접 호출하는 시스템 호출을 수행하는 다른 write(2)
라이브러리에서 가능한 모든 기능을 하이재킹하면 결국 stdout 또는 stderr(printf, puts, perror... .)에 무언가를 작성하게 될 수 있음을 의미합니다. , 이로 인해 동작이 변경될 수 있습니다.
strace
또 다른 접근 방식은 시스템 호출이 호출될 때마다 PTRACE 트릭을 사용하여 파일 설명자가 파일 설명자 1 또는 2에 있는지 여부에 따라 출력 색상을 설정하는 것입니다.gdb
write(2)
write(2)
그러나 이것은 꽤 큰 문제입니다.
내가 방금 사용한 한 가지 트릭은 LD_PRELOAD를 사용하여 자신을 하이재킹하고(모든 시스템 호출 전에 자신을 연결하는 더러운 작업을 수행) fd 1 또는 2가 감지 strace
되는지 여부에 따라 달라지도록 지시하는 것입니다 .write(2)
소스 코드를 보면 strace
모든 출력이 함수를 통해 수행되는 것을 볼 수 있습니다 vfprintf
. 우리가 해야 할 일은 기능을 가로채는 것뿐입니다.
LD_PRELOAD 래퍼는 다음과 같습니다:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
int vfprintf(FILE *outf, const char *fmt, va_list ap)
{
static int (*orig_vfprintf) (FILE*, const char *, va_list) = 0;
static int c = 0;
va_list ap_orig;
va_copy(ap_orig, ap);
if (!orig_vfprintf) {
orig_vfprintf = (int (*) (FILE*, const char *, va_list))
dlsym (RTLD_NEXT, "vfprintf");
}
if (strcmp(fmt, "%ld, ") == 0) {
int fd = va_arg(ap, long);
switch (fd) {
case 2:
write(2, "\e[31m", 5);
c = 1;
break;
case 1:
write(2, "\e[32m", 5);
c = 1;
break;
}
} else if (strcmp(fmt, ") ") == 0) {
if (c) write(2, "\e[m", 3);
c = 0;
}
return orig_vfprintf(outf, fmt, ap_orig);
}
그런 다음 다음과 같이 컴파일합니다.
cc -Wall -fpic -shared -o wrap.so wrap.c -ldl
다음과 같이 사용하십시오.
LD_PRELOAD=/path/to/wrap.so strace -qqf -a0 -s0 -o /dev/null \
-e write -e status=successful -P "$(tty)" \
env -u LD_PRELOAD some-cmd
some-cmd
로 대체하면 bash
bash 프롬프트와 입력한 내용이 빨간색(stderr)으로 표시되고 zsh
대체 항목은 검정색으로 표시됩니다(zsh가 프롬프트와 에코를 표시하기 위해 stderr를 새 fd에 복사하기 때문입니다).
예상하지 못한 응용 프로그램(예: 색상을 사용하는 응용 프로그램)에서도 놀라울 정도로 잘 수행되는 것 같습니다.
strace
셰이딩 모드는 터미널로 가정된 stderr에 출력됩니다. 를 사용하면 -P "$(tty)"
stdout/stderr이 리디렉션된 경우와 같이 터미널로 전송되지 않은 쓰기에 대해 이 작업을 피할 수 있습니다.
이 솔루션에는 다음과 같은 제한 사항이 있습니다.
- 본질적인 것 : 성능 문제, 또는
strace
그 안에서 다른 PTRACE 명령을 실행할 수 없음 , setuid/setgid 문제strace
gdb
- 색상은
write
각 개별 프로세스의 stdout/stderr에 기반합니다. 예를 들어 에서는sh -c 'echo error >&2'
다음 으로 출력되므로error
녹색이 됩니다 .echo
그것은stdout (sh는 sh의 stderr로 리디렉션되지만 strace가 보는 모든 것은 a입니다write(1, "error\n", 6)
).
2021년 10월에 수정됨. 이 래퍼는 strace 5.10, glibc 2.32, gcc 10.30.0을 사용하는 불안정한 Debian에서 더 이상 작동하지 않습니다. 래핑해야 할 함수 __vfprintf_chk
와 찾아야 할 형식이 변경되었기 때문입니다. 래퍼를 다음으로 변경해야 합니다.
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
int __vfprintf_chk(FILE *outf, int x, const char *fmt, va_list ap)
{
static int (*orig_vfprintf) (FILE*, int, const char *, va_list) = 0;
static int c = 0;
va_list ap_orig;
va_copy(ap_orig, ap);
if (!orig_vfprintf) {
orig_vfprintf = (int (*) (FILE*, int, const char *, va_list))
dlsym (RTLD_NEXT, "__vfprintf_chk");
}
if (strcmp(fmt, "%d") == 0) {
int fd = va_arg(ap, long);
switch (fd) {
case 2:
write(2, "\e[31m", 5);
c = 1;
break;
case 1:
write(2, "\e[32m", 5);
c = 1;
break;
}
} else if (strcmp(fmt, "= %lu") == 0) {
if (c) write(2, "\e[m", 3);
c = 0;
}
return orig_vfprintf(outf, x, fmt, ap_orig);
}
이 접근 방식은 문서화되지 않은 불안정한 API(실제 API가 아님)를 사용하기 때문에 약간 취약하다는 것을 보여줍니다.
답변4
이것은 제가 얼마 전에 했던 개념 증명입니다.
zsh에서만 작동합니다.
# make standard error red
rederr()
{
while read -r line
do
setcolor $errorcolor
echo "$line"
setcolor normal
done
}
errorcolor=red
errfifo=${TMPDIR:-/tmp}/errfifo.$$
mkfifo $errfifo
# to silence the line telling us what job number the background job is
exec 2>/dev/null
rederr <$errfifo&
errpid=$!
disown %+
exec 2>$errfifo
또한 setcolor라는 함수가 있다고 가정합니다.
단순화된 버전:
setcolor()
{
case "$1" in
red)
tput setaf 1
;;
normal)
tput sgr0
;;
esac
}