क्या मैं अलग-अलग रंगों में STDERR और STDOUT को प्रिंट करने के लिए अपने शेल को कॉन्फ़िगर कर सकता हूं?


62

मैं अपने टर्मिनल को सेट करना चाहता हूं ताकि stderrएक अलग रंग में मुद्रित हो stdout; शायद लाल। इससे दोनों को अलग बताना आसान होगा।

क्या इसमें कॉन्फ़िगर करने का कोई तरीका है .bashrc? यदि नहीं, तो क्या यह भी संभव है?


नोट : इस सवाल के साथ विलय कर दिया गया एक और है कि के लिए कहा stderr, stdout और उपयोगकर्ता इनपुट गूंज में उत्पादन होने के लिए 3 अलग अलग रंग । उत्तर या तो प्रश्न को संबोधित कर सकते हैं।


1
स्टैक ओवरफ्लो पर एक ही सवाल: stackoverflow.com/questions/6841143/…
स्टीफन Gimenez

दिलचस्प सवाल + जवाब, हालांकि लाल बहुत ज्यादा बाहर खड़ा है क्योंकि
स्टॉडर

जवाबों:


32

यह केवल स्क्रीन पर शोल्डर का एक कठिन संस्करण है, लेकिन फाइल करने के लिए स्टडआउट और स्टादर दोनों लिखें

टर्मिनल में चल रहे एप्लिकेशन इसके साथ संवाद करने के लिए एकल चैनल का उपयोग करते हैं; अनुप्रयोगों के दो आउटपुट पोर्ट, stdout और stderr हैं, लेकिन वे दोनों एक ही चैनल से जुड़े हुए हैं।

