Dicas de bash - linha de comando para lá de triviais.

Dicas de Bash - Linha de Comando para Uso Diário

Aqui jaz os recursos e atalhos ocultos, os quais ajudarão você executar comandos triviais em qualquer bash.

command key on keyboard
créditos da imagem: 

cool-retro-term classic bash

O shell padrão abrange muitas das variantes Linux e Unix, e principalmente o bash inclui uma ampla variedade de recursos, o que torna muito difícil decidir o que escolher como dica de linha de comando. Mas após algumas horas navegando, eis que surge um artigo gringo lindo na net que fala em especial do bash, tratando o assunto de forma fácil e descontraída. Por mim, seria fácil compartilhar apenas o link desse cara, mas tomei a decisão de ir um pouco além, fazendo a tradução completa desse artigo para o nosso idioma em português, mas com o jeitinho brasileiro... dando mais ênfase e melhorando o entedimento das dicas de bash, este poderoso shell que facilita e muito, às atividades do dia-a-dia de qualquer operador de terminal.

Fonte → artigo original em inglês: Bash tips for everyday at the command line

command key on keyboard

Utilizando o bash history like Boss

Uma das melhores maneiras de aumentar sua produtividade é aprender a usar o histórico do bash de forma mais eficaz. Com isso em mente, talvez um dos ajustes mais importantes que você pode fazer em um ambiente multiusuário seja habilitar histappend option no seu shell. Para fazer isso corretamente, basta executar o seguinte comando:

shopt -s histappend 

Isso permite que várias sessões de terminais gravem no histórico ao mesmo tempo. Na maioria dos ambientes, por padrão, esta opção é not enabled (não ativada). Isso significa que os históricos são perdidos no caso, por exemplo, se você tiver mais de uma sessão de bash aberta (local ou por SSH).

Outra tarefa comum é repetir o último comando com sudo. Por exemplo, suponha que você queira criar um diretório mkdir /etc/ansible/facts.d. A menos que você seja root, esse comando falhará. Veja que a maioria dos usuários faz assim: repete o comando com a tecla seta acima↑, e vai até o começo da linha e em seguida adiciona a palavra sudo na frente desse comando. Mas meus caros, existe uma maneira mais fácil e pro para fazer isso! Basta executar o comando assim:

sudo !!

o bash irá rodar o sudo e depois preenche automaticamente o comando anterior. Veja o exemplo abaixo:

[user@centos ~]$ mkdir -p /etc/ansible/facts.d
mkdir: cannot create directory ‘/etc/ansible’: Permission denied

[user@centos ~]$ sudo !!
sudo mkdir -p /etc/ansible/facts.d

Quando o !! é executado, o comando completo é ecoado no terminal para que você saiba o que acabou de ser executado.

Semelhante, mas usado com muito menos frequência, é o atalho !*. Isso diz ao bash que você quer que todos os *argumentos* do comando anterior sejam repetidos no comando atual. Isso pode ser útil para um comando que tenha muitos argumentos que você deseja reutilizar. Um exemplo simples é criar vários arquivos e depois alterar as permissões:

[user@centos tmp]$ touch file1 file2 file3 file4
[user@centos tmp]$ chmod 777 !*
chmod 777 file1 file2 file3 file4

Como vimos, tudo vai depender das circunstâncias de uso. É útil apenas em casos específicos e isolados, mas de qualquer forma vai economizar alguns toques de tecla.

Vamos falar agora sobre encontrar comandos no seu histórico. Aposto um picolé que a maioria de vocês ainda fazem assim:

history | grep <some command>

No entanto, existe uma maneira mais fácil de pesquisar seu histórico. Se você pressionar:

ctrl + r

O bash ativa o modo de busca reversa do seu histórico. A medida que digitar, os resultados irão aparecer na tela. Por exemplo:

