8 de julho de 2010

A complexa variável de exibição PS1

O shell é uma parte muito importante de qualquer sistema GNU/Linux. Mesmo usuários que nem sequer sabem que o usam dependem muito dele, pois há diversos utilitários gráficos baseados no simple e poderoso zenity, uma ferramenta de linha de comando capaz de criar janelas e diálogos tão úteis quanto belos e em GTK!

Ainda assim, ainda há e sempre haverá tarefas que ficam muito mais fáceis com o shell, tais como abrir todos os arquivos com um determinado componente no meio do nome, ou renomear apenas aqueles arquivos que tenham sido criados entre 4 dias atrás e ontem, retirando um prefixo de seus nomes.

É claro que existem aquelas pessoas que acreditam que o shell vai desaparecer e geralmente, são as pessoas que não o usam e acabaram de descobrir que ele existe, de forma que ainda não perceberam o poder que se esconde naquele assustador monte de caracteres feiosos. :)

Para quem já conhece o shell e se sente confortável trabalhando nele, talvez seja hora de torná-lo ainda mais útil. Vejamos neste post como tornar mais poderoso um componente sempre presente no shell: o prompt.

Se você usa uma distribuição como Ubuntu ou Fedora, seu prompt é, convenhamos, meio sem graça. Óbvio que ninguém deseja que seu prompt tenha letras piscando ou emita um som a cada vez que você pressionar ENTER, mas agregar novos recursos ao prompt pode ser uma boa ideia.


 

Configuração atual

Para começar, descubra como é definido seu prompt atual. No shell, digite:

  echo $PS1


O resultado deve ser algo como:

  \u@\h \W \$


Que exibe o seu prompt como:

  usuário@máquina ~ $


Ou seja, \u refere-se ao usuário, enquanto \h refere-se ao host. ;) Já o \W pode ser associado a "Where?" ("onde?"). Nota: provavelmente vem de "working directory", ou diretório de trabalho. Mas "where" é mnemonicamente mais fácil.

Isso é muito mais útil do que apenas um simples $ como prompt, certo?


 

Primeiros testes

Vamos começar fazendo testes sem compromisso, alterando o prompt sem registrar nada, apenas para ganharmos intimidade com a coisa. Experimente executar o comando:

 
export PS1='[\u@\h] [\W] \$'


Atenção às aspas simples!

O interessante é que o resultado já vem logo em seguida:

  [pablo@beren] [~] $



Já notou um problema incômodo? Com esse prompt, qualquer coisa escrita pelo usuário já aparece grudada no cifrão (ou "dólar", como preferir), assim:

 
[pablo@beren] [~] $cd /var/spool



Feio, não? Isso nos leva à descoberta número 1: espaços importam.

Corrija já o seu prompt:

 
[pablo@beren] [~] $export PS1='[\u@\h] [\W] \$ '



Notou o espaço após o \$ ?
Com isso, agora teremos sempre um espaço separando o $ dos comandos digitados.


 

 Outras variáveis

Nosso prompt ainda pode ser mais informativo. Suponha que você esteja trabalhando com bibliotecas, circulando entre os diretórios /usr/lib/ e /lib/. Com nosso prompt atual, veja o que acontece:


  [pablo@beren] [~] $ cd /usr/lib
  [pablo@beren] [lib] $ pwd
  /usr/lib
  [pablo@beren] [lib] $ cd /lib
  [pablo@beren] [lib] $ pwd
  /lib


Percebeu o problema? Nossa variável \W só mostra o nome do diretório mais "alto" da nossa localização. Então, se estivermos em /lib/, /usr/lib/ ou /home/pablo/dir1/dir2/dir3/lib/, o prompt será sempre:

 
[pablo@beren] [lib] $


A forma mais prática para melhorar isso é substituir aquele \W por um \w, muito mais informativo:

  [pablo@beren] [lib] $ export PS1='[\u@\h] [\w] \$ '
  [pablo@beren] [/lib] $ cd /usr/lib
  [pablo@beren] [/usr/lib] $ 