आप उनमें से एक को एक अलग चैनल से जोड़ सकते हैं, उस चैनल में रंग जोड़ सकते हैं, और दो चैनलों को मर्ज कर सकते हैं, लेकिन इससे दो समस्याएं होंगी:

  • मर्ज किए गए आउटपुट बिल्कुल उसी क्रम में नहीं हो सकते हैं जैसे कि कोई पुनर्निर्देशन नहीं हुआ था। ऐसा इसलिए है क्योंकि चैनल में से किसी एक पर जोड़े गए प्रसंस्करण में (थोड़ा) समय लगता है, इसलिए रंगीन चैनल में देरी हो सकती है। यदि कोई बफरिंग की जाती है, तो विकार और भी बदतर होगा।
  • टर्मिनल रंग बदलने के क्रम में रंग बदलने के क्रम का उपयोग करते हैं, उदाहरण के लिए ␛[31m"लाल अग्रभूमि पर स्विच करें"। इसका मतलब यह है कि अगर स्टडआउट के लिए नियत किए गए कुछ आउटपुट ठीक उसी तरह से आते हैं जैसे स्टडर के लिए कुछ आउटपुट प्रदर्शित किए जा रहे हैं, तो आउटपुट मिसकॉल हो जाएगा। (इससे भी बदतर, अगर एक एस्केप अनुक्रम के बीच में एक चैनल स्विच है, तो आपको कचरा दिखाई देगा।)

सिद्धांत रूप में, एक प्रोग्राम लिखना संभव होगा जो दो ptys synchron पर सुनता है, सिंक्रोनाइज़ (यानी एक चैनल पर इनपुट को स्वीकार नहीं करेगा जबकि यह दूसरे चैनल पर आउटपुट प्रोसेस कर रहा है), और तुरंत उपयुक्त रंग बदलने वाले निर्देशों के साथ टर्मिनल पर आउटपुट करता है। आप उन प्रोग्रामों को चलाने की क्षमता खो देंगे जो टर्मिनल के साथ इंटरैक्ट करते हैं। मैं इस पद्धति के किसी भी कार्यान्वयन के बारे में नहीं जानता।

एक अन्य संभावित दृष्टिकोण प्रोग्राम को उचित रंग बदलने वाले अनुक्रमों को आउटपुट करने के लिए होगा, जो कि उन सभी परिवाद कार्यों के आसपास हुक करके होगा जो writeसिस्टम लोड को लाइब्रेरी में लोड करते हैं LD_PRELOAD। मौजूदा कार्यान्वयन के लिए बीमारी का जवाब देखें , या लीवर के लिए मिश्रित दृष्टिकोण के लिए स्टीफन चेज़ेलस का जवाबstrace

व्यवहार में, यदि यह लागू है, तो मैं सुझाव देता हूं कि स्टैडआउट और पाइपिंग को एक पैटर्न-आधारित colorizer जैसे कि Colortail या multitail , या colorgcc या colormake जैसे विशेष-प्रयोजन के colorizers में पुनर्निर्देशित करें

¹ छद्म-टर्मिनल। बफरिंग के कारण पाइप काम नहीं करेंगे: स्रोत बफर को लिख सकता है, जो कि कलराइज़र के साथ सिंक्रनाइजेशन को तोड़ देगा।


1
Stderr स्ट्रीम को कोलिज़ करने के लिए टर्मिनल प्रोग्राम को पैच करना मुश्किल नहीं हो सकता है। Ubuntu के बुद्धिशीलता पर किसी ने कुछ इस तरह का सुझाव दिया है ।
intuited

@intuited: कि आप के साथ काम करने के लिए यह चाहते हैं कि हर टर्मिनल एमुलेटर की आवश्यकता होगी। कॉल LD_PRELOADको इंटरसेप्ट करने के लिए ट्रिक का उपयोग करना writeसबसे उचित प्रतीत होता है, IMO (लेकिन फिर, कुछ निश्चित * डिक्स फ्लेवर पर अंतर हो सकता है।)
एलेक्स

कम से कम लिनक्स पर, writeअकेले अवरोधन काम नहीं करेगा क्योंकि अधिकांश एप्लिकेशन सीधे कॉल नहीं करते हैं, लेकिन कुछ साझा लाइब्रेरी (जैसे printf) से एक अन्य फ़ंक्शन जो मूल कॉल करेगाwrite
स्टीफन चेज़लस

@ स्टेफ़ेनचेज़लस मैं सिसकॉल writeरैपर के आसपास हुक करने के बारे में सोच रहा था । क्या यह ग्लिबक में अन्य कार्यों में शामिल है?
गाइल्स

1
Stderred परियोजना hooking के एक कार्यान्वयन हो रहा है writeके माध्यम से LD_PRELOADके रूप में आप का वर्णन।
ड्रू नॉक

36

जांच करें stderred। यह का उपयोग करता है LD_PRELOADकरने के लिए हुक करने libcके write()कॉल, सभी colorizing stderrउत्पादन एक टर्मिनल के लिए जा रहा। (डिफ़ॉल्ट रूप से लाल रंग में।)


8
अच्छा लगा, वह पुस्तकालय बहुत बढ़िया है । असली सवाल यह है: मेरा ऑपरेटिंग सिस्टम / टर्मिनल इस प्रीइंस्टॉल्ड के साथ क्यों नहीं आता है? ;)
नातपुली केए

5
मुझे लगता है कि आप लेखक हैं, क्या यह सही है? आपको उस मामले में अपनी संबद्धता का खुलासा करना चाहिए।
दिमित्री ग्रिगोरीव

15

उपयोगकर्ता इनपुट को रंगना मुश्किल है क्योंकि आधे मामलों में, यह टर्मिनल ड्राइवर (स्थानीय गूंज के साथ) द्वारा आउटपुट होता है इसलिए उस स्थिति में, उस टर्मिनल में चल रहे किसी भी एप्लिकेशन को यह पता नहीं चल सकता है कि उपयोगकर्ता पाठ टाइप करने जा रहा है और तदनुसार उत्पादन का रंग बदल रहा है। । केवल छद्म टर्मिनल चालक (कर्नेल में) जानता है (टर्मिनल एमुलेटर (जैसे xterm)) इसे कुछ कीपर पर कुछ वर्ण भेजता है और टर्मिनल चालक कुछ वर्णों को प्रतिध्वनि के लिए वापस भेज सकता है, लेकिन xterm यह नहीं जान सकता है कि वे कौन से हैं स्थानीय गूंज या क्या छद्म टर्मिनल के गुलाम पक्ष के लिए आवेदन उत्पादन से)।

