Tôi có thể định cấu hình vỏ của mình để in STDERR và STDOUT với các màu khác nhau không?


63

Tôi muốn thiết lập thiết bị đầu cuối của mình lên để stderrđược in bằng một màu khác hơn stdout; có thể là màu đỏ Điều này sẽ làm cho việc phân biệt hai người dễ dàng hơn.

Có cách nào để cấu hình cái này .bashrckhông? Nếu không, điều này thậm chí có thể?


Lưu ý : Câu hỏi này đã được sáp nhập với nhau rằng yêu cầu stderr, stdout và người dùng nhập vào tiếng vang được sản xuất trong 3 màu sắc khác nhau . Câu trả lời có thể được giải quyết một trong hai câu hỏi.


1
Câu hỏi tương tự về Stack Overflow: stackoverflow.com/questions/6841143/ từ
Stéphane Gimenez

Câu hỏi thú vị + câu trả lời, tuy nhiên màu đỏ nổi bật quá nhiều IMO vì stderr không chỉ là lỗi
krookingking

Câu trả lời:


32

Đây là phiên bản khó hơn của Show only stderr trên màn hình nhưng ghi cả stdout và stderr vào tệp .

Các ứng dụng chạy trong thiết bị đầu cuối sử dụng một kênh duy nhất để giao tiếp với nó; các ứng dụng có hai cổng đầu ra, stdout và stderr, nhưng cả hai đều được kết nối với cùng một kênh.

Bạn có thể kết nối một trong số chúng với một kênh khác, thêm màu vào kênh đó và hợp nhất hai kênh, nhưng điều này sẽ gây ra hai vấn đề:

  • Đầu ra được hợp nhất có thể không chính xác theo cùng một thứ tự như thể không có chuyển hướng. Điều này là do quá trình xử lý được thêm vào trên một trong các kênh mất (một chút) thời gian, do đó kênh màu có thể bị trì hoãn. Nếu bất kỳ bộ đệm được thực hiện, rối loạn sẽ tồi tệ hơn.
  • Thiết bị đầu cuối sử dụng các chuỗi thoát thay đổi màu để xác định màu hiển thị, ví dụ: ␛[31mcó nghĩa là chuyển đổi sang màu đỏ foreground. Điều này có nghĩa là nếu một số đầu ra dành cho thiết bị xuất chuẩn xuất hiện giống như một số đầu ra cho thiết bị xuất chuẩn được hiển thị, thì đầu ra sẽ bị sai màu. (Thậm chí tệ hơn, nếu có một kênh chuyển đổi ở giữa một chuỗi thoát, bạn sẽ thấy rác.)

Về nguyên tắc, có thể viết một chương trình nghe trên hai pty¹, một cách đồng bộ (nghĩa là không chấp nhận đầu vào trên một kênh trong khi nó xử lý đầu ra trên kênh khác) và ngay lập tức xuất ra thiết bị đầu cuối với các hướng dẫn thay đổi màu thích hợp. Bạn sẽ mất khả năng chạy các chương trình tương tác với thiết bị đầu cuối. Tôi không biết thực hiện phương pháp này.

Một cách tiếp cận khả thi khác là làm cho chương trình xuất ra các chuỗi thay đổi màu thích hợp, bằng cách nối xung quanh tất cả các hàm libc gọi cuộc gọi writehệ thống trong thư viện được tải LD_PRELOAD. Xem câu trả lời của illill cho một triển khai hiện có hoặc câu trả lời của Stéphane Chazelas cho một cách tiếp cận hỗn hợp tận dụng strace.

Trong thực tế, nếu điều đó có thể áp dụng, tôi khuyên bạn nên chuyển hướng stderr sang thiết bị xuất chuẩn và đường ống vào một công cụ tô màu dựa trên mô hình như colortail hoặc multitail , hoặc các công cụ tô màu đặc biệt như colorgcc hoặc colormake .

¹ pseudo-thiết bị đầu cuối. Các đường ống sẽ không hoạt động vì bộ đệm: nguồn có thể ghi vào bộ đệm, điều này sẽ phá vỡ tính đồng bộ với bộ tạo màu.


1
Có thể không khó để vá một chương trình thiết bị đầu cuối để thu thập luồng stderr. Ai đó đã đề xuất một cái gì đó như thế này tại Brainstorm .
trực giác

@intuited: điều đó sẽ yêu cầu dẫn đường cho mọi trình giả lập thiết bị đầu cuối mà bạn muốn nó hoạt động. Sử dụng LD_PRELOADmánh khóe để chặn writecác cuộc gọi dường như là thích hợp nhất, IMO (nhưng sau đó, một lần nữa, có thể có sự khác biệt về một số hương vị * nix nhất định.)
alex

Ít nhất là trên Linux, việc chặn writemột mình sẽ không hoạt động vì hầu hết các ứng dụng không gọi trực tiếp, nhưng một chức năng khác từ một thư viện dùng chung (như printf) sẽ gọi bản gốcwrite
Stéphane Chazelas

@StephaneChazelas Tôi đã nghĩ đến việc móc xung quanh writetrình bao bọc tòa nhà . Có phải nội tuyến trong các chức năng khác trong Glibc?
Gilles 'SO- ngừng trở nên xấu xa'

1
Các dự án stderred có vẻ là một thực hiện hooking writequa LD_PRELOADnhư bạn mô tả.
Drew Noakes

36

Kiểm tra stderred. Nó sử dụng LD_PRELOADđể nối libccác write()cuộc gọi, tô màu tất cả stderrđầu ra đi đến một thiết bị đầu cuối. (Theo màu đỏ theo mặc định.)


8
Thật tuyệt, thư viện đó thật tuyệt vời . Câu hỏi thực sự là: tại sao hệ điều hành / thiết bị đầu cuối của tôi không được cài đặt sẵn? ;)
Naftuli Kay

