Kabuğumu STDERR ve STDOUT'u farklı renklerde basacak şekilde yapılandırabilir miyim?


62

Benim terminali yukarı böylece ayarlamak istediğiniz stderrfarklı renkte daha basılıyor stdout; belki kırmızı. Bu, ikisini birbirinden ayırmayı kolaylaştıracaktır.

Bunu yapılandırmanın bir yolu var mı .bashrc? Olmazsa, bu mümkün mü?


Not : Bu soru ile birleştirilmiştir başka istedi o stderr, stdout ve kullanıcı girişi yankı çıktı olmak 3 farklı renk . Cevaplar her iki soruyu da ele alıyor olabilir.


1
Yığın Taşması ile ilgili aynı soru: stackoverflow.com/questions/6841143/…
Stéphane Gimenez

İlginç soru + cevaplar, ancak kırmızı çok fazla IMO öne çıkıyor ,
saat 16

Yanıtlar:


32

Bu sadece ekranda stderr göster daha zor bir versiyonudur ancak dosyaya hem stdout hem de stderr yazınız .

Terminalde çalışan uygulamalar, onunla iletişim kurmak için tek bir kanal kullanır; uygulamaların stdout ve stderr olmak üzere iki çıkış portu vardır, ancak ikisi de aynı kanala bağlanır.

Bunlardan birini farklı bir kanala bağlayabilir, o kanala renk ekleyebilir ve iki kanalı birleştirebilirsiniz, ancak bu iki soruna neden olur:

  • Birleştirilmiş çıktı, herhangi bir yeniden yönlendirme yapılmadı gibi aynı sırada olmayabilir. Bunun nedeni, kanallardan birine eklenmiş işlemin (biraz) zaman almasıdır, dolayısıyla renkli kanal gecikebilir. Herhangi bir tamponlama yapılırsa, hastalık daha kötü olacaktır.
  • Terminaller, ekran rengini belirlemek için renk değiştirme çıkış dizileri kullanır, örneğin ␛[31m“kırmızı ön plana geçmek” anlamına gelir. Bu, stdout'a yönlendirilen bir çıktının stderr için bir çıktının görüntülenmesi gibi gelmesi durumunda çıkışın yanlış renklendirileceği anlamına gelir. (Daha da kötüsü, bir kaçış dizisinin ortasında bir kanal anahtarı varsa, çöp görürsünüz.)

Prensip olarak, eş zamanlı olarak iki pty list dinleyen bir program yazmak mümkün olacaktır (diğer bir kanalda çıktıyı işlerken bir kanalda girişi kabul etmeyecektir) ve hemen uygun renk değiştirme talimatları ile terminale çıkış yapması mümkün olacaktır. Terminal ile etkileşime giren programları çalıştıramazsınız. Bu yöntemin herhangi bir uygulamasını bilmiyorum.

Bir başka olası yaklaşım, programın writeyüklü olduğu bir kütüphanede sistem çağrısı olarak adlandırılan tüm libc işlevlerinin etrafına asılarak uygun renk değiştirme dizileri çıkarmasına neden olmaktır LD_PRELOAD. Bkz sickill cevabını varolan uygulanması için veya Stéphane Chazelas yanıtını güçlendirir karışık bir yaklaşım için strace.

Uygulamada, eğer uygulanabilirse, stderr'i stdout'a ve boruya, colortail veya multitail gibi desen bazlı bir renklendiriciye veya colorgcc veya colormake gibi özel amaçlı renklendiricilere yönlendirmeyi öneririm .

¹ sözde terminalleri. Borular tamponlama nedeniyle işe yaramaz: kaynak tampona yazabilir, bu da renklendirici ile senkronizasyonu bozabilir.


1
Stderr akışını renklendirmek için bir terminal programını yamalamak zor olmayabilir. Birisi ubuntu beyin fırtınasında böyle bir şey önerdi .
intuited

@intuited: bununla çalışmak istediğiniz her terminal emülatörünü yönlendirmeyi gerektirir. Çağrıları LD_PRELOADengellemek için numarayı kullanmak writeen uygun gibi görünüyor, IMO (ama sonra yine, bazı * nix tatlarında farklılıklar olabilir.)
alex

En azından Linux'ta, writetek başına müdahale etmek çoğu uygulama doğrudan printfwrite
aramadığı için

@StephaneChazelas Syscall writesarmalayıcısının çevresine takılmayı düşünüyordum . Glibc’deki diğer işlevlerde de belirtilmiş mi?
Gilles

1
Stderred proje çengel bir uygulama gibi görünüyor writearacılığıyla LD_PRELOADsize tarif olarak.
Drew No:

36

Kontrol edin stderred. O kullanır LD_PRELOADiçin kanca libc'ın write()tüm renklendirip, aramaları stderrbir terminale gidiş çıktı. (Varsayılan olarak kırmızı renkte).


8
Güzel, bu kütüphane harika . Asıl soru şudur: neden işletim sistemim / terminalim bu önceden kurulu olarak gelmiyor? ;)
Naftuli Kay

