You are currently browsing the category archive for the ‘Shell’ category.

O tr é um comando muito versátil, especialmente útil para apagar caracteres indesejados.

Digamos que você peça ao usuário para digitar o número do CPF. Esse número poderá vir das mais diferentes formas, com espaços, hífens, pontos, ponto-e-vírgula e sabe mais o que que a criatividade da pessoa pode produzir.

Para preservar sua sanidade, tudo o que você quer são os números, ignorando toda a pontuação, espaços e outros caracteres. Com o tr é fácil.

$ echo 123.456.789-00 | tr -d 0-9
..-

A opção -d apaga coisas e o 0-9 representa os números de zero a nove. Ah, note que não é necessário o uso de colchetes ao redor “[0-9]“, como muita gente costuma fazer.

Mas este comando apagou os números e deixou a pontuação, quando o desejado é o justamente o contrário. Então basta usar a opção -c, que inverte o sentido da expressão, fazendo o tr apagar tudo exceto os números:

$ echo 123.456.789-00 | tr -c -d 0-9
12345678900

Com o comando cut é fácil extrair o primeiro, segundo ou qualquer outro caractere da linha. Porém, é preciso saber a posição exata deste caractere para informá-la ao cut.

$ echo abcdef | cut -c 1
a
$ echo abcdef | cut -c 2
b
$ echo abcdef | cut -c 3
c

E se você quiser extrair o último caractere, independente do tamanho da linha em questão?

A primeira solução é saber qual a posição exata deste último caractere. Supondo que a linha está guardada em uma variável, use o modificador # para obter o tamanho do conteúdo da variável, ou seja, o tamanho da linha.

$ linha="abcdef"
$ echo ${#linha}
6

Agora sim, você pode usar esse truque para obter o último caractere:

$ echo $linha | cut -c ${#linha}
f

Há ainda uma outra solução, que envolve pensar diferente. Em vez de contornar a limitação do cut apelando para a expansão de variáveis do shell, que tal modificar nosso problema para que ele se enquadre nas possibilidades do cut?

  • O problema: extrair o último caractere.
  • O cut: sabe extrair o primeiro caractere.
  • Solução: tem como fazer o último caractere ser o primeiro?

O comando rev inverte a linha, fazendo com que o último caractere seja o primeiro, e vice-versa:

$ echo abcdef | rev
fedcba

Agora sim, já que nossa linha-problema foi modificada para satisfazer o cut, podemos usá-lo normalmente:

$ echo abcdef | rev | cut -c 1
f

No Linux é muito comum um daemon, durante sua execução, criar um arquivo .pid dentro de /var/run. Dentro do arquivo syslogd.pid, por exemplo, contém o PID da instância do syslogd em execução. Usa-se este mecanismo para impedir que duas instâncias do mesmo processo rodem simultaneamente e conflitem na obtenção de recursos.

Porém, a simples existência do arquivo .pid não garante que o processo esteja em execução. Ele pode ter sido fechado de uma forma inesperada e não o apagou. Então, temos que ler o conteúdo do arquivo .pid e certificar que o processo com aquele número de PID está em execução.

A forma mais fácil de fazer isso é enviando o sinal 0 para o processo. O sinal 0 é especial e não é de fato enviado, caso contrário o processo destinatário poderia ser fechado se não tratasse o sinal, mas o retorno da função indica se um sinal real seria enviado com sucesso. O código a seguir exemplifica como aplicar a técnica em C:

int main(int argc, char **argv)
{
    unsigned int pid;

    if (argc != 2) {
        printf("Uso: %s PID\n", argv[0]);
        return 1;
    }

    pid = (unsigned int) atoi(argv[1]);

    if (!kill(pid, 0))
        printf("%d esta em execucao.\n", pid);
    else
        printf("%d nao esta em execucao.\n", pid);

    return 0;
}

A mesma técnica pode ser aplicada no terminal:

$ kill -0 123
$ echo $? # Imprime 0 se o PID 123 existir, 1 caso contrario

Note que você precisa ter permissão para enviar um sinal para um determinado processo. Para um usuário sem privilégios especiais, a dica só vai funcionar para verificar a existência de processos iniciados pelo próprio usuário.

Você tem uma lista de números, um por linha, e precisa somá-los:

$ cat numeros.txt
3
5
7
9
11
13
15

Primeiro, coloque todos em uma única linha:

$ paste -s numeros.txt
3	5	7	9	11	13	15

Agora mude o delimitador (-d) de TAB para “+”, para compor a expressão aritmética de soma:

$ paste -s -d + numeros.txt
3+5+7+9+11+13+15

Agora sim, basta passar a expressão resultante para a calculadora do shell:

$ paste -s -d + numeros.txt | bc
63
(LANG=C; ifconfig eth0|grep "inet addr"|cut -f2 -d:|cut -f1 -d" ")

Para pegar o endereço de uma interface diferente basta colocar o nome dessa interface no lugar de eth0.

É interessante que o comando seja colocado entre parênteses, para não modificar o valor da variável de ambiente LANG da sua sessão de shell atual (que controla o idioma em que os aplicativos são exibidos).

Algumas vezes uma máquina está conectada à internet de forma indireta, através de um proxy ou mesmo um firewall com múltiplos links de internet. Os comandos seguintes permitem descobrir qual IP a máquina está utilizando na internet.

Utilizando o wget:

echo $(wget -qO- http://www.whatismyip.com/automation/n09230945.asp)

Utilizando o lynx (ou o links2):

lynx -dump http://www.whatismyip.com/automation/n09230945.asp

Para calcular a raiz quadrada de um número em Shell, use o comando bc. Ele é uma calculadora completa, que entre outras funções úteis, tem a sqrt (de “square root”).

Basta mandar o comando pela entrada padrão (STDIN) e o resultado aparece na saída padrão (STDOUT):

$ echo "sqrt(16)" | bc
4

$ echo "sqrt(144)" | bc
12

Para guardar o número e o resultado em variáveis, faça assim:

numero=144
resultado=$(echo "sqrt($numero)" | bc)

echo "A raiz quadrada de $numero é $resultado"

Palavras-chave: Shell, agrupar, redirecionamento

Ao fazer log da execução de um programa ou compor um arquivo de texto que agrupe a saída de vários comandos, é comum ver a seguinte estrutura ser utilizada:

comando-1 >> arquivo
comando-2 >> arquivo
comando-3 >> arquivo

Tudo bem, isso funciona. Mas há uma maneira mais prática e eficiente de se fazer o mesmo, que é agrupar todos os comandos e redirecioná-los para o arquivo de log uma única vez:

(comando-1 ; comando-2 ; comando-3) >> arquivo

Basta colocar todos os comandos dentro de um par de parênteses para que o shell os encare como um grupo único.

Veja um exemplo da aplicação dessa técnica e perceba uma outra vantagem: o script fica mais legível.

Log Ineficiente

echo "Iniciando em $(date)" >> /tmp/log.txt
rm -vr /tmp/becape/* >> /tmp/log.txt
~/bin/meu-becape.sh --tudo >> /tmp/log.txt
echo "Tamanho do becape: $(du -hs /tmp/becape)" >> /tmp/log.txt
echo "Terminado em $(date)" >> /tmp/log.txt

Log Eficiente

(
echo "Iniciando em $(date)"
rm -vr /tmp/becape/*
~/bin/meu-becape.sh --tudo
echo "Tamanho do becape: $(du -hs /tmp/becape)"
echo "Terminado em $(date)"
) >> /tmp/log.txt
Add to Technorati Favorites

Assuntos