5
Tôi cho rằng bạn là tác giả, phải không? Bạn nên tiết lộ liên kết của bạn trong trường hợp đó.
Dmitry Grigoryev

15

Việc tô màu đầu vào của người dùng rất khó vì trong một nửa trường hợp, nó được đầu ra bởi trình điều khiển đầu cuối (có tiếng vang cục bộ), vì vậy trong trường hợp đó, không có ứng dụng nào chạy trong thiết bị đầu cuối đó có thể biết khi nào người dùng sẽ nhập văn bản và thay đổi màu đầu ra cho phù hợp . Chỉ trình điều khiển thiết bị đầu cuối giả (trong nhân) biết (trình giả lập thiết bị đầu cuối (như xterm) gửi cho nó một số ký tự khi nhấn phím và trình điều khiển đầu cuối có thể gửi lại một số ký tự cho tiếng vang, nhưng xterm không thể biết liệu chúng có phải từ tiếng vang cục bộ hoặc từ những gì ứng dụng xuất ra phía phụ của thiết bị đầu cuối giả).

Và sau đó, có chế độ khác mà trình điều khiển đầu cuối được yêu cầu không lặp lại, nhưng ứng dụng lần này tạo ra một cái gì đó. Ứng dụng (như những ứng dụng sử dụng readline như gdb, bash ...) có thể gửi nó trên thiết bị xuất chuẩn hoặc thiết bị xuất chuẩn, điều này sẽ khó phân biệt với thứ gì đó xuất ra cho những thứ khác ngoài việc lặp lại đầu vào của người dùng.

Sau đó, để phân biệt thiết bị xuất chuẩn của ứng dụng với thiết bị xuất chuẩn của nó, có một số cách tiếp cận.

Nhiều trong số chúng liên quan đến việc chuyển hướng các lệnh stdout và stderr sang các đường ống và các đường ống được đọc bởi một ứng dụng để tô màu cho nó. Có hai vấn đề với điều đó:

  • Khi thiết bị xuất chuẩn không còn là thiết bị đầu cuối (như đường ống thay thế), nhiều ứng dụng có xu hướng điều chỉnh hành vi của chúng để bắt đầu đệm bộ đệm đầu ra, điều đó có nghĩa là đầu ra sẽ được hiển thị trong các khối lớn.
  • Ngay cả khi đó là cùng một quy trình xử lý hai đường ống, không có gì đảm bảo rằng thứ tự văn bản được viết bởi ứng dụng trên thiết bị xuất chuẩn và thiết bị xuất chuẩn sẽ được giữ nguyên, vì quá trình đọc không thể biết (nếu có gì đó được đọc từ cả hai) nên bắt đầu đọc từ ống "stdout" hay ống "stderr".

Một cách tiếp cận khác là sửa đổi ứng dụng để nó tô màu cho thiết bị xuất chuẩn và stdin của nó. Nó thường không thể hoặc thực tế để làm.

Sau đó, một mẹo (đối với các ứng dụng được liên kết động) có thể là chiếm quyền điều khiển (sử dụng $LD_PRELOADnhư trong câu trả lời của bệnh nhân ), các hàm xuất ra được ứng dụng gọi để xuất ra một cái gì đó và bao gồm mã trong đó đặt màu nền trước dựa trên việc chúng có nghĩa là xuất ra thứ gì đó không trên thiết bị xuất chuẩn hoặc thiết bị xuất chuẩn. Tuy nhiên, điều đó có nghĩa là chiếm quyền điều khiển mọi chức năng có thể từ thư viện C và bất kỳ thư viện nào khác có write(2)ứng dụng được gọi trực tiếp bởi ứng dụng có khả năng kết thúc bằng cách viết một cái gì đó trên thiết bị xuất chuẩn hoặc stderr (printf, put, perror ...), và thậm chí sau đó , điều đó có thể sửa đổi hành vi của nó.