5
Sanırım yazar sizsiniz, doğru mu? Bu durumda üyeliğinizi ifşa etmeniz gerekir.
Dmitry Grigoryev

15

Renklendirme kullanıcı girişi zordur, çünkü vakaların yarısında, terminal sürücüsü tarafından (yerel yankı ile) verilir, bu durumda, bu terminalde çalışan hiçbir uygulama, kullanıcının metin yazacağını ve çıktı rengini buna göre değiştireceğini bilemez . Sadece sözde-terminal sürücüsü (çekirdekte) bilir (terminal emülatörü (xterm gibi) bazı tuş basışlarına bazı karakterler gönderir ve terminal sürücüsü yankı için bazı karakterleri geri gönderebilir, ancak xterm bunların yazıp yazılmadığını bilemez. yerel yankı veya uygulamanın sözde terminalin bağımlı tarafına ne çıktığı).

Ve sonra, terminal sürücünün yankılanmaması gerektiği söylenen başka bir mod var, ancak bu sefer uygulama bir şey çıkarıyor. Uygulama (gdb, bash ... gibi readline kullananlar gibi), kullanıcı girişini tekrarlamaktan başka şeyler için çıkardığı bir şeyden ayırt etmesi zor olacak stdout veya stderr'de gönderebilir.

Sonra bir uygulamanın stdout'unu stderr'den ayırt etmek için birkaç yaklaşım vardır.

Bunların çoğu stdout ve stderr komutlarını borulara ve onu renklendirmek için bir uygulama tarafından okunan borulara yönlendirmeyi içerir. Bununla ilgili iki sorun var:

  • Stdout artık bir terminal olmadığında (bunun yerine bir boru gibi), birçok uygulama çıktılarını tamponlamaya başlama davranışlarını uyarlama eğilimindedir, bu da çıktıların büyük parçalarda gösterileceği anlamına gelir.
  • İki boruyu işleyen aynı süreç olsa bile, uygulama tarafından stdout ve stderr'de yazılan metnin, okuma işlemi bilmediğinden (her ikisinden de okunacak bir şey varsa) korunamaması garanti edilemez. "stdout" borusundan veya "stderr" borusundan okumaya başlayıp başlamaması.

Diğer bir yaklaşım ise, uygulamanın stdout ve stdin rengini değiştirecek şekilde değiştirilmesidir. Bunu yapmak çoğu zaman mümkün veya gerçekçi değildir.

