Jak zidentyfikować terminal na podstawie skryptu?

8

I nie mów „ $TERM” - zawsze tak jest xterm.

W jaki sposób bashskrypt może stwierdzić, w którym terminalu działa, a konkretnie czy jest to iTerm, Terminal.app, czy faktycznie xterm?

Pytam, ponieważ resetnie działa¹ po wyjęciu z pudełka w Terminal.app i iTerm2. Jednak iTerm2 rozpoznaje sekwencję ucieczki podczas resetowania terminala ( \x1b]50;ClearScrollback\x07), a gdybym mógł ją wykryć, mógłbym zastąpić resetją aliasem, który robi to dobrze. AFAICT, Terminal.app nie ma sekwencji resetowania, a ludzie uciekają się do absurdalnego hakowania tomów, aby włamać się do tego .

Moim ostatecznym celem jest, aby resetpraca była taka sama, niezależnie od tego, czy pracuję na OS X, czy Linux, pracuję lokalnie lub zdalnie przez SSH. (Nie chcę próbować pamiętać, które z nich są przydatne i warto reset && command-that-outputs-a-bunchmieć dostęp do pracy.) Terminal.app i iTerm rzucają klucz w tym planie, nie wdrażając resetpoprawnie.

Oznacza to, że zwykłe zastąpienie resetto nie wszystko: jeśli korzystam z komputera z systemem Linux, musi wiedzieć, czy używam, gnome-terminalczy iTerm, aby wysłać prawidłową sekwencję zmiany znaczenia.

Czy jest jakiś sposób (nawet jeśli potrzebuję ioctl), aby zapytać terminal, co to naprawdę jest?

¹ Dla celów tego pytania resetowanie powinno wyczyścić ekran, zresetować kursor i wyczyścić bufor przewijania.

Tanatos
źródło
Cóż, jest to funkcja, a nie błąd, a przynajmniej tak mówią. Co się stanie, jeśli otworzysz Terminal.appustawienia, zakładkę terminala i ustawisz linie przewijania na 0? Czy to wyłącza cały tekst powyżej bieżącego wiersza, czy tylko cokolwiek powyżej górnej części ekranu? Wiem, że nie jest to dokładnie to, o co prosiłeś, warte strzału.
nitro2k01
@ nitro2k01: Nie jestem pewien, co by to osiągnęło? I chcą scrollback. Chcę po prostu móc to wyczyścić od czasu do czasu, najlepiej za pomocą reset, ponieważ tak wiedzą moje palce.
Thanatos,
Jeśli chodzi o problem z resetowaniem: w celu wyjaśnienia oczekujesz, że resetpolecenie wyczyści zawartość przewijania wstecz emulatora terminala, ale nie jest to gwarantowane, ponieważ przewijanie wstecz jest funkcją specyficzną dla emulatora terminala, a nie część terminala. Jednak Terminal obsługuje rozszerzenie sekwencji ucieczki ED (Erase in Display), aby usunąć przewijanie do tyłu ESC [ 3 J. Możesz wyczyścić ekran, a następnie użyć go np.reset && printf '\e[3J’
Chris Page
Zobacz moją odpowiedź tutaj apple.stackexchange.com/a/113168/6883, aby uzyskać więcej informacji na temat rozszerzenia Erase in Display.
Chris Page
@ChrisPage: Zdaję sobie sprawę, że terminale są dziwaczne, ale jeśli zadeklarujesz się jako xterm(przez TERM=xterm), spodziewam się, że emulujesz nadzbiór XTerm , który usuwa resetowanie po zresetowaniu. (Tak jakbyś wysłał sekwencję ucieczki dla „niebieskiego”, spodziewałbyś się niebieskiego.) To prawda, mój xterm mówi mi to Erase is backspace., czego jestem wdzięczny, nic więcej; to po prostu denerwujące.
Thanatos,

Odpowiedzi:

10

Zastosowanie $TERM_PROGRAM.

iTerm ustawia to na iTerm.app, a Terminal.app na Apple_Terminal.

Daniel Beck
źródło
Hej, OK, to wyraźnie lepsze niż mój hack, +1 :). Hack powinien jednak działać dla każdego emulatora terminala, nie tylko tych dwóch.
terdon
Nie ma jednego sposobu na wykrycie każdego emulatora terminala. W przypadku xterm możesz na przykład sprawdzić zmienną $ XTERM_VERSION. To zajmie się prawdopodobnie trzema najpopularniejszymi emulatorami w OS X.
Chris Page
Akceptując to, bo to dobry początek. Niestety nie jest to takie łatwe, ponieważ ta zmienna nie jest dostępna w sesji SSH. Podejrzewam, że istnieją konfiguracje, które mogę edytować, aby je przesłać.
Tanatos
1
@Thanatos sshd_config„s AcceptEnvprawdopodobnie. Ponieważ to (twoja powłoka działa na innym hoście niż ten, na którym działa terminal) jest bardzo ważną informacją, naprawdę powinna być częścią pytania.
Daniel Beck
@DanielBeck: Nie jest tak ściśle; Chciałbym zrobić to samo lokalnie i zdalnie. To, co zamierzam, to to, że wpisanie „reset” w terminalu powoduje reset terminalu, bez względu na to, czy korzystam z systemu OS X czy Linux, pracuję lokalnie czy zdalnie. Zredagowałem to w pytaniu.
Thanatos,
1

$TERMnie ma nic wspólnego z obecnie działającym emulatorem terminali, jest to tylko domyślny terminal i można go ustawić na cokolwiek. Aby uzyskać nazwę uruchomionego emulatora terminala, możesz użyć psPID procesu nadrzędnego bieżącej powłoki.

UWAGA: Poniższe nie powiedzie się w OSX, ale powinno działać OK w systemie Linux

PID bieżącego procesu powłoki to $$. Stamtąd można użyć psdo wyświetlenia drzewa procesów i wydrukowania PID rodzica bieżącej sesji powłoki:

ps -axjf | awk -v pid=$$ '($2==pid){print $1}'

Następnie możesz przekazać ten identyfikator PID psi nakazać mu wydrukowanie nazwy polecenia:

ps -o comm=  $(ps -axjf | awk -v pid=$$ '($2==pid){print $1}')

To obetnie nazwę, powinno wystarczyć, abyś ją rozgryzł, ale może nie być dobre dla skryptów. Aby uzyskać pełną nazwę, możesz spróbować

ps --no-headers $(ps -axjf | awk -v pid=$$ '($2==pid){print $1}') | awk '{print $NF}'

Oto, co otrzymuję w moim systemie, używając kilku różnych terminali:

  1. terminator

    $ ps --no-headers $(ps -axjf | awk -v pid=$$ '($2==pid){print $1}') | 
       awk '{print $NF}'
    /usr/bin/x-terminal-emulator
    
  2. gnome-terminal

    $ ps --no-headers $(ps -axjf | awk -v pid=$$ '($2==pid){print $1}') | 
       awk '{print $NF}'
    /usr/lib/gnome-terminal/gnome-terminal-server
    
  3. xterm

    $ ps --no-headers $(ps axjf | awk -v pid=$$ '($2==pid){print $1}') | 
       awk '{print $NF}'
    xterm
    
terdon
źródło
Nie działa w systemie OS X:ps: illegal option -- f
Daniel Beck
@DanielBeck darn styl BSD, dzięki. Czy możesz spróbować ponownie ze zaktualizowaną wersją? Dodanie -przed opcjami powinno działać zgodnie z OSX man ps.
terdon
Lepiej, ale nadal nie działa. Format wyjściowy jest inny (PID jest $2, PPID jest $3), a proces nadrzędny powłoki może być login(w zależności od konfiguracji terminala), z różnymi parametrami. Jednak jego proces nadrzędny jest Terminal. --no-headersnie istnieje, potrzebujesz czegoś takiego tail -n1. A ponieważ wszystkie procesy z interfejsem użytkownika mają argument-psn_0_... , awk '{print $NF}'też nie działa.
Daniel Beck
@DanielBeck dobrze się pieprzy. OK, dzięki, wyjaśnię, że to nie działa w OSX.
terdon
Pamiętaj, że działa to tylko lokalnie. Nie można użyć tego podejścia, aby dowiedzieć się, jaki emulator terminala jest używany na zdalnym kliencie. Najlepszą odpowiedzią jest poszukiwanie zmiennych, takich jak $ TERM_PROGRAM (jak wspomniano w odpowiedzi @ DanielBeck) i $ XTERM_VERSION, aby wywnioskować aplikację emulatora.
Chris Page
0

Oto przenośny sposób na uzyskanie nazwy lub ścieżki procesu nadrzędnego:

iTerm 2:

$ ps -p $(ps -p $$ -o ppid=) -o comm=
/Applications/iTerm.app/Contents/MacOS/iTerm

gnome-terminal w Ubuntu:

$ ps -p $(ps -p $$ -o ppid=) -o comm=
gnome-terminal

Terminal.app:

$ ps -p $(ps -p $$ -o ppid=) -o comm=
login

Zauważ, że jeśli Terminal.app jest ustawiony na otwieranie nowych powłok z domyślną powłoką logowania, nadrzędnym procesem powłoki jest loginterminal, a nie terminal.

commKolumna jest pełna ścieżka polecenia w OS X i nazwy polecenia skrócona do 15 znaków w realizacji Procps w Linuksie.

Lri
źródło
Wypróbowałem w iTerm i otrzymałem login- czy dostosowałem kiedyś swój profil, aby uruchomić polecenie login -fp danielbeck?
Daniel Beck