Một cách tiếp cận khác có thể là sử dụng các thủ thuật PTRACE như stracehoặc gdbthực hiện để tự móc mỗi khi write(2)cuộc gọi hệ thống được gọi và đặt màu đầu ra dựa trên việc write(2)có trên mô tả tệp 1 hoặc 2 hay không.

Tuy nhiên, đó là một việc khá lớn để làm.

Một mẹo mà tôi vừa chơi là đánh cắp stracechính nó (công việc bẩn là tự móc trước mỗi cuộc gọi hệ thống) bằng LD_PRELOAD, để bảo nó thay đổi màu đầu ra dựa trên việc nó có phát hiện ra write(2)trên fd 1 hay không 2.

Từ việc xem stracemã nguồn, chúng ta có thể thấy rằng tất cả các kết quả đầu ra được thực hiện thông qua vfprintfchức năng. Tất cả chúng ta cần làm là chiếm quyền điều khiển chức năng đó.

Trình bao bọc LD_PRELOAD sẽ trông như sau:

#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);
}

Sau đó, chúng tôi biên dịch nó với:

cc -Wall -fpic -shared -o wrap.so wrap.c -ldl

Và sử dụng nó như:

LD_PRELOAD=/path/to/wrap.so strace -qfo /dev/null -e write -s 0 env -u LD_PRELOAD some-cmd

Bạn sẽ chú ý làm thế nào nếu bạn thay thế some-cmdbằng bash, dấu nhắc bash và những gì bạn gõ xuất hiện màu đỏ (stderr) trong khi zshnó xuất hiện màu đen (vì zsh dups stderr lên một fd mới để hiển thị dấu nhắc và tiếng vang của nó).

Nó dường như hoạt động tốt đáng ngạc nhiên ngay cả đối với các ứng dụng mà bạn không mong đợi (như những ứng dụng sử dụng màu sắc).

Chế độ tô màu là đầu ra trên thiết bị stracelỗi chuẩn được coi là thiết bị đầu cuối. Nếu ứng dụng chuyển hướng thiết bị xuất chuẩn hoặc thiết bị xuất chuẩn của nó, bước tiến bị tấn công của chúng tôi sẽ tiếp tục viết các chuỗi thoát màu trên thiết bị đầu cuối.

Giải pháp đó có những hạn chế:

  • Những stracevấn đề cố hữu đối với : các vấn đề về hiệu năng, bạn không thể chạy các lệnh PTRACE khác như stracehoặc gdbtrong đó hoặc các vấn đề setuid / setgid
  • Nó được tô màu dựa trên writes trên stdout / stderr của từng tiến trình riêng lẻ. Vì vậy, ví dụ, trong sh -c 'echo error >&2', errorsẽ có màu xanh vì echoxuất ra nó trên thiết bị xuất chuẩn của nó (được chuyển hướng đến stderr của sh, nhưng tất cả các bước nhìn thấy là a write(1, "error\n", 6)). Và trong sh -c 'seq 1000000 | wc', seqthực hiện rất nhiều hoặc writes cho thiết bị xuất chuẩn của nó , vì vậy trình bao bọc cuối cùng sẽ tạo ra rất nhiều chuỗi thoát (vô hình) cho thiết bị đầu cuối.

Đẹp. Có những gợi ý về các hàm bao có sẵn cho câu hỏi trùng lặp . Tôi đã đánh dấu câu hỏi để hợp nhất để câu trả lời của bạn có thể được nhìn thấy ở đó.
Gilles 'SO- ngừng trở nên xấu xa'

Có thể tinh chỉnh cú pháp vim nổi bật? strace $CMD | vim -c ':set syntax=strace' -.
Pablo A

4

Đây là một bằng chứng về khái niệm tôi đã làm một thời gian trở lại.

Nó chỉ hoạt động trong 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

Nó cũng giả sử bạn có một hàm gọi là setcolor.

Một phiên bản đơn giản hóa:

setcolor()
{
    case "$1" in
    red)
        tput setaf 1
        ;;
    normal)
        tput sgr0
        ;;
    esac
}

Có một cách đơn giản hơn nhiều để làm điều này : exec 2> >(rederr). Cả hai phiên bản sẽ có các vấn đề tôi đề cập trong câu trả lời của tôi, về việc sắp xếp lại các dòng và rủi ro đầu ra bị sai lệch (đặc biệt là với các dòng dài).
Gilles 'SO- ngừng trở nên xấu xa'

Tôi đã thử nó, và nó không hoạt động.
Mikel

seterrsẽ phải là một tập lệnh độc lập, không phải là một chức năng.
Gilles 'SO- ngừng trở nên xấu xa'

4

Xem Hilite của Mike Schiraldi , người thực hiện điều này cho một lệnh tại một thời điểm. Gush của riêng tôi thực hiện điều này trong cả một phiên, nhưng cũng có rất nhiều tính năng / đặc điểm khác mà bạn có thể không muốn.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.