ct

    Guia: Programando em Shell Script


     Carlos E. Morimoto
     22/03/2005


    Alterando arquivos de configuração


    Caso sejam executados pelo root, os scripts podem também alterar arquivos de configuração do sistema, modificando entradas existentes ou incluindo novas configurações. O comando mais usado para fazer substituições é o sed. Para simplesmente adicionar linhas no final do arquivo, você pode usar o echo, o mesmo comando que usamos para escrever mensagens na tela.

    Por exemplo, um erro comum ao acessar partições do Windows formatadas em NTFS é a partição ser montada com permissão de leitura apenas para o root. Você só consegue ver os arquivos abrindo o gerenciador de arquivos como root, o que é desconfortável.

    Isto acontece porque o default do mount é montar partições NTFS e FAT com permissão de acesso apenas para o root. Para que outros usuários possam visualizar os arquivos, é necessário montar incluindo a opção "umask=000".

    A tarefa é relativamente simples, basta abrir o arquivo "/etc/fstab" e adicionar a opção na linha referente à partição.

    Ao invés de:

    /dev/hda1 /mnt/hda1 auto noauto,users,exec 0 0

    Ficaríamos com:

    /dev/hda1 /mnt/hda1 auto noauto,users,exec,umask=000 0 0

    Até aí tudo bem. O problema é que este parâmetro pode ser usado apenas em partições FAT ou NTFS. Se você usar em partições formatadas em ReiserFS ou EXT, elas não montam.

    Ou seja, ao fazer isso via script, precisamos primeiro ter certeza de que a partição está formatada em NTFS, antes de alterar o "/etc/fstab".

    Uma forma de fazer isto é verificando a saída do comando "fdisk -l", que mostra detalhes sobre as partições, como em:

    # fdisk -l /dev/hda

    Disk /dev/hda: 20.0 GB, 20003880960 bytes
    255 heads, 63 sectors/track, 2432 cylinders
    Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot Start End Blocks Id System

    /dev/hda1 * 1 401 3221001 7 HPFS/NTFS
    /dev/hda2 402 1042 5148832+ 83 Linux
    /dev/hda3 1116 2432 10578802+ 5 Extended
    /dev/hda4 1043 1115 586372+ 83 Linux
    /dev/hda5 1116 1164 393561 82 Linux swap / Solaris
    /dev/hda6 1165 2432 10185178+ 83 Linux

    Podemos então usar o grep para filtrar a saída e carregar o resultado dentro de uma variável, de modo que ela contenha alguma coisa apenas se a partição testada estiver formatada em NTFS, como em:

    winntfs=`fdisk -l /dev/hda | grep hda1 | grep NTFS`

    Note que aqui estamos usando três comandos diferentes, ligados por pipes. Esta é uma prática comum na hora de filtrar a saída de comandos, já que é difícil chegar a um único comando que faça de uma vez tudo o que você precisa. Neste exemplo, o "grep hda1" vai remover todas as linhas, deixando apenas a linha referente à partição "/dev/hda1". O "grep NTFS" verifica a linha e deixa-a passar apenas se a partição estiver formatada em NTFS, visto que, caso ela esteja formatada em outro sistema, a string com a identificação do sistema de arquivos será outra.

    Usamos em seguida uma função que verifica se a variável é não nula (-n). Por causa dos dois comandos do grep, ela só conterá algum texto se o /dev/hda1 estiver formatado em NTFS. Usamos então um if com o comando do sed para alterar a linha no "/etc/fstab" caso positivo:

    if [ -n "$winntfs" ]; then

    # O gigantesco comando abaixo forma uma única linha:
    sed -e 's/\/dev\/hda1 \/mnt\/hda1 auto noauto,users,exec 0 0/\/dev\/hda1 \/mnt\/hda1 auto noauto,users,exec,umask=000 0 0/g' /etc/fstab > /tmp/fstab2

    rm -f /etc/fstab; mv /etc/fstab2 /etc/fstab
    fi

    Veja que a linha do sed é um pouco longa. A sintaxe básica para fazer substituições é:

    $ sed -e 's/texto/novo-texto/g' arquivo > novo-arquivo

    Ele sempre gera um novo arquivo com as alterações, por isso depois do comando você ainda precisa remover o arquivo original e renomear o novo arquivo. Caso dentro da string de texto a substituir existam barras ou caracteres especiais, use barras invertidas (\) como no exemplo, para demarcá-los. As barras invertidas indicam que o caractere seguinte não deve ser interpretado.

    O comando "sed -e 's/texto/novo-texto/g' arquivo > novo-arquivo" que vimos, simplesmente substitui todas as instâncias da palavra "texto" por "novo-texto". Você precisa certificar-se que a palavra só existe dentro do contexto que imagina, caso contrário, o comando pode facilmente causar acidentes. O ideal é que você seja sempre o mais específico possível.

    Para deletar uma linha, use o comando "sed -e '/texto/D' arquivo > novo-arquivo, como em:

    $ sed -e '/Load "dri"/D' /etc/X11/xorg.conf > /tmp/xorg.conf.new

    O sed suporta também alguns caracteres especiais. O "^", por exemplo, indica o início da linha. Para remover todas as linhas que começam com "#" (ou seja, linhas com comentários), use:

    $ sed -e '/^\#/D' /etc/lilo.conf > /tmp/lilo.conf.new

    Note que precisei incluir uma barra invertida antes do "#". Para remover todas as linhas em branco de um arquivo, use:

    $ sed -e '/^$/D' /etc/X11/xorg.conf > /tmp/xorg.conf.new

    É possível também combinar vários comandos do sed usando o pipe, fazendo com que a saída de um passe também pelo outro antes de ser escrita no arquivo final. Para remover todas as linhas com comentários e todas as em branco, o comando seria:

    $ sed -e '/^\#/D' /etc/lilo.conf | sed -e '/^$/D' /etc/lilo.conf > /tmp/lilo.conf.new

    Para eliminar espaços repetidos num arquivo (útil caso você queira filtrar alguma informação usando o cut):

    $ sed -r 's/ +/ /g' /etc/X11/xorg.conf > /tmp/xorg.conf.new

    Para substituir todos os espaços por quebras de linha (representadas pelo "\n" dentro do sed):

    $ sed -e 's/ /\n/g' /tmp/myfreq >> /tmp/myfreq2

    Para remover todas as quebras de linhas de um arquivo, fazendo com que tudo fique numa única linha (o que pode ser útil para facilitar a localização de determinadas informações, que originalmente estavam em várias linhas separadas), use esta função inventada pelo Thobias Salazar:

    $ sed ':a;$!N;s/\n//;ta;' ~/.synergy.conf > /tmp/synergy.conf

    Você pode usá-la junto com a função sed 's/^/ /', que adiciona um caracter no início de cada linha. Ao usar esta função ('s/^/ /'), você pode substituir o espaço entre a segunda e terceira barra por outro caracter, ou conjunto de caracteres, como em sed 's/^/\&\&/'. Veja um exemplo de uso dos dois combinados, que formata o arquivo ".synergy.conf" (usado pelo Synergy):

    $ cat .synergy.conf | sed 's/^/ /' | sed ':a;$!N;s/\n//;ta;'

    section: screens kurumin: semprao: end section: links semprao: right = kurumin kurumin: left = semprao end section: aliases kurumin: 192.168.1.102 end

    O sed, aliado ao grep, pode ser usado também para criar scripts inteligentes, que verificam a configuração do sistema e, se necessário alteram as linhas necessárias.

    Por exemplo, para instalar o FreeNX server no Debian, seguindo o tutorial que publiquei no Guia do Hardware (http://www.guiadohardware.net/tutoriais/109/), é necessário adicionar uma linha no "/etc/apt/sources.list", com o repositório do Kanotix que possui os pacotes. Estes pacotes possuem algumas dependências do Unstable, de forma que o sources.list precisa conter também a linha correspondente.

    Este é o trecho do script do ícone mágico que escrevi para o Kurumin, que verifica e altera o arquivo caso necessário:

    # Verifica se o sources.list contém entradas para o unstable,
    # se não tiver adiciona:

    unstable=`sed '/^$/d' /etc/apt/sources.list | sed '/#/d' \
    | grep "unstable main contrib"`

    if [ -z "$unstable" ]; then
    echo "deb http://ftp.us.debian.org/debian unstable \
    main contrib non-free" >> /etc/apt/sources.list
    remover="1"
    fi

    # Adiciona o repositório do freenx
    kano=`sed '/^$/d' /etc/apt/sources.list \
    | sed '/#/d' | grep "project/kanotix/unstable/"`

    if [ -z "$kano" ]; then
    echo 'deb http://debian.tu-bs.de/project/kanotix/unstable\
    / ./' >> /etc/apt/sources.list
    removerkano="1"
    fi

    # Instala o freenx a partir do unstable do Kano
    apt-get update
    apt-get install -t unstable freenx
    nxsetup --install --setup-nomachine-key

    # Remove o unstable e/ou o repositorio do kano, se adicionado:
    if [ "$remover" = "1" ]; then
    rm -f /tmp/sources.list
    sed -e '/deb http:\/\/ftp.us.debian.org\/debian unstable main contrib non-free/D' \
    /etc/apt/sources.list > /tmp/sources.list

    rm -f /etc/apt/sources.list
    cp /tmp/sources.list /etc/apt/sources.list
    apt-get update
    fi

    if [ "$removerkano" = "1" ]; then
    rm -f /tmp/sources.list
    sed -e '/deb http:\/\/debian.tu-bs.de\/project\/kanotix\/unstable\/ \.\//D' \
    /etc/apt/sources.list > /tmp/sources.list

    rm -f /etc/apt/sources.list
    cp /tmp/sources.list /etc/apt/sources.list
    apt-get update
    fi

    Logo no início do script são definidas duas variáveis, "unstable" e "kano" que são usadas para verificar se as linhas dos repositórios de que o script precisa estão ou não disponíveis no script. Esta informação é usada para decidir se se elas devem ser adicionadas ou não.

    Localizar linhas no arquivo pode ser um pouco complexo. Como explicar para o script que ele deve ignorar a linha caso esteja comentada e como fazer ele ignorar pequenas variações nas linhas (como os códigos de país nas linhas dos repositórios do Debian)?

    Prevendo isso, estou usando um "super pipe", que usa dois comandos do sed para remover as linhas em branco (sed '/^$/d'), as linhas comentadas (sed '/#/d') e em seguida o grep para filtrar apenas a linha que estou procurando. Para tornar o script mais flexível, faço a busca por apenas parte da linha, isso evita que a função deixe passar uma entrada similar, mas com outro mirror:

    unstable=`sed '/^$/d' /etc/apt/sources.list | sed '/#/d' \
    | grep "unstable main contrib"`

    Caso as variáveis estejam vazias (-z), significa que as linhas não existem no arquivo. Neste caso, entram em ação as funções que coloco em seguida, que adicionam as linhas necessárias e criam mais duas variáveis ("remover" e "removerkano") que são usadas para que o script "lembre" que fez as alterações e remova as linhas no final, deixando o arquivo da forma como estava originalmente.

    A função para remover as linhas usa novamente o sed, desta vez numa sintaxe mais simples, que simplesmente deleta as linhas (sed -e '/texto/D') anteriormente adicionadas. O sed não é capaz de salvar as alterações diretamente no arquivo, por isso é sempre necessário salvar num arquivo temporário e depois substituir o arquivo original por ele.

    Você pode ver mais dicas e exemplos do uso do sed na página do Aurélio: http://aurelio.net/sed/.

cb
HOME