Prof. Armando Luiz N. Delgado
Todo processo em UNIX é iniciado a partir do shell. Da mesma forma acontece com scripts shell.
O mecanismo que é usado por UNIX, denominado fork, está esquematizado na Figura 1:
Chamamos de subshell ao processo iniciado como filho de uma sessão shell. Como se vê na figura, todo script shell é executado em um subshell.
A noção de subshell é importante para o tratamento de variáveis de ambiente, como se verá em aulas posteriores.
Todos os processos originados a partir de um processo P qualquer são denominados filhos do processo P. Reversamente, o processo P é chamado de pai dos seus subprocessos. Quando a opção l de ps(1) é usada (sozinha ou em conjunto com outras opções) são apresentados os dois principais identificadores de um processo: o PID (Process ID) e o PPID (Parent Process ID - PID do processo pai).
OBS.:
Um comando simples é uma sequência de palavras separadas por espaços. A primeira palavra especifica o comando ou programa a ser executado e as palavras restantes são passadas ao programa como argumentos:
ls /usr/bin grep "Arnoldo" fones.txt
Um comando tem sempre um valor de retorno denominado estado de saída, exit status ou simplesmente status. Em UNIX, um status 0 (zero) significa sucesso na execução do comando. Um status diferente de 0 (zero) indica a ocorrência de alguma anormalidade ou condição de erro. Estes estados de retorno são usados principalmente em controle de fluxo de programas shell, como veremos adiante.
O parâmetro especial $? expande o valor do status do último comando executado. O parâmetro $! expande para o PID do processo executado em background mais recentemente.
Alguns comandos úteis:
Pipelines são definidos como um ou mais comandos separados por | :
[!] comando1 [ | comando2 ...]
A saída padrão de comando1 é conectada à entrada padrão de comando2.
O status da execução de um pipeline é o status do último comando executado. Se o sinal ! é usado, o status do pipeline é a negação lógica do status do último comando executado.
Lista é uma seqüência de um ou mais pipelines separados pelos operadores:
comando1 && comando2 # comando2 é executado SE, E SOMENTE # SE, comando1 retorna status 0 (zero) comando1 || comando2 # comando2 é executado SE, E SOMENTE # SE, comando1 retorna status # DIFERENTE de 0 (zero)
Os operadores && (AND) e | | (OR) têm igual precedência. Por sua vez, eles têm precedência sobre ; e & que têm igual precedência entre si.
O status de listas com ;, && e | | é o status do último comando executado na lista. No caso de listas com &, o status é sempre 0 (zero).
Um Comando composto é definido por uma das seguintes formas:
Outros operadores podem ser vistos em test(1) ou na seção CONDITIONAL EXPRESSIONS em bash(1).
Outros operadores podem ser vistos na seção ARITHMETIC EVALUATION em bash(1).
A Expansão Aritmética permite a avaliação de uma expressão aritmética e a substituição do resultado como valor de variável ou dentro de alguma linha de comando. O formato para expansão aritmética é:
$((expressão))
As expressões abaixo são portanto expressões aritméticas válidas e têm todas o mesmo efeito:
CONT=$(($CONT + 1)) CONT=$((CONT + 1)) ((CONT = CONT + 1))
A Substituição de Comando permite que a saída padrão de um comando seja colocada (inserida) no ponto em que este comando foi usado. Existem 2 formas:
`command` # usando crase ou $(command)
Quebras de linhas no início ou final da saída do comando são descartadas. Quebras de linhas no meio da saída são substituídas por espaços.
Exemplo:
wc -l `find ~ -name '*.c'` # conta as linhas de todos os # arquivos com programas em # Ling. C existentes na área # do usuário
if lista_1 then lista_A elif lista_2 then lista_B ... else lista_else fiSe o status de lista_1 for 0 (zero), executa lista_A. Senão, se o status de lista_2 for 0 (zero), executa lista_B. Se todos os status forem diferentes de 0 (zero), executa lista_else. Os blocos definidos por elif e else são opcionais.
while lista_1 do lista doneEnquanto o status de lista_1 for 0 (zero), executa lista.
until lista_1 do lista doneEnquanto o status de lista_1 for DIFERENTE de 0 (zero), executa lista.
for var in palavras do lista done
O termo palavras é considerado uma seqüência de itens separados por espaço. Um de cada vez, um item da seqüência é atribuído como valor da variavel var e lista é executada. O laço termina quando não restam mais itens a serem atribuídos à variavel var
for ((inic; condição; reinic )) do lista done
Esta estrutura é análoga à estrutura for da Linguagem C. A expressão aritmética inic é avaliada uma vez. lista é executada repetidas vezes enquanto a expressão aritmética condição for diferente de 0 (zero). Ao cada iteração, a expressão artimética reinic. O laço termina quando a expressão aritmética condição for 0 (zero).
Exemplo 1: Inserir a linha Arnoldo Orlando (011)-234-4567 no arquivo fones.txt somente se não existir nenhuma pessoa de nome Arnoldo. Após a inserção (se houver), deve-se ordenar fones.txt.
if ! grep --quiet "Arnoldo" fones.txt then echo "Arnoldo Orlando (011)-234-4567" >> fones.txt ; sort -o fones.txt fones.txt exit else echo "Já existe um Arnoldo !!!!" exit 1 fi
Outra solução, agora mais compacta:
! grep --quiet "Arnoldo" fones.txt && ( echo "Arnoldo Orlando (011)-234-4567" >> fones.txt ; sort -o fones.txt fones.txt )
Note-se o uso dos parênteses, obrigatórios neste caso para forçar a execução da seqüência de echo(1) e sort(1) somente se grep(1) não encontra Arnoldo.
Exemplo 2: Escreva um script que escreva na tela as frases Bom Dia !!, Boa Tarde !!, Boa Noite !! e Vai dormir, menino !!! de acordo com a hora do dia apresentada por date(1).
#!/bin/bash HORA=`date +"%H"` # Também poderia ser HOME=$(date +"%H") if [[ $HORA -ge 6 && $HORA -lt 12 ]] then echo "Bom Dia !!!" elif [[ $HORA -ge 12 && $HORA -lt 18 ]] then echo "Boa Tarde !!!" elif [[ $HORA -ge 18 && $HORA -le 23 ]]; then echo "Boa Noite !!!" elif [[ $HORA -ge 0 && $HORA -lt 2 ]]; then echo "Vai dormir, menino !!!" else echo "Por que tu bebe, nojento !!!" fi
Exemplo 3: Mudar para um diretório corrente específico, SOMENTE se este diretório não existir. Neste caso, antes de mudar de diretório, ele deve ser criado.
MeuDIR="${HOME}/teste" [[ -d "${MeuDIR}" ]] || (mkdir ${MeuDIR} && cd ${MeuDIR})
Mudar para um diretório corrente específico, de forma que se este diretório não existir, ele deve ser criado antes.
MeuDIR="${HOME}/teste" [[ -d "${MeuDIR}" ]] || mkdir ${MeuDIR} && cd ${MeuDIR} ou ([[ -d "${MeuDIR}" ]] || mkdir ${MeuDIR}) && cd ${MeuDIR}
Exemplo 4:
#!/bin/sh MOZILLA_HOME=/usr/local/netscape WWWBROWSER="/usr/local/bin/netscape.bin -install" XENVIRONMENT="/usr/lib/X11/app-defaults/Netscape-4.79" if [[ `basename $0` = "news" ]]; then SERVICE="-news news://${NNTPSERVER}" else SERVICE="" fi export XENVIRONMENT WWWBROWSER SERVICE MOZILLA_HOME ${WWWBROWSER} $* ${SERVICE} &
Exemplo 5:
read NOMES for i in $NOMES do echo "$i" >>nomes.txt done
Exemplo 6:
touch nomes.txt while [[ $(wc -l nomes.txt | cut -c1-7) -lt 15 ]]; do read -p "Indique no maximo 15 nomes: " NOMES for i in $NOMES do echo "$i" >>nomes.txt done done
Exemplo 7:
if [[ "${SET_WALLPAPER}" = "no" ]] then for i in Esetroot xsetroot do cmd=$(which $i) if [[ -x $cmd ]]; then XSETROOT="$cmd" if [[ "${cmd}" = "Esetroot" ]]; then XSETROOT_OPT="/usr/local/Graphics/Backgrounds/BlueStone.xpm" else XSETROOT_OPT="-solid grey" fi break fi done ${XSETROOT} ${XSETROOT_OPT} exit fi