Sonra (dinamik olarak bağlı uygulamalar için) bir hile yapmak ( sickill'in cevabında$LD_PRELOAD olduğu gibi ) uygulama tarafından çağrılan çıktı fonksiyonlarını bir şeyi çıkarmak için kullanmak ve bir şeyi çıkarıp atmayacağına bağlı olarak ön plan rengini ayarlayan bir kod eklemek olabilir. stderr veya stdout'ta. Bununla birlikte, bu, C kütüphanesinden ve write(2)stdout veya stderr (printf, put, perror ...) üzerine bir şeyler yazmaya neden olabilecek, uygulama tarafından doğrudan çağrılan bir çağrı yapan C kütüphanesinden ve olası her işlevi ele geçirme anlamına gelir. , bu davranışını değiştirebilir.

Başka bir yaklaşım , sistem çağrısı her çağrıldığında kendimizi bağlamak için PTRACE püf noktalarını kullanmak straceveya gdbyapmak write(2)ve çıktı write(2)tanımlayıcısını 1 veya 2 dosya tanımlayıcıda olup olmadığına göre ayarlamak olabilir .

Ancak, bu yapılacak oldukça büyük bir şey.

Az önce oynadığım bir hile straceLD_PRELOAD kullanarak kendisini ele geçirmek (bu, her sistem çağrısından önce kendini çengelemek için kirli iş yapıyor) write(2), fd 1'de tespit edip etmediğine bağlı olarak çıktı rengini değiştirmesini söylemek. 2.

Bakarak stracekaynak kodu, bunun çıkışı tüm aracılığıyla yapıldığını görebilirsiniz vfprintffonksiyonu. Tek yapmamız gereken, bu işlevi kaçırmak.

LD_PRELOAD sarmalayıcısı şöyle görünür:

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

O zaman, onu derleriz:

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

Ve onu şu şekilde kullanın:

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

Eğer bash istemiyle ve yazdıklarınız kırmızı (stderr) ile siyah renkte göründüğünde (zsh, stderr komutunu ve ekosunu görüntülemek için yeni bir fd'ye basar çünkü ) nasıl değiştirirsiniz some-cmd, fark edeceksiniz .bashzsh

Beklemeyeceğiniz uygulamalar için bile (renk kullananlar gibi) şaşırtıcı derecede iyi çalışıyor gibi görünüyor.

Renklendirme modu, straceterminal olduğu kabul edilen stderr'de çıkar. Eğer uygulama stdout'unu veya stderr'ini yönlendirirse, kaçırılan strace terminalimizde renk kaçış dizileri yazmaya devam edecektir.

Bu çözümün sınırlamaları var:

  • straceİçinde olanlar : performans sorunları, onun gibi straceveya gdbiçindeki diğer PTRACE komutlarını çalıştıramazsınız veya setuid / setgid sorunları
  • writeHer bir sürecin stdout / stderr s sine göre renklendirilir . Yani örneğin, içinde sh -c 'echo error >&2', errorçünkü yeşil olurdu echoüzerindeki çıkışlar onu kendi Stdout'a (sh en stderr'e yönlendirildi sh, ancak tüm strace bir olduğunu görür write(1, "error\n", 6)). Ve de sh -c 'seq 1000000 | wc', seqbir çok veya yok writeetmek s da sargı terminaline (görünmez) kaçış dizilerinin bir çok çıkış olanağı sona erecek, bu yüzden Stdout'a.

Güzel. Yinelenen soruda önceden var olan sarıcıların önerileri vardı . Cevabınızı orada görebilmek için birleştirme sorusunu işaretledim.
Gilles

Belki vim sözdizimi vurgulamak tweaking? strace $CMD | vim -c ':set syntax=strace' -.
Pablo A

4

İşte bir süre önce yaptığım kavramın bir kanıtı.

Sadece zsh ile çalışır.

# 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

Ayrıca setcolor adlı bir işleve sahip olduğunuzu varsayar.

Basitleştirilmiş bir sürüm:

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

Bunu yapmak için çok daha basit bir yolu var: exec 2> >(rederr). Her iki sürümde de cevabımda belirttiğim konular, satırları yeniden sıralama ve karışık çıktıları riske atma (özellikle uzun satırlarda) olacak.
Gilles

Bunu denedim ve işe yaramadı.
Mikel

seterrbir fonksiyon değil bağımsız bir komut dosyası olmalı.
Gilles


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.