और फिर, वहाँ दूसरी विधा है जहाँ टर्मिनल चालक को प्रतिध्वनि के लिए नहीं कहा जाता है, लेकिन इस बार का अनुप्रयोग कुछ आउटपुट करता है। एप्लिकेशन (जैसे कि gdb, bash ... जैसे रीडलाइन का उपयोग करने वाले) अपने stdout या stderr पर भेज सकते हैं, जो कि उस चीज़ से अंतर करना मुश्किल हो रहा है जो वह उपयोगकर्ता इनपुट वापस करने के अलावा अन्य चीजों के लिए आउटपुट करता है।

फिर किसी एप्लिकेशन के स्टडआउट को उसके स्टैडर से अलग करने के लिए, कई दृष्टिकोण हैं।

उनमें से कई कमांड को स्टडआउट और स्टेडर को पाइप में रीडायरेक्ट करना और उन पाइपों को पढ़ना है जो इसे रंगने के लिए एक एप्लिकेशन द्वारा पढ़ते हैं। इसके साथ दो समस्याएं हैं:

  • एक बार जब स्टडआउट टर्मिनल नहीं होता है (इसके बजाय एक पाइप की तरह), तो कई एप्लिकेशन अपने व्यवहार को अपने आउटपुट को बफ़र करने के लिए अनुकूलित करने लगते हैं, जिसका अर्थ है कि आउटपुट बड़ी मात्रा में प्रदर्शित होने वाला है।
  • यहां तक ​​कि अगर यह वही प्रक्रिया है जो दो पाइपों को संसाधित करती है, तो इस बात की कोई गारंटी नहीं है कि स्टडआउट और स्टेडर पर एप्लिकेशन द्वारा लिखे गए आदेश को संरक्षित किया जाएगा, क्योंकि पढ़ने की प्रक्रिया को पता नहीं चल सकता है (यदि दोनों से पढ़ने के लिए कुछ है) चाहे "stdout" पाइप या "stderr" पाइप से पढ़ना शुरू करें।

एक और तरीका यह है कि एप्लिकेशन को संशोधित किया जाए ताकि वह अपने स्टडआउट और स्टडिन को रंग दे। यह अक्सर संभव या यथार्थवादी नहीं है।

फिर एक ट्रिक (डायनेमिकली लिंक किए गए एप्लिकेशन के लिए) हाईजैक करने के लिए हो सकती है ( बीमारी के जवाब के$LD_PRELOAD रूप में उपयोग करके ) आउटपुटिंग फंक्शंस को एप्लिकेशन द्वारा किसी चीज को आउटपुट करने के लिए कहा जाता है और इसमें कोड शामिल होते हैं जो फोरग्राउंड कलर को सेट करते हैं, जो इस बात पर आधारित होता है कि वे कुछ आउटपुट के लिए हैं या नहीं stderr या stdout पर। हालांकि, इसका मतलब है कि सी लाइब्रेरी और किसी भी अन्य लाइब्रेरी से हर संभव फ़ंक्शन को अपहृत करना जो सीधे आवेदन द्वारा कॉल किए गए एक syscall करता है जो संभवतः stdout या stderr (प्रिंटफ, पुट, पेरर ...) पर कुछ लिख सकता है, और तब भी। , जो उसके व्यवहार को संशोधित कर सकता है।write(2)

एक और तरीका यह हो सकता है कि जब भी सिस्टम कॉल करे और हर बार फाइल डिस्क्रिप्टर 1 या 2 पर हो, के आधार पर आउटपुट कलर सेट करने के लिए PTRACE ट्रिक्स का उपयोग करें straceया gdbखुद को हुक write(2)करें write(2)

हालाँकि, यह काफी बड़ी बात है।

