उपयोगकर्ता इनपुट को रंगना मुश्किल है क्योंकि आधे मामलों में, यह टर्मिनल ड्राइवर (स्थानीय गूंज के साथ) द्वारा आउटपुट होता है इसलिए उस स्थिति में, उस टर्मिनल में चल रहे किसी भी एप्लिकेशन को यह पता नहीं चल सकता है कि उपयोगकर्ता पाठ टाइप करने जा रहा है और तदनुसार उत्पादन का रंग बदल रहा है। । केवल छद्म टर्मिनल चालक (कर्नेल में) जानता है (टर्मिनल एमुलेटर (जैसे 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