Agora, nosso \w sempre vai mostrar o caminho completo do diretório, não importa onde estejamos. E no seu home/, ele mostrará o bom e velho til:

  [pablo@beren] [/usr/lib] $ cd
  [pablo@beren] [~] $





A página de manual do Bash 4.0 (man bash ou man 1 bash) lista todas as variáveis que podem entrar no prompt (mais especificamente, na seção PROMPTING):

  \a     an ASCII bell character (07)
  \d     the date in "Weekday Month Date" format (e.g.,  "Tue  May
         26")
  \D{format}
         the  format  is  passed  to strftime(3) and the result is
         inserted into the prompt string; an empty format  results
         in a locale-specific time representation.  The braces are
         required
  \e     an ASCII escape character (033)
  \h     the hostname up to the first `.'
  \H     the hostname
  \j     the number of jobs currently managed by the shell
  \l     the basename of the shell's terminal device name
  \n     newline
  \r     carriage return
  \s     the name of the shell, the basename of  $0  (the  portion
         following the final slash)
  \t     the current time in 24-hour HH:MM:SS format
  \T     the current time in 12-hour HH:MM:SS format
  \@     the current time in 12-hour am/pm format
  \A     the current time in 24-hour HH:MM format
  \u     the username of the current user
  \v     the version of bash (e.g., 2.00)
  \V     the release of bash, version + patch level (e.g., 2.00.0)
  \w     the  current  working  directory,  with $HOME abbreviated
         with a tilde (uses the $PROMPT_DIRTRIM variable)
  \W     the basename of the current working directory, with $HOME
         abbreviated with a tilde
  \!     the history number of this command
  \#     the command number of this command
  \$     if the effective UID is 0, a #, otherwise a $
  \nnn   the character corresponding to the octal number nnn
  \\     a backslash
  \[     begin  a sequence of non-printing characters, which could
         be used to embed a terminal  control  sequence  into  the
         prompt
  \]     end a sequence of non-printing characters


Como você pode ver, é possível criar prompts que apitam (variável \a) e prompts com múltiplas linhas (\n):

  [pablo@beren] [~] $ export PS1='[\u@\h] \n[\w] \a\$ '
  [pablo@beren] 
  [~] $ 


Além disso, a variável \h pode ser substituída pro \H para exibir também o domínio onde a máquina se encontra.


 

Que horas são?

Uma variável que considero particularmente útil é a hora local. Como as minhas atividades no computador vão bem além do trabalho, alcançando sempre o terreno da diversão, é comum eu extrapolar o tempo na frente do monitor. Por isso, ter um relógio sempre presente é uma ótima ideia. Por isso, eu uso:

  [pablo@beren] [~] $ export PS1='[\t] [\u@\h] [\w] \$ '
  [17:54:38] [pablo@beren] [~] $ 
 
 


Contabilidade

Quantos comandos você já digitou neste terminal? Perdeu a conta? Se esse dado for importante para você, pode ser útil exibi-lo constantemente:

  [17:54:38] [pablo@beren] [~] $ export PS1='[\u@\h] [\w] \# \$ '
  [pablo@beren] [~] 36 $ 


Se você usa o comando history para executar novamente comandos já utilizados, pode ser interessante também mostrar o número do comando atual na lista do history com a variável \!. Note como esse número pode ser diferente do \#:

  [pablo@beren] [~] $ export PS1='[\u@\h] [\w] C:\# H:\! \$ '
  [pablo@beren] [~] C:37 H:538 $


Usei C:\# H:\! para deixar claro para o usuário que o primeiro número diz respeito aos comandos usados neste shell, enquanto que o segundo número é relativo ao histórico do Bash.


 

Agora em cores

Usar múltiplos campos no prompt é sempre uma boa ideia, e quanto mais campos, melhor, certo? Nem sempre.

Se você incluir informações demais no seu prompt do Bash, pode acabar tendo dificuldade na hora de encontrar a informação desejada devido justamente ao excesso de informações. Uma saída interessante para esse problema é usar cores em vez de delimitadores (como os colchetes [ e ] que usamos até agora).

Vejamos como incluir uma cor. Vamos colorir de azul o nome da máquina? É pra já!

  [pablo@beren] [~] C:37 H:538 $ export PS1='u@\[\033[00;34m\]\h\[\033[00m\] [\w] \$ '
  pablo@beren [~] $


Posso ouvir os gritos de desespero. Realmente, essas sequências de escape \[\033[00;34m\] e \[\033[00m\] são muito menos intuitivas do que <azul> e </azul>. Mas é justamente isso que elas fazem. Na realidade, a segunda sequência de escape, \[\033[00m\], é equivalente ao que seria um </cor>, porque "fecha" todas as definições de cor que existam à sua esquerda.

Vamos testar usando duas cores: vermelho para o nome de usuário (\u) e azul para o hostname (\h).

  pablo@beren [~] $ export PS1='\[\033[00;31m\]\u@\[\033[00;34m\]\h\[\033[00m\] [\w] \$ '
  pablo@beren [~] $ 


Interessante, não?


 

Peso e cor

No Bash, é possível manipular três aspectos do texto: peso da fonte, cor da fonte e cor do fundo. Até aqui, mexemos apenas na cor da fonte.

Você já deve ter percebido que, nas sequências de "abertura" de cor, o número logo após o ponto-e-vírgula é o que define a cor, certo? Nos exemplos acima, \[\033[00;34m\] deixou a fonte azul, enquanto \[\033[00;31m\] deixou-a vermelha. Como encontrar esses números?
A resposta está, por exemplo, nos comentários do arquivo /etc/DIR_COLORS:

  # Below are the color init strings for the basic file types. A color init
  # string consists of one or more of the following numeric codes:
  # Attribute codes:
  # 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed
  # Text color codes:
  # 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white
  # Background color codes:
  # 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white


Esses comentários chamam o peso da fonte de "attribute", com as possibilidades "nenhum" (00), "negrito" (=cor mais forte, 01), "sublinhado" (04), "piscante" (05), "invertido" (cores invertidas, 07) e "oculto" (08).

Os atributos são definidos no primeiro campo da sequência de escape. Ou seja:
\[\033[00;34m\] resulta em azul, com peso normal, e \[\033[01;34m\] resulta em azul mais forte (negrito).

Experimente e perceba a diferença.

Fique à vontade também para experimentar os demais atributos. Mas por favor, não use nenhum outro além de 00 e 01, pelo bem da estética. :)
Em seguida, os comentários listam a cor das fontes (finalmente!):


CódigoCor
30Preto
31Vermelho
32Verde
33Amarelo
34Azul
35Magenta
36Ciano
37Branco

Experimente um pouco as novas cores e seus resultados com e sem negrito. Em específico, veja a diferença entre o amarelo com e sem negrito.

Notou que o 01;33 é amarelo de verdade, enquanto o 00;33 está mais para marrom?


Para um mapa mais completo das cores, a documentação do bash no TLDP fez uma listagem maior:

Cor normal (00)Com com negrito (01)
Preto 00;30Cinza escuro 01;30
Azul 00;34Azul claro 01;34
Verde 00;32Verde claro 01;32
Ciano 00;36Ciano claro 01;36
Vermelho 00;31Vermelho claro 01;31
Roxo 00;35Roxo claro 01;35
Marrom 00;33Amarelo 01;33
Cinza claro 00;37Branco 01;37


 

Mais atenção

Isso pode ser ainda mais útil. Sabe aquele servidor onde você entra e frequentemente se esquece de que está dentro dele? Que tal chamar muita atenção quando você estiver lá? Vejamos como fazer bom uso da cor de fundo para isso.





Lembrando o comentário no arquivo /etc/DIR_COLORS:

  # Background color codes:
  # 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white


Confira o resultado do comando:

  export PS1='\[\033[00;31m\]\u@\[\033[01;34;41m\]\h\[\033[00m\] [\w] \$ '
  pablo@beren [~] $ 


Como dizem por aí, é
Letra azul sobe fundo vermelho: aí está uma forma eficaz de chamar a atenção e evitar a execução de comandos na máquina errada.

Como você pode ver, para aplicar uma cor de fundo, basta incluir o número da cor desejada após a cor da fonte, como fizemos no trecho \[\033[01;34;41m\]\h.


 

Fim dos testes. Onde eu gravo?

Gostou das alterações? Então não feche seu terminal. Grave seu novo prompt num arquivo para usá-lo sempre.

O arquivo que define a variável PS1 depende da sua distribuição. Algumas o fazem no arquivo /etc/profile, outras em /etc/bashrc, e tem aquelas que usam o /etc/bash/bashrc, além de diversas outras variações.


 

Sem root

Se você não possui acesso de root na máquina, só lhe resta o seu arquivo particular de definições de ambiente do Bash, o ~/.bashrc. Edite-o e inclua ao final do arquivo a linha:

  export PS1='definições que você mais gostou'


 

Com root

Já se você tem acesso de root e deseja alterar a variável de todos os usuários (isto é, daqueles que não possuírem uma definição diferente em seus arquivos ~/.bashrc), vai precisar encontrar o arquivo que faz isso globalmente.

Uma forma eficaz de encontrar esse arquivo é a força bruta: usar o grep no diretório /etc/ inteiro.

 
grep --recursive PS1 /etc


No meu sistema Gentoo, trata-se do /etc/bash/bashrc.


 

Prompt diferente para o root

É sempre uma boa ideia alertar o usuário quando ele está usando a todo-poderosa conta root. No arquivo /etc/bash/bashrc do meu sistema Gentoo (em outras distribuições o arquivo pode ser outro) podemos incluir as linhas:

 
if [[ ${EUID} == 0 ]] ; then
      # Quando o usuário é ROOT:
      PS1='\[\033[00;33m\]\t \[\033[01;33;41m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
  else
      # Quando o usuário é normal:
      PS1='\[\033[00;33m\]\t \[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
  fi


Resultado: ao usar a conta de root, o prompt deixa ainda mais chamativo o campo com os nomes do usuário e da máquina:

12:34:56 root@beren ~ $

Outra possibilidade ainda mais apelativa seria:

  if [[ ${EUID} == 0 ]] ; then
      # Quando o usuário é ROOT:
      PS1='\[\033[00;33m\]\t \[\033[01;33;41m\]ATENÇÃO:ROOT@\h\[\033[00m\]\[\033[01;34m\] \w \$\[\033[00m\] '
  else
      # Quando o usuário é normal:
      PS1='\[\033[00;33m\]\t \[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
  fi

Resultado do usuário root:

12:34:56 ATENÇÃO:ROOT@beren ~ $
Usuário normal:

12:34:56 pablo@beren ~ $
Note que, no primeiro prompt, foi necessário fechar as cores (\[\033[00m\]) logo após a variável \h, caso contrário o fundo vermelho se estenderia até o final do prompt.


 

Conclusão

O Bash é, além de muito prático e poderoso, altamente personalizável. Variáveis como hora local e nome completo da máquina podem acrescentar mais utilidades ao prompt do que as distribuições fazem por padrão, e o uso de cores pode ajudar a evitar desastres como comandos em máquinas erradas ou sob contas enganadas.


Se você gostou de personalizar seu prompt, talvez tenha interesse em conhecer o shell também livre ZSH, com ainda mais opções de personalização.


Outro programa interessante para esse fim é o bashish, um mecanismo para criação e aplicação de temas (sim, temas!) ao Bash, não somente ao prompt.
Por favor, deixe sua opinião sobre este blog ou este post nos comentários no fim deste artigo.

Créditos externos a esse artigo --> Blog developer

Nenhum comentário: