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 strace
veya gdb
yapmak 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 strace
LD_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 strace
kaynak kodu, bunun çıkışı tüm aracılığıyla yapıldığını görebilirsiniz vfprintf
fonksiyonu. 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 .bash
zsh
Beklemeyeceğiniz uygulamalar için bile (renk kullananlar gibi) şaşırtıcı derecede iyi çalışıyor gibi görünüyor.
Renklendirme modu, strace
terminal 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 strace
veya gdb
içindeki diğer PTRACE komutlarını çalıştıramazsınız veya setuid / setgid sorunları
write
Her 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'
, seq
bir çok veya yok write
etmek s da sargı terminaline (görünmez) kaçış dizilerinin bir çok çıkış olanağı sona erecek, bu yüzden Stdout'a.