एक ट्रिक जो मैंने अभी-अभी खेली है, straceअपने आप को हाईजैक करना है (जो कि हर सिस्टम कॉल से पहले अपने आप को हुक करने का गंदा काम करता है) LD_PRELOAD का उपयोग करके, आउटपुट के रंग को बदलने के लिए यह बताने के लिए कि क्या इसने write(2)fd 1 का पता लगाया है या नहीं 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 -qfo /dev/null -e write -s 0 env -u LD_PRELOAD some-cmd

आप कैसे आप की जगह अगर ध्यान देंगे some-cmdसाथ bash, बैश शीघ्र और जो आप लिखना लाल (stderr) में प्रकट होता है, जबकि साथ zshयह काले रंग में दिखाई देता है (क्योंकि zsh dups एक नई एफडी पर stderr इसके शीघ्र प्रदर्शित करने के लिए और मेल खाती है)।

यह उन अनुप्रयोगों के लिए भी आश्चर्यजनक रूप से अच्छी तरह से काम करता प्रतीत होता है, जिनसे आप अपेक्षा नहीं करते (जैसे कि वे जो रंगों का उपयोग करते हैं)।

रंग मोड आउटपुट पर है straceजो कि टर्मिनल माना जाता है। यदि एप्लिकेशन अपने स्टडआउट या स्टैडर को पुनर्निर्देशित करता है, तो हमारा अपहृत स्ट्रेस टर्मिनल पर कलरिंग एस्केप सीक्वेंस लिखता रहेगा।

उस समाधान की अपनी सीमाएँ हैं:

  • उन पर अंतर्निहित strace: प्रदर्शन समस्याएँ, आप अन्य PTRACE आदेशों को नहीं चला सकते हैं जैसे straceया gdbउसमें, या setuid / setgid समस्याएँ
  • यह writeप्रत्येक व्यक्तिगत प्रक्रिया के stdout / stderr के आधार पर रंग है। तो उदाहरण के लिए, में sh -c 'echo error >&2', errorहरे रंग की वजह से हो सकता है echoपर आउटपुट यह अपने stdout (जो श के stderr पर पुनः निर्देशित एसएच, लेकिन सभी strace देखता है एक है write(1, "error\n", 6))। और sh -c 'seq 1000000 | wc', अपने स्टडआउट के लिए seqबहुत कुछ करता है , इसलिए रैपर टर्मिनल के लिए बहुत सारे (अदृश्य) एस्केप सीक्वेंस को आउटपुट करता है।write

अच्छा लगा। डुप्लिकेट प्रश्न पर preexisting रैपर के सुझाव थे । मैंने प्रश्न को मर्ज करने के लिए ध्वजांकित किया है ताकि आपका उत्तर वहां देखा जा सके।
गिल्स

शायद विम सिंटैक्स हाइलाइटिंग tweaking? strace $CMD | vim -c ':set syntax=strace' -
पाब्लो ए

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()
{
    case "$1" in
    red)
        tput setaf 1
        ;;
    normal)
        tput sgr0
        ;;
    esac
}

ऐसा करने का एक बहुत सरल तरीका है exec 2> >(rederr):। दोनों संस्करणों में मेरे द्वारा बताए गए मुद्दों का उल्लेख होगा, लाइनों को फिर से व्यवस्थित करने और (विशेषकर लंबी लाइनों के साथ) मैंगल्ड आउटपुट को जोखिम में डालने का।
गाइल्स

मैंने कोशिश की कि, और यह काम नहीं किया।
मिकेल

seterrएक स्टैंडअलोन स्क्रिप्ट होना चाहिए, एक फ़ंक्शन नहीं।
गाइल्स

4

माइक Schiraldi के देखें hilite जो एक समय में एक आदेश के लिए ऐसा करता है। मेरा अपना गश पूरे सत्र के लिए ऐसा करता है, लेकिन इसमें बहुत सी अन्य विशेषताएं / आइडिओसिंक्रैसीज़ भी हैं जो आप नहीं चाहते हैं।


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.