(reverse-i-search)`hist': shopt -s histappend

No exemplo acima, digitei hist e combinou com o shopt comando que digitamos anteriormente. Se você continuar pressionando ctrl + r, o bash continuará a fazer a pesquisa reversa (de para trás) em busca de todas as outras ocorrências.

Nosso último truque não é um truque, mas um comando útil que você pode usar para contar e exibir os comandos mais usados no seu histórico:

[user@centos tmp]$ history | awk 'BEGIN {FS="[ \t]+|\\|"} {print $3}' | sort | uniq -c | sort -nr | head
81 ls
50 sudo
46 ssh
45 ping
39 cd
30 cabextract
20 nmap
23 vim

Neste exemplo, você pode ver que ls é de longe o comando mais utilizado no momento.

Busca e nomeação de arquivos

Você provavelmente já sabe que se digitar um comando, seja ele um arquivo ou pasta, basta pressionar a tecla tab uma vez para completar o texto para você. Isso funciona se houver uma única correspondência exata. No entanto, você pode não saber que se pressionar a tecla duas vezes, então mostrará todas as correspondências com base no que você digitou. Por exemplo:

[user@centos tmp]$ cd /lib <tab><tab>
lib/ lib64/

Isso pode ser muito útil para facilitar a busca dentro do sistema de arquivos. Outro truque bastante útil é habilitar o cdspell no seu shell. Você pode fazer isso entrando com o comando: shopt -s cdspell. Esse cara aí, te ajudará a corrigir os erros de digitação:

[user@centos etc]$ cd /tpm
/tmp
[user@centos tmp]$ cd /ect
/etc

Não é lá grande coisa... mas quebra um galho uma vez ou outra!

Depois de ter alterado com sucesso os diretórios, e se você precisar retornar ao diretório anterior? Sabemos que isso não é um problema se você não descer muito nas profundezas da árvore de diretórios. Mas se estiver em um caminho bastante profundo, do tipo... lá na casa do chapéu, como por exemplo: /var/lib/flatpak/exports/share/applications/, você pode digitar:

cd /va<tab>/lib/fla<tab>/ex<tab>/sh<tab>/app<tab>

Felizmente, o bash não é carroça, e ele se lembra do seu diretório anterior, daí você pode voltar lá simplesmente digitando cd -.

Aqui está o que acontece ao fazer isso:

[user@centos applications]$ pwd
/var/lib/flatpak/exports/share/applications

[user@centos applications]$ cd /tmp
[user@centos tmp]$ pwd
/tmp

[user@centos tmp]$ cd -
/var/lib/flatpak/exports/share/applications

Maravilha Alberto! Mas e se você tiver um monte de diretórios os quais deseja navegar neles com facilidade? Tem coisas que só bash faz por você... Há uma variável fura bolo mata piolho, que pode ser definida para ajudar nessa empreitada. Veja o exemplo abaixo:

[user@centos applications]$ export CDPATH='~:/var/log:/etc'
[user@centos applications]$ cd vsftpd
/etc/vsftpd

[user@centos vsftpd]$ cd Downloads
/home/user/Downloads

[user@centos Downloads]$ cd lighttpd
/etc/lighttpd

[user@centos Downloads]$ cd journal
/var/log/journal

No exemplo acima, eu configurei meu diretório home (indicado pelo acento: (~), /var/log e /etc. Qualquer coisa no nível superior desses diretórios, serão preenchidas automaticamente quando você os referenciar. Os diretórios que não estão na base das pastas listados em CDPATH não serão encontrados. Outra coisa, se por exemplo, o diretório que você está procurando era /etc/vsftpd/meubackup/ isso não será concluído digitando cd meubackup. Isso ocorre, porque enquanto o diretório vsftpd é encontrado sob /etc, meubackup por sua vez já não o é. Assim sendo, CDPATH é útil para chegar ao topo de uma árvore que você acessa com frequência, mas pode ser complicado gerenciá-la, quando estiver navegando por uma estrutura de pastas maior que de costume.

Por fim, vamos falar sobre dois casos de uso comuns que todo mundo faz em algum momento: Alterar uma extensão de arquivo e renomear arquivos. À primeira vista, isso pode soar como a mesma coisa, mas o bash oferece alguns truques diferentes para realizar tais tarefas.

Embora possa ser uma operação "tipo solução porca", a maioria da galera, em algum momento da vida, precisam criar uma cópia rápida de um arquivo no qual está trabalhando (velha cópia de segurança). A grande maioria faz assim: Copia exatamente o nome do arquivo e depois simplesmente anexa uma extensão de arquivo como .old ou .bak.
Há um pulo do gato, para isso no bash! Suponha que você tenha um arquivo chamado lazaro_dbase_15-06-2045_saved.dbf o qual você deseja manter uma cópia. Você poderia digitar:

cp -v lazaro_dbase_15-06-2045_saved.dbf lazaro_dbase_15-06-2045_saved.dbf.bak

Você pode fazer um trabalho mais rápido usando operações de copiar/colar, possivelmente usando um dos atalhos para repetir um argumento ou simplesmente digitando a coisa toda. No entanto, o comando abaixo deve ser ainda mais rápido quando você se acostumar a digitá-lo:

cp -v lazaro_dbase_15-06-2045_saved.dbf{,.old}

Isso vai copiar o arquivo anexando a extensão .old direto no arquivo. Isso é ótimo, com toda certeza, mas se derrepente eu mudar de idéia e quiser renomear um grande número de arquivos de uma só vez. Nesse caso, claro, você pode escrever um laço de repetição com o "for/while loop", para lidar com isso (eu prefiro usar o for).
Mas para que complicar não é mesmo? Digo isso porque existe realmente um utilitário já feito para isso, como no caso do comando rename. Há algumas diferenças no uso deste utilitário entre Debian/Ubuntu e CentOS/Fedora. A renomeação baseada em Debian usa uma sintaxe muito similar ao comando SED do Unix:

user@ubuntu-1604:/tmp$ for x in `seq 1 5`; do touch old_text_file_${x}.txt; done

user@ubuntu-1604:/tmp$ ls old_text_file_*
old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt
old_text_file_2.txt old_text_file_4.txt

user@ubuntu-1604:/tmp$ rename 's/old_text_file/alberto_new_doc/' *.txt

user@ubuntu-1604:/tmp$ ls alberto_new_doc_*
alberto_new_doc_1.txt alberto_new_doc_3.txt alberto_new_doc_5.txt
alberto_new_doc_2.txt alberto_new_doc_4.txt

Em uma distribuição CentOS ou Fedora:

[user@centos /tmp]$ for i in {1..5} ; do touch old_text_file_${i}.txt; done

[user@centos /tmp]$ ls old_text_file_*
old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt
old_text_file_2.txt old_text_file_4.txt

[user@centos tmp]$ rename old_text_file fedora_new_doc *.txt

[user@centos tmp]$ ls -1 fedora_new_doc_*

fedora_new_doc_1.txt
fedora_new_doc_2.txt
fedora_new_doc_3.txt
fedora_new_doc_4.txt
fedora_new_doc_5.txt

Não é via de regra usar laço For para se criar múltiplos arquivos. Podemos usar algo mais simples, por exemplo: $ touch leiame-{01..10}.txt

Combinações de Teclas no bash

O bash possui muitos atalhos de teclado integrados do tipo built-in shortcuts. Você pode encontrar uma lista deles digitando bind -p. Abaixo estão destacados os que geralmente são os mais usados no dia-a-dia.

    ctrl + _ (undo)
    ctrl + t (swap two characters)
    ALT + t (swap two words)
    ALT + . (prints last argument from previous command)
    ctrl + x + * (expand glob/star)
    ctrl + arrow (move forward a word)
    ALT + f (move forward a word)
    ALT + b (move backward a word)
    ctrl + x + ctrl + e (opens the command string in an editor so that you can edit it before execution)
    ctrl + e (move cursor to end)
    ctrl + a (move cursor to start)
    ctrl + xx (move to the opposite end of the line)
    ctrl + u (cuts everything before the cursor)
    ctrl + k (cuts everything after the cursor)
    ctrl + y (pastes from the buffer)
    ctrl + l (clears screen)s

Mano, Não vou perder tempo em discutir os mais óbvios. No entanto, alguns dos atalhos mais úteis que encontrei são aqueles que permitem excluir palavras (ou seções de texto) e desfazê-las. Suponha que você fosse parar um monte de serviços usando systemd, mas depois, logo em seguida você só queria começar alguns deles. Nesse caso você pode fazer algo assim:

systemctl stop httpd mariadb nfs smbd
<aperte o botão para cima para obter o command anterior>
<use 'ctrl + w' para remover os argumentos indesejados>

Mas e se você remover um a mais? Não tem problema - simplesmente use ctrl + _ para desfazer a última edição.

Os outros comandos de corte permitem que você remova tudo rapidamente do cursor até o final ou início da linha (usando Ctrl + k e Ctrl + u, respectivamente). Isso tem a vantagem de colocar o texto cortado no buffer do terminal para que você possa colá-lo mais tarde (usando ctrl + y). Esses comandos são difíceis de demonstrar aqui, então eu recomendo que você os experimente testar aí por conta própria.

Por último, mas não menos importante, gostaria de mencionar uma combinação de teclas raramente usada que pode ser extremamente útil em ambientes confinados, como por exemplo: contêineres. Se você já viu aqueles comandos sinistro, monstruosos, cheio de parâmetros que não acabam nunca, que uma vez regorgitado no shell o maldito nem cabe direito na tela...nem dá para selecionar de tão comprido... é meu caro colega, para essas situações existe também uma solução: repita o comando gigante na tela com a seta para cima, e depois Pressione ctrl + x + ctrl + e irá abrir o comando em qualquer editor que esteja definido na variável de ambiente EDITOR (padrão vim, nano ou joe). Isso permitirá que você edite um comando longo ou truncado em um editor de texto que (potencialmente) possa quebrar o texto e deixar editar ou parametrizar suas variáveis. Além disso ele permite salvar o seu trabalho e sair, da mesma forma que você faria ao trabalhar em um arquivo normal, ou seja, executaria o comando exit :wq ou ^KX para sair do editor.

Dicas diversas (miscellaneous)

Salvo os daltônicos, acho que ter cores exibidas no seu bash shell, não só deixa ele com um aspecto agradável aos olhos, mas sobretudo pode aprimorar bastante a sua experiência de uso. Se você estiver usando uma sessão que não tenha a colorização ativada, veja abaixo uma série de comandos que você pode colocar na sua .bash_profile para adicionar cores na sua sessão.
Estes comandos são bem simples e portanto dispensam qualquer explicação:

# enable colors
eval "`dircolors -b`"

# forçar o comando ls usar indicadores de cor e tipo
alias ls='ls -hF --color=auto'

# faz o comando dir funcionar igual ao windows (formato longo)
alias dir='ls --color=auto --format=long'

# faz o comando grep marcar com cor os seus resultados
export GREP_OPTIONS='--color=auto'

# adiciona um pouco de cor no comando LESS/MAN pages
export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;33m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m'
export LESS_TERMCAP_so=$'\E[01;42;30m'
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;36m'

Além de ajustar as várias opções no bash, você também pode usar alguns truques para economizar tempo. Por exemplo, para executar dois comandos consecutivos, independentemente do status de saída de cada um, use o comando ; para separar os comandos, como no exemplo abaixo:

[user@centos /tmp]$ du -hsc * ; df -h

Isso simplesmente calcula a quantidade de espaço ocupada por cada arquivo no diretório atual (soma) e, em seguida, consulta o sistema quanto ao uso de disco por dispositivo de bloco. Estes comandos serão executados independentemente de quaisquer erros gerados pelo comando du.

E se você quiser que uma ação seja tomada após a conclusão bem-sucedida do primeiro comando? Você pode usar o && para indicar que você deseja executar o segundo comando, apenas se o primeiro retornar um status de saída bem-sucedido. Por exemplo, suponha que você queira reinicializar uma máquina somente se as atualizações forem bem-sucedidas (exemplos arch e fedora):

[root@arch ~]# pacman -Syu --noconfirm && reboot
[root@fedora ~]# dnf -y  update --refresh && reboot

Às vezes, ao executar um comando, você pode querer capturar sua saída. A maioria das pessoas sabe sobre o comando multitarefatee, que copia a saída padrão para o terminal e um arquivo. No entanto, se você quiser capturar uma saída mais complexa, digamos, strace, você precisará começar a trabalhar com I/O redirection. Os detalhes do redirecionamento de I/O estão além do escopo deste pequeno artigo, mas para nossos propósitos estamos mais preocupados com o STDOUT e STDERR. A melhor maneira de capturar exatamente o que você está vendo é combinar os dois em um arquivo. Para fazer isso, use o 2>&1 redirection.

[root@arch ~]# strace -p 1140 > strace_output.txt 2>&1

Isso colocará toda a saída relevante em um arquivo chamado strace_output.txt para uma visualização posterior.

Às vezes, durante um comando de longa duração, talvez seja necessário pausar a execução de uma tarefa. Você pode usar a tecla de atalho 'stop' ctrl + z para parar (não mata) um processo (job). Este job é imediatamente parado e adicionado à fila de jobs, mas você não verá mais esse job até que o retome. Assim para que esse job possa ser retomado posteriormente, use o comando o comando foreground: fg , isso retoma o processo job para o primeiro plano.

Além disso, você também pode simplesmente pausar um job com ctrl + s. O job e sua saída permanecem no primeiro plano do terminal e o uso do shell não retorna para o usuário. Mas o job pode ser retomado pressionando ctrl + q.

Se você estiver trabalhando em um ambiente gráfico com vários terminais abertos, poderá achar útil ter atalhos de teclado para copiar e colar a saída. Para fazer isso, use os seguintes atalhos:

# Copia apenas o texto que foi devidamente marcado/selecionado
ctrl + shift + c

# Cola o texto no buffer
ctrl + shift + v

OBS: Alguns terminais gráficos (linux terminal emulators) como gnome-terminal, guake, terminator, terminology, st, e tilix, podem ser configurados para copiar automaticamente para o buffer ao selecionar ou marcar qualquer texto no terminal. Isso facilita bastante a edição dos comandos.

Suponha que na saída de um comando em execução você veja outro comando sendo executado e queira obter mais informações dele. Há algumas maneiras de fazer isto. Se este comando existir em algum lugar do sistema, você pode executar o comando which para descobrir onde esse comando está localizado no seu disco rígido:

[user@arch ~]$ which ls
/usr/bin/ls

Com esta informação, você pode inspecionar o binário com o comando file:

[user@arch ~]$ file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d4e02b88e596e4f82c6cc62a5bc4ce5827209a49, stripped

Você pode ver todos os tipos de informações, mas o mais importante para a maioria dos usuários é o absurdo (nonsense) ELF 64-bit LSB. Isso significa essencialmente que é um binário pré-compilado em oposição à um script ou outro tipo de executável. Uma ferramenta relacionada que você pode usar para inspecionar comandos é a própria ferramenta command. Simplesmente rode: command -V <command> lhe dará diferentes tipos de informações:

[user@arch ~]$ command -V ls
ls is aliased to `ls --color=auto`

[user@arch ~]$ command -V bash
bash is /usr/bin/bash

[user@arch ~]$ command -V shopt
shopt is a shell builtin

Por último, mas definitivamente não menos importante, um dos mais usados, especialmente quando se trabalha com contêineres ou em ambientes onde se tem pouco conhecimento ou controle, é o comando echo. Este comando pode ser usado para fazer tudo, desde a verificação, para garantir que um for loop irá executar a seqüência esperada ou até para permitir que você verifique se as portas remotas de um servidor estão abertas. A sintaxe é muito simples para verificar se há uma porta aberta: echo > /dev/<udp or tcp>/<server ip>/<port>.

Se a porta estiver fechada para o tipo de conexão que você está tentando fazer, você receberá a mensagem: Connection refused

user@ubuntu-02:~$ echo > /dev/tcp/192.168.1.30/22
-bash: connect: Connection refused
-bash: /dev/tcp/192.168.1.30/22: Connection refused

user@ubuntu-02:~$ echo > /dev/tcp/192.168.1.30/5900

Mas se o pacote for enviado com sucesso, não haverá nenhuma saída na tela, como ocorreu no exemplo acima, refere-se à maquina com real vnc, porta padrão 5900.

Espero que essas dicas tornem o bash mais eficiente e agradável de se usar. Há muito mais truques escondidos por aí no velho bash do que eu listei aqui.

Obrigado!

...até o próximo artigo :-P

matrix

Revisão (lista geral / super-resumo):

# History related
ctrl + r (reverse search)
!! (rerun last command)
!* (reuse arguments from previous command)
!$ (use last argument of last command)
shopt -s histappend (allow multiple terminals to write to the history file)
history | awk 'BEGIN {FS="[ \t]+|\\|"} {print $3}' | sort | uniq -c | sort -nr | head (list the most used history commands)

# File and navigation
cp /home/foo/realllylongname.cpp{,-old}
cd -
rename 's/text_to_find/been_renamed/' *.txt
export CDPATH='/var/log:~' (variable is used with the cd built-in.)

# Colourize bash

# enable colors
eval "`dircolors -b`"
# force ls to always use color and type indicators
alias ls='ls -hF --color=auto'
# make the dir command work kinda like in windows (long format)
alias dir='ls --color=auto --format=long'
# make grep highlight results using color
export GREP_OPTIONS='--color=auto'

export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;33m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m' # end the info box
export LESS_TERMCAP_so=$'\E[01;42;30m' # begin the info box
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;36m'

# bash shortcuts
    shopt -s cdspell (corrects typoos)
    ctrl + _ (undo)
    ctrl + arrow (move forward a word)
    ctrl + a (move cursor to start)
    ctrl + e (move cursor to end)
    ctrl + k (cuts everything after the cursor)
    ctrl + l (clears screen)
    ctrl + q (resume command that is in the foreground)
    ctrl + s (pause a long running command in the foreground)
    ctrl + t (swap two characters)
    ctrl + u (cuts everything before the cursor)
    ctrl + x + ctrl + e (opens the command string in an editor so that you can edit it before it runs)
    ctrl + x + * (expand glob/star)
    ctrl + xx (move to the opposite end of the line)
    ctrl + y (pastes from the buffer)
    ctrl + shift + c/v (copy/paste into terminal)

# Running commands in sequence
&& (run second command if the first is successful)
; (run second command regardless of success of first one)

# Redirecting I/O
2>&1 (redirect stdout and stderr to a file)

# check for open ports
echo > /dev/tcp/<server ip>/<port>
`` (use back ticks to shell out)

# Examine executable
which <command>
file <path/to/file>
command -V <some command binary> (tells you whether <some binary> is a built-in, binary or alias)