Bir program renkli çıktıya sahip olup olmayacağına nasıl karar verir?


17

Renkli çıktı ( lsveya gibi gcc) yazdıran bir terminalden bir komut yürüttüğümde , renkli çıktı yazdırılıyor. Anladığım kadarıyla, süreç aslında ANSI kaçış kodlarını çıkarıyor ve terminal rengi biçimlendiriyor.

Ancak, aynı komutu başka bir işlemle (özel bir C uygulaması söyleyin) yürütürsem ve çıktıyı uygulamanın kendi çıktısına yönlendirirsem, bu renkler devam etmez.

Bir program, renkli formatta metin çıktısı alıp almayacağına nasıl karar verir? Bazı ortam değişkenleri var mı?

Yanıtlar:


25

Bu tür programların çoğu varsayılan olarak renk kodlarını terminale verir; çıktılarını kullanarak bir TTY olup olmadığını kontrol ederler isatty(3). Genellikle bu davranışı geçersiz kılma seçenekleri vardır: her durumda renkleri devre dışı bırakın veya her durumda renkleri etkinleştirin. Örneğin GNU grepiçin --color=neverrenkleri devre dışı bırakır ve --color=alwaysetkinleştirir.

Kabukta -t testoperatörü kullanarak aynı testi yapabilirsiniz : [ -t 1 ]sadece standart çıkış bir terminal ise başarılı olacaktır.


Başlatılan uygulamanın, işlemin bir tty olduğunu kandırmanın bir yolu var mı?
Chris Smith

4
Zaten unix.stackexchange.com/questions/249723 adresinden soruldu ve yanıtlandı , chris13523. Yorumlar bu arada gerçekten takip sorularının yeri değil.
JdeBP

1
@ chris13524 bakınız JdeBP'nin bağlantısı; programları birçok durumda renk kodları çıkarmaya zorlayabilirsiniz (güncellenmiş cevabıma bakın).
Stephen Kitt

13

Bazı ortam değişkenleri var mı?

Evet. Öyle TERMortam değişkeni. Bunun nedeni, karar sürecinin bir parçası olarak kullanılan birkaç şey olmasıdır.

Burada genelleme yapmak zordur, çünkü tüm programlar tek bir karar akış şeması üzerinde anlaşamazlar. Aslında grepM. Kitt'in cevabında değinilen GNU , beklenmedik sonuçlarla biraz alışılmadık bir karar süreci kullanan bir aykırı durumun iyi bir örneğidir. Çok genel anlamda, bu nedenle:

  • Standart çıkış, belirlendiği gibi bir terminal cihazı olmalıdır isatty().
  • Program, termcap / terminfo veritabanındaki terminal tipi için kaydı arayabilmelidir.
  • Bu nedenle , aramak için bir terminal tipi olmalıdır . TERMÇevre değişkeni bulunmalı ve kendi değer bir veritabanı kaydı aynı olmalıdır.
  • Bu nedenle bir terminfo / termcap veritabanı olmalıdır. Alt sistemin bazı uygulamalarında, termcap veritabanının yeri bir TERMCAPortam değişkeni kullanılarak belirtilebilir . Bazı uygulamalarda ikinci bir ortam değişkeni vardır.
  • Termcap / terminfo kaydı, terminal tipinin renkleri desteklediğini belirtmelidir. max_colorsTerminfoda bir alan var . Aslında renk özellikleri olmayan terminal türleri için ayarlanmamıştır. Gerçekten de, her renklendirilebilir terminal türü için, herhangi bir renk yeteneği belirtmeyen adla birlikte eklenen -mveya -monoadın sonuna eklenen başka bir kayıt vardır .
  • Termcap / terminfo kaydı, programın renk değiştirmesine yol açmalıdır. Terminfoda set_a_foregroundve set_a_backgroundalanlar var .

Sadece kontrol etmekten biraz daha karmaşık isatty(). Bu hale getirilebileceğini ileri birkaç şeyden karmaşık:

  • Bazı uygulamalar, isatty()denetimi geçersiz kılan komut satırı seçenekleri veya yapılandırma bayrakları ekler , böylece program her zaman veya hiçbir zaman çıktısı olarak (renklendirilebilir) bir terminale sahip olduğunu varsayar. Örneğin:
    • GNU lssahiptir --colorkomut satırı seçeneği.
    • BSD ls, CLICOLOR( hiç olmadığı anlamsızlığı ) ve CLICOLOR_FORCE( her zaman anlamı anlamına gelen ) ortam değişkenlerine bakar ve ayrıca -Gkomut satırı seçeneğini kullanır.
  • Bazı uygulamalar termcap / terminfo kullanmaz ve değerine kablolu yanıtlar verir TERM.
  • Tüm terminaller, renkleri değiştirmek için "ANSI kaçış dizileri" olarak biraz yanlış adlandırılan ECMA-48 veya ISO 8613-6 SGR dizilerini kullanmaz. Termcap / terminfo mekanizması aslında uygulamaları kesin kontrol dizilerinin doğrudan bilgisinden yalıtmak üzere tasarlanmıştır. (Dahası, hiç kimsenin ISO 8613-6 SGR sekanslarını kullanmadığına dair bir argüman var , çünkü herkes RGB renkli SGR sekansları için sınırlayıcı olarak yarı kolon kullanma hatasını kabul ediyor . Standart aslında kolonu belirtir.)

Belirtildiği gibi, GNU grepaslında bu ek karmaşıklıkların bazılarını sergilemektedir. Termcap / terminfo'ya başvurmaz, yaymak için kontrol sekanslarını bağlar ve TERMortam değişkenine bir yanıtı kablolar .

Bunun Linux / Unix portu bu kodu vardır sadece renk oluşumuna olanak, TERMortam değişkeni varsa ve değeri kablolu adı eşleşmiyor dumb:

int
renklendirmeli (geçersiz)
{
  char const * t = getenv ("TERM");
  dönüş t && strcmp (t, "aptal")! = 0;
}

Yani hatta eğer TERMIS xterm-mono, GNU grepbile diğer programlar olsa gibi yayarlar renklerine karar verecektir vimolmaz.

Bunun Win32 portu bu kodu vardır renk oluşumuna olanak ya zaman TERMortam değişkeni değil yok veya varolduğundan ve ne zaman değeri kablolu adı eşleşmiyor dumb:

int
renklendirmeli (geçersiz)
{
  char const * t = getenv ("TERM");
  dönüş ! (t && strcmp (t, "aptal") == 0);
}

GNU'nun grepRenk Sorunları

GNU'nun greprengi aslında kötü şöhretlidir. Aslında terminal çıkışını inşa etmek için uygun bir iş yapmadığı için, bunun yerine boşluğundaki çıkışındaki çeşitli noktalarda birkaç kablolu kontrol sekansındaki suçlamalar, bunun yeterince iyi olduğunu umar, bazı durumlarda yanlış çıktı görüntüler.

Bu koşullar, terminalin sağ kenarında bulunan bir şeyi renklendirmek zorunda olduğu yerlerdir. Terminal çıkışını düzgün yapan programların otomatik sağ kenar boşluklarını hesaba katması gerekir. Terminalin sahip olamayacağına dair küçük bir olasılığa ek olarak ( auto_right_marginterminfodaki alanı da), otomatik sağ kenar boşluklarına sahip olan terminallerin davranışı genellikle beklemedeki hat sargısının DEC VT emsalini takip eder . GNU grepbunu açıklamıyor, hemen satır sarmayı bekliyor ve renkli çıktısı yanlış gidiyor.

Renkli çıktı basit bir şey değildir.

daha fazla okuma


2
Anladığım kadarıyla OP, çıktı yeniden yönlendirildiğinde davranıştaki değişikliği soruyor; $TERMaçıklamıyor. (Cevabınız genel olarak ilginç, ama soruyu ele aldığını sanmıyorum ...)
Stephen Kitt

çok ilginç. Programların birkaç aydır bir terminal kapasitesinin ne olduğunu nasıl keşfettiği (veya basitçe "karar" verdiği) konusunda genel bir bakış istiyorum. Bu aynı zamanda böyle bir genel bakış bulmanın neden bu kadar zor olduğuna dair bir fikir verir - çünkü her program bunu biraz farklı yapıyor gibi görünüyor.
the_velour_fog

Ne bir açıklama bekleyen satır sarma ve acil hat sarma farkını gösteren bir örnekle ortalama birlikte ile güzel olurdu.
mosvy

0

unbufferKomut beklemek paket de-çiftler ilk program çıkışı ve ikinci programa girdi.

Bunu şöyle kullanırsınız:

unbuffer myshellscript.sh | grep value

Her zaman ansible ve homebrewed ctee komut dosyası ile kullanıyorum, böylece günlük dosyasını normal ( renksiz) çıktıyla bırakırken terminaldeki renk çıktısını görebiliyorum.

unbuffer ansible-playbook myplaybook.yml | ctee /var/log/ansible/run-$( date "+%F" ).log
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.