Questão Como identificar o terminal de um script?


E não diga "$TERM" - é sempre xterm.

Como pode um bash script diz em qual terminal está sendo executado, especificamente se é iTerm, Terminal.app ou, na verdade, um xterm?

Eu pergunto porque reset não funciona¹ fora da caixa no Terminal.app e no iTerm2. O iTerm2, no entanto, reconhece uma sequência de escape para fazer uma reinicialização do terminal (\x1b]50;ClearScrollback\x07), e se eu pudesse detectá-lo, eu poderia substituir reset com um alias que faz a coisa certa. AFAICT, Terminal.app não possui uma sequência de reinicialização e as pessoas recorrem ao ridículo tom-hackery para cortar em torno desse.

Meu objetivo final aqui é ter reset funcionam da mesma forma, quer eu esteja trabalhando no OS X ou Linux, trabalhando local ou remotamente através do SSH. (Eu não quero ter que lembrar qual, e é útil fazer reset && command-that-outputs-a-bunch e ter up-enter trabalho). Terminal.app e iTerm estão jogando uma chave neste plano por não implementar reset corretamente.

Isso significa que simplesmente substituir reset não é bem isso: se eu estou em uma máquina Linux, ele precisa saber se estou usando gnome-terminal ou iTerm para enviar a seqüência de escape correta.

Existe alguma maneira (mesmo se eu precisar de um ioctl) perguntar ao terminal o que realmente é?

ThePara as finalidades desta pergunta, a reinicialização deve limpar a tela, redefinir o cursor e limpar o buffer de rolagem.


7


origem


Bem, isso é uma característica, não um bug, ou assim eles dizem. O que acontece se você abrir Terminal.appconfigurações do terminal, guia de terminal e definir linhas de rolagem para 0? Isso desativa todo e qualquer texto acima da linha atual, ou qualquer coisa acima do topo da tela? Eu sei, não é exatamente o que você pediu, vale um tiro. - nitro2k01
@ nitro2k01: Eu não tenho certeza o que isso iria conseguir? Eu quer retroceder. Eu só quero poder limpá-lo agora e depois, de preferência com reset, desde que isso é o que meus dedos sabem. - Thanatos
Quanto ao seu problema de "redefinição": para esclarecer, você está esperando reset comando para limpar o conteúdo de rolagem para trás do emulador de terminal, mas não é garantido que isso seja feito, porque o recurso de retrocesso é um recurso específico do emulador de terminal, não sendo realmente parte de um terminal. No entanto, o Terminal suporta uma extensão da seqüência de escape ED (Apagar na Exibição) para apagar a rolagem para trás. ESC [ 3 J. Você pode limpar a tela e usá-la, por exemplo, reset && printf '\e[3J’ - Chris Page
Veja minha resposta aqui apple.stackexchange.com/a/113168/6883 para mais detalhes sobre a extensão Erase in Display. - Chris Page
@ ChrisPage: Eu percebo que os terminais são coisas peculiares, mas se você se declara ser um xterm (através TERM=xterm) Espero que você emule um superconjunto do XTerm, que limpa o seu retrocesso na reinicialização. (Assim como se você mandasse a seqüência de escape para "blue", você esperaria azul.) Concedido, meu xterm me diz que Erase is backspace., o que eu sou grato mais nada faz; isso é chato. - Thanatos


Respostas:


Usar $TERM_PROGRAM.

iTerm define para iTerm.appe Terminal.app para Apple_Terminal.


8



Heh, OK, isso é claramente melhor do que o meu hack, +1 :). O hack deve funcionar para qualquer emulador de terminal, não apenas esses dois. - terdon
Não há uma maneira única de detectar cada emulador de terminal. Para o xterm, você pode verificar a variável $ XTERM_VERSION, por exemplo. Isso cuidará dos que provavelmente são os três emuladores mais populares no OS X. - Chris Page
Aceitando isso, como é um bom começo. Infelizmente, isso não é tão fácil, pois essa variável não está disponível em uma sessão SSH. Eu suspeito que existem configurações que eu possa editar para encaminhá-lo. - Thanatos
@Thanatos sshd_config's AcceptEnv provavelmente. Como isso (seu shell é executado em um host diferente daquele em que o seu Terminal é executado) é uma informação muito importante, deve ser parte da questão. - Daniel Beck♦
@DanielBeck: Não é estritamente o caso; Eu gostaria de fazer o mesmo localmente e remotamente. O que eu estou indo é que digitar "reset" em um terminal provoca uma redefinição do terminal, não importa se eu estou no OS X ou Linux, trabalhando localmente ou remotamente. Eu editei isso na pergunta. - Thanatos


Aqui está uma maneira portátil de obter o nome ou o caminho do processo pai:

iTerm 2:

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

gnome-terminal no Ubuntu:

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

Terminal.app:

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

Observe que, se Terminal.app estiver configurado para abrir novos shells com o shell de login padrão, o processo pai do shell será login e não o terminal.

o comm column é o caminho completo do comando no OS X e o nome do comando truncado para 15 caracteres na implementação do procps no Linux.


1



Tentei em iTerm e tenho login - Eu personalizei meu perfil algum tempo atrás para executar o comando login -fp danielbeck? - Daniel Beck♦


$TERM não tem nada a ver com o emulador de terminal atualmente em execução, é apenas o seu terminal padrão e pode ser configurado para qualquer coisa. Para obter o nome do emulador de terminal que você está executando, você pode usar ps para obter o PID do processo pai do seu shell atual.

NOTA: O seguinte falhará no OSX, mas deverá funcionar bem no Linux

O PID do seu processo shell atual é $$. De lá, você pode usar ps para mostrar uma árvore de processos e imprimir o PID do pai da sua sessão de shell atual:

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

Você pode então passar esse PID para ps e diga para imprimir o nome do comando:

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

Isso vai truncar o nome, deve ser o suficiente para você descobrir, mas pode não ser bom para o script. Para obter o nome completo, você pode tentar

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

Isto é o que eu recebo no meu sistema usando alguns terminais diferentes:

  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
    

1



Não funciona no OS X: ps: illegal option -- f - Daniel Beck♦
@ DanielBeck darn estilo BSD, obrigado. Você poderia tentar novamente com a versão atualizada? Adicionando um - antes que as opções funcionem de acordo com a OSX man ps. - terdon
Melhor, mas ainda não está funcionando. O formato de saída é diferente (o PID é $2, O PPID é $3), e o processo pai do shell pode ser login (dependendo da configuração do terminal), com diferentes parâmetros. Contudo, Está processo pai é Terminal. --no-headers não existe, você precisaria de algo como tail -n1. E como todos os processos com interface do usuário têm um argumento -psn_0_..., awk '{print $NF}' não funciona também. - Daniel Beck♦
@DanielBeck bem shucks então. Ok, obrigado por esclarecer que isso falha no OSX. - terdon
Note que isso só funciona localmente. Você não pode usar essa abordagem para descobrir qual emulador de terminal está sendo usado em um cliente remoto. A melhor resposta é procurar variáveis ​​como $ TERM_PROGRAM (como mencionado na resposta de @ DanielBeck) e $ XTERM_VERSION para deduzir o aplicativo de emulador. - Chris Page