0% acharam este documento útil (0 voto)
4 visualizações20 páginas

Processo em Shell Script Linux

O documento aborda o controle de tarefas em Shell Script Linux, destacando a importância da automatização de processos e a utilização de corrotinas e subshells. Ele explica como os sinais são usados para gerenciar processos, permitindo a execução simultânea e a manipulação de tarefas. Exemplos práticos são fornecidos para ilustrar o uso de comandos e a interação entre processos no ambiente Linux.

Enviado por

felipesilverio17
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
4 visualizações20 páginas

Processo em Shell Script Linux

O documento aborda o controle de tarefas em Shell Script Linux, destacando a importância da automatização de processos e a utilização de corrotinas e subshells. Ele explica como os sinais são usados para gerenciar processos, permitindo a execução simultânea e a manipulação de tarefas. Exemplos práticos são fornecidos para ilustrar o uso de comandos e a interação entre processos no ambiente Linux.

Enviado por

felipesilverio17
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

PROGRAMAÇÃO

EM AMBIENTES DE
REDES DE
COMPUTADORES

Maikon Lucian Lenz


Processo em Shell
Script Linux
Objetivos de aprendizagem
Ao final deste texto, você deve apresentar os seguintes aprendizados:

„„ Descrever o controle das tarefas por meio de scripts.


„„ Definir o que são corrotinas e subshells.
„„ Comparar exemplos de scripts que utilizem corrotinas e subshells na
automação das tarefas.

Introdução
Cada execução de um script, ainda que com o mesmo código-fonte e
finalidade, é tratada como um processo independente no Linux, cujo
controle exige o uso de sinais, que, no Shell Script, são enviados por meio
do comando kill e interceptados pelo comando trap. Essa separação
possibilita a execução de múltiplas tarefas de maneira simultânea por
meio de corrotinas ou subshells criados para cada uma delas.
Neste capítulo, você estudará o que são os sinais enviados para os pro-
cessos e como eles podem ser utilizados para automatizar tarefas. Ainda,
verá de que forma corrotinas podem ser empregadas para um melhor
desempenho de tarefas executadas simultaneamente e como o Linux
lida com diversas instâncias do mesmo interpretador criando subshells.

1 Controle de tarefas
Em geral, a automatização de tarefas em ambientes de rede e gerenciamento
de sistemas Linux é muito importante, pois possibilita agilizar a resolução
de problemas e reaproveitar a mesma solução diversas vezes, sem a necessi-
dade de redigir ou recordar todo o código novamente. Entretanto, trata-se de
atividades normalmente muito específicas e muito adaptáveis a cada situação
(ROBBINS; BEEBE, 2005).
2 Processo em Shell Script Linux

Apesar de a eficiência ser normalmente maior na execução de progra-


mas compilados, as linguagens de script, como o caso do Shell Script para
Linux, costumam se destacar diante das demais linguagens nos processos
de automatização de tarefas. Por não exigirem um processo de compilação,
esses códigos podem ser testados mais rapidamente e facilitam o processo de
desenvolvimento continuado, muito útil quando se precisa lidar com múltiplas
versões de sistema e adaptar soluções anteriores de script para cada uma delas,
algo que requer dinamismo. Ainda, precisamos destacar que as linguagens
de script, utilizando um interpretador e estando em um nível de abstração
ainda maior que os programas de linguagem compilada, lidam com objetos
complexos, como diretórios e arquivos, com maior facilidade, além de se
adaptarem melhor ao tipo de problema a ser resolvido pelos programadores
de scripts (ROBBINS; BEEBE, 2005).
Além das vantagens que envolvem diretamente o processo de programação
em si, o Shell Script permite que o usuário combine a execução de múltiplos
scripts por técnicas de redirecionamento de entradas e saídas, troca de sinais
e execução de corrotinas ou subshells, as quais garantem ainda maior flexi-
bilidade na automatização de tarefas, um processo naturalmente dinâmico.
Assim, o desenvolvedor pode facilmente dividir as tarefas a serem exe-
cutadas em vários processos, vinculados entre si na ordem e da maneira que
melhor convir para cada situação, o que seria muito mais complexo e exigiria
planejamento e visão tão amplos das possibilidades e situações envolvidas
que demandaria um tempo excessivo de desenvolvimento.
Para compreender melhor como é possível tirar proveito das vantagens do
Shell Script em comparação a outros tipos de linguagem, devemos entender
como o sistema operacional, em especial o Linux, lida com a execução dos
programas.
As atividades de um sistema operacional dividem-se em processos e cada
um deles consiste basicamente em uma instância de um programa em execução.
Assim, se duas instâncias do interpretador são utilizadas ao mesmo tempo,
cada uma delas é tratada como um processo separado, ainda que tenham
origem no mesmo código/programa em execução (BOVET; CESATI, 2006).
O processo visa a utilizar os recursos disponibilizados pelo sistema ope-
racional de modo a cumprir com as funções programadas pelo desenvolvedor
(BOVET; CESATI, 2006).
Processo em Shell Script Linux 3

Cada processo é identificado por um valor inteiro, que serve de ponteiro


para este, conhecido como PID (do inglês, process identification). O número
de identificação de um processo é uma unidade maior que o último processo
criado, normalmente limitado pelo valor máximo de 32767, momento a partir
do qual o sistema operacional passa a utilizar valores previamente associados
a outros processos, mas que não se encontram mais em execução (BOVET;
CESATI, 2006).
Ao longo do capítulo, você verá muitos exemplos utilizando comandos e
processos em Shell Script Linux, como na Figura 1, na qual são listados os
processos em execução pelo comando ps no terminal.

Figura 1. Listagem dos processos em execução


por meio do comando ps.

Em geral, não se pode perceber a criação de novos processos na execução da


maioria dos comandos utilizados, visto que a execução é rápida demais para que
o usuário tente listar os processos existentes antes que o atual seja encerrado.
No entanto, pode-se facilmente comprovar a existência de um novo processo
ao invocarmos um comando qualquer se este for executado em segundo plano
pelo operador &. Nesse caso, o terminal exibirá o PID do processo criado no
exato momento em que este é invocado, como visto na Figura 2, em que o
comando de listagem de arquivos ls é invocado em segundo plano.

Figura 2. Processo criado durante a execução do comando ls em segundo


plano.
4 Processo em Shell Script Linux

Na Figura 2, são visíveis dois identificadores: o primeiro (entre colchetes)


se refere ao trabalho (BLUM, 2008), ou seja, o comando executado nessa ins-
tância do terminal; e o segundo (236) diz respeito à identificação do processo
no sistema operacional.
Assim, ao invocar um comando em segundo plano por meio do operador
&, não apenas um novo processo é criado em todas as execuções de um pro-
grama qualquer, como também surge um novo trabalho dentro do terminal.
É sempre mais fácil controlar a execução de um programa pelo seu identificador
de trabalho do que pelo seu identificador de processo (NEWHAM, 2005).
Por meio do número de identificação do trabalho, podemos levar um pro-
cesso em execução em segundo plano (background) para o primeiro plano
( foreground) pelo comando fg (BLUM, 2008). A Figura 3 mostra a execução
de um comando ping em segundo plano seguido de um comando ls antes
mesmo da conclusão do comando ping, mas que, mesmo assim, retorna a
listagem de arquivos. No entanto, ao trazer o trabalho de volta para o primeiro
plano de execução, as tentativas de execução do comando ls são ignoradas.

Figura 3. Comando ping com intervalo de 5 segundos entre cada tentativa executado
em segundo plano por meio do operador &. O processo é trazido de volta para o plano
principal pelo comando fg.

No exemplo da Figura 3, o comando fg não recebe nenhum parâmetro e


será direcionado ao processo criado mais recentemente (BLUM, 2008). Uma
vez que só existe um processo em execução em segundo plano, não haverá
qualquer confusão quanto à instância a que está se referindo, no entanto,
se executados dois processos em segundo plano como na Figura 4, deve-se
informar de maneira explícita qual o processo que está sendo transferido do
segundo plano para o plano principal.
Processo em Shell Script Linux 5

Figura 4. Mudança de plano de execução para um trabalho específico.

Assim como há um comando para listar os processos em execução, existe


um comando para listar os trabalhos invocando o comando jobs, como na
Figura 5. O parâmetro –l junto ao comando jobs inclui na listagem o número
do processo vinculado ao trabalho.

Figura 5. Listagem dos trabalhos em execução por meio do comando jobs.

No entanto, ao trazer um processo de voltar para o plano principal,


o usuário perderá o controle do terminal até que o processo em execução seja
encerrado ou suspenso, condição que pode ser atingida pelas teclas de atalho
Ctrl+Z (ROBBINS; BEEBE, 2005). No exemplo da Figura 6, dois comandos
ping são executados em segundo plano, o que permite que o usuário execute
qualquer outro comando normalmente, como é o caso da primeira invocação
do comando jobs. Em seguida, ambos os processos são levados de volta
para o plano principal e suspensos pelo atalho de teclado Ctrl+Z (evidenciado
pelos caracteres ^Z na tela do terminal). Agora, se invocado o comando jobs,
os dois processos aparecem como parados (em inglês, stopped).
6 Processo em Shell Script Linux

Figura 6. Suspensão de processos em execução no plano principal.

Com os trabalhos/processos interrompidos e o controle do terminal re-


tomado, o usuário pode, inclusive, enviá-los novamente para execução em
segundo plano por meio do comando bg, seguido dos parâmetros que espe-
cificam cada um deles, conforme a Figura 7.

Figura 7. Retomada da execução dos processos suspensos em segundo plano.

Ainda, é possível finalizar um processo que esteja sendo executado no


plano principal por meio da combinação do atalho de teclado Ctrl+C. Ambos
os atalhos, tanto para suspender quanto para encerrar, consistem no envio de
sinais de um processo para outro. Um sinal é definido como a troca de uma
mensagem entre dois processos (BLUM, 2008).
Processo em Shell Script Linux 7

No Linux, os processos são protegidos para evitar que uma eventual interferência mútua,
o que poderia corromper os dados utilizados por um programa e levar a inúmeros
problemas de segurança (BOVET; CESATI, 2006). Ainda assim, algum controle é admitido
por meio de mecanismos de troca de sinais entre os processos.

A quantidade e os tipos de sinais existentes variam entre as versões do


Linux, no entanto a maioria deles é utilizada pelo próprio sistema operacional
para orientar quanto a anormalidades ocorridas, por exemplo, um endereço de
memória inválido, uma divisão por zero e outros eventos de vital importância
para a execução do processo (NEWHAM, 2005).
Apesar de poucos tipos de sinais disporem de teclas de atalho vinculadas,
tanto o tipo de sinal quanto o processo a que este deverá ser enviado podem
ser devidamente especificados por meio do comando kill.
Os tipos de sinais têm um número, bem com um nome, de identificação
que varia entre as versões do Linux, embora os nomes sejam mais constantes
que os identificadores numéricos (NEWHAM, 2005).
Por exemplo, podemos suspender um processo em segundo plano enviando
um sinal de nome SIGSTOP pelo comando kill e identificando o PID do
alvo e, na sequência, retomar a sua execução, sem alterar o plano em que isso
ocorre, por meio de um sinal de nome SIGCONT (Figura 8).

Figura 8. Suspensão de um processo em segundo plano e retomada da sua execução


na sequência.
8 Processo em Shell Script Linux

Alguns desses sinais são passíveis de detectar pelo algoritmo para que
sua ocorrência resulte na execução de outro trecho de código a ser definido
pelo desenvolvedor do algoritmo. Isso se dá por meio do comando trap, cujo
primeiro parâmetro corresponde ao trecho de código a ser executado e os
parâmetros seguintes especificam o tipo de sinal que deverá ser monitorado
(NEWHAM, 2005).
A Figura 9 mostra o resultado da utilização por um usuário do atalho Ctrl+C,
que envia um sinal de nome SIGINT ou, simplesmente, INT (BLUM, 2008).
O comando trap, anterior ao atalho, determina que, toda vez que este sinal
seja detectado, o terminal exiba a mensagem Sinal INT recebido! em
uma nova linha.

Figura 9. Captura de um sinal enviado para um processo.

Além de especificar uma sequência de comandos, pode ser utilizada uma


função ou um script a ser executado pelo comando trap facilitando a or-
ganização do código. Na Figura 10, as mensagens que seriam exibidas pelo
comando trap da Figura 9 foram inseridas em uma função que pode ser
invocada por este.
Em ambos os casos (Figuras 9 e 10), ao pressionar as teclas Ctrl+C (o que
pode ser percebido no terminal pelos caracteres ^C), a mesma mensagem foi
exibida. Além disso, a Figura 10 demonstra que o sinal enviado pela tecla de
atalho é o mesmo do comando kill -INT 6333, em que 6333 corres-
ponde à identificação do processo que está enviando a mensagem; nesse caso,
a instância atual do interpretador/terminal conforme listado pelo comando ps.
Processo em Shell Script Linux 9

Figura 10. Desvio de um sinal para execução da função mensagem.

Obviamente, um sinal pode ser programado para simplesmente ter sua


função ignorada, sem especificar comando nem função para o comando
trap. Uma vez que o primeiro parâmetro sempre corresponde ao comando
a ser executado, isso pode ser atingido por meio do emprego de aspas simples
sem qualquer texto entre elas, como no exemplo da Figura 11, em que um laço
de repetição infinito exibe uma mensagem a cada 1 segundo — ao receber o
sinal de Ctrl+C sem qualquer desvio, a execução do laço será interrompida.
No entanto, se especificado um desvio para os sinais do tipo INT com um
comando vazio '', nada será feito, e as mensagens continuarão a ser exibidas.
Percebe-se que, apesar de os processos serem protegidos para evitar o
comprometimento dos dados, é possível interferir no comportamento de
outro processo por meio de sinais. Essa técnica é amplamente utilizada pelo
sistema operacional quando da necessidade de informar o processo quanto
à ocorrência de eventos anormais, como a tentativa de acessar um endereço
de memória inválido, mas também pode ser aproveitada pelo desenvolvedor.
10 Processo em Shell Script Linux

Figura 11. Uso do comando trap com comando vazio.

2 Corrotinas
Trata-se de processos executados simultaneamente, como no caso em que
dois ou mais comandos são agrupados por meio de um pipeline (comandos
separados pelo operador |) (NEWHAM, 2005).
Utilizando o pipe, a saída de um comando serve de entrada para outro e
sugere, portanto, a troca de informações entre os dois processos criados, por
exemplo, na linha de comando ls | more, o processamento se dá em cinco
etapas (NEWHAM, 2005), descritas a seguir.

1. Cria um processo para cada um dos comandos.


2. Manipula os descritores de arquivo de modo a direcionar a saída do
primeiro comando para o segundo.
3. Inicia o processo 1, dado pelo comando ls.
4. Inicia o processo 2, dado pelo comando more.
5. Aguarda que ambos os processos sejam concluídos.

Outra maneira simples de implementar corrotinas consiste em utilizar


processos em segundo plano, embora, nesse caso, se um script invoca duas
funções ou comandos em corrotina, mas a última linha de comando termina a
execução antes que todos os demais processos em corrotina tenham terminado,
o trabalho criado pelo script será encerrado ainda que outros processos por
ele iniciados estejam em execução (NEWHAM, 2005).
Processo em Shell Script Linux 11

O Exemplo 1 (assim como os demais) demonstra um código em Shell


Script para o Linux, em que são criadas duas funções: teste1 e teste2,
que atuam para exibir uma mensagem após aguardar um intervalo de tempo.
A diferença reside no fato de que a função teste1 espera 10 segundos até
exibir a mensagem, enquanto a função teste2 aguarda apenas 5 segundos,
de modo que a função teste2 termina sua execução mais cedo.

# Exemplo 1 – script: corrotina, que invoca duas funções


simultâneas,
# mas com tempo de execução diferentes, invocando primeiro a
mais rápida
function teste1()
{
sleep 10
echo ''
echo Fim do Teste1
}
function teste2()
{
sleep 5
echo ''
echo Fim do Teste2
}
teste2 &
teste1

Ao final desse script (Exemplo 1), é invocada inicialmente a função teste2


em segundo plano seguida da função teste2 em primeiro plano, permitindo
que estas sejam executadas simultaneamente. Certamente, a função teste2
termina primeiro que a função teste1 e assim o trabalho criado para invocar
o script corrotina em segundo plano na Figura 12 é encerrado sem grandes
problemas após todos os processos (teste1 e teste2) terem sido executados.
Podemos observar na Figura 12 que, ao listar os trabalhos em execução,
o trabalho criado para executar o script de nome corrotina somente é encerrado
após a conclusão da execução de ambas as funções.
12 Processo em Shell Script Linux

Figura 12. Todas as corrotinas concluídas ao encerrar o trabalho iniciado


pelo script em segundo plano.

Porém, caso o script do Exemplo 1 seja modificado para que as ordens


de execução teste1 e teste2 sejam trocadas, conforme o Exemplo 2,
o resultado obtido será outro:

# Exemplo 2 – script: corrotina, que invoca duas funções


simultâneas,
# mas com tempo de execução diferentes, invocando primeiro a
mais lenta
function teste1()
{
sleep 10

echo ''
echo Fim do Teste1
}
function teste2()
{
sleep 5
echo ''
echo Fim do Teste2
}
teste1 &
teste2
Processo em Shell Script Linux 13

A execução do script é concluída assim que a função teste2, invocada


em primeiro plano, também é concluída. No entanto, o processo criado pela
função teste1, invocada em segundo plano, ainda será executado, apesar
de a listagem de trabalhos indicar que a execução do script corrotina encerrou
após a primeira mensagem recebida (mensagem Fim do Teste2), conforme
verificado na Figura 13.

Figura 13. Apesar de o trabalho estar concluído após a mensagem Fim


do Teste2, o processo iniciado pela função de teste1 ainda está em
execução nesse momento.

Provavelmente, a execução de múltiplas rotinas simultaneamente resultará


em um melhor aproveitamento dos recursos de processamento disponíveis
da máquina, acelerando a execução do script como um todo. Entretanto,
é importante que, ao concluir um trabalho, o usuário tenha a garantia de que
todos os processos envolvidos foram também concluídos.
Para controlar de maneira adequada a situação demonstrada na Figura 13,
pode-se recorrer ao comando wait, cuja finalidade consiste em aguardar a
conclusão de todos os processos em segundo plano antes de prosseguir com
a execução do restante do script (ALBING; VOSSEN, 2017). Se inserido no
final do código, como no Exemplo 3, o encerramento do trabalho iniciado
pelo script corrotina só se dará após a execução de ambas as funções, assim
como no Exemplo 1, mesmo que a função teste2 termine antes que a função
teste1.
14 Processo em Shell Script Linux

# Exemplo 2 – script: corrotina, que invoca duas funções


simultâneas,
# mas com tempo de execução diferentes, invocando primeiro a
mais lenta
function teste1()
{
sleep 10
echo ''
echo Fim do Teste1
}

function teste2()
{
sleep 5
echo ''
echo Fim do Teste2
}
teste1 &
teste2
wait

Dessa vez, conforme a Figura 14, a mensagem Fim do Teste2 se dará


primeiro, assim como nos demais exemplos (1 e 2), mas, apesar de ser a última
função invocada no script entre as funções teste1 e teste2, não é a última
do arquivo em si, restando ainda a linha de comando wait, que apenas será
concluída no momento em que todos os processos em segundo plano tenham
sido finalizados. Nesse caso, ela aguarda até que a função teste1 termine.
Ao contrário do exemplo em que fora utilizada a técnica de pipeline,
a corrotina dos Exemplos 1 a 3 não envolve a comunicação entre as corrotinas,
motivo pelo qual a situação demonstrada pela Figura 13 é possível, quando
o trabalho é encerrado antes mesmo que todos os processos tenham sido
concluídos.
Processo em Shell Script Linux 15

Figura 14. Comando wait utilizado para aguardar o término de exe-


cução de todos os processos em segundo plano.

Apesar de em alguns casos não existir qualquer troca de informações entre corrotinas,
estas ainda são amplamente utilizadas para um melhor aproveitamento dos recursos
computacionais disponíveis de modo a diminuir o tempo de processamento, espe-
cialmente em computadores com múltiplos núcleos (NEWHAM, 2005).

3 Subshells
Várias instâncias de um mesmo interpretador podem ser executadas ao mesmo
tempo. Ao abrir o terminal pela primeira vez, você está criando uma instância
do interpretador utilizado. No entanto, se invocá-lo novamente, mesmo dentro
do terminal recém-criado, uma nova instância do interpretador é criada dentro
da primeira.
A Figura 15 mostra que a primeira listagem de processos indica ape-
nas uma instância do interpretador bash, utilizado nesse caso. No entanto,
ao invocar o comando bash, outro processo é iniciado contendo a instância
de um novo interpretador.
16 Processo em Shell Script Linux

Figura 15. Duas instâncias do mesmo inter-


pretador executadas dentro de um terminal.

Essa nova instância é conhecida como subshell e pode ser encerrada assim
como seria a primeira, pelo comando exit (BLUM, 2008). A Figura 16 mostra
qual é o processo atual utilizado e como o terminal retornará para a instância
inicial se a segunda for encerrada.

Figura 16. Encerrado um subshell.

Ao invocar um script por meio do comando source, o código é executado


como se tivesse sido digitado diretamente no terminal, portanto, como um
código inserido na instância do interpretador atual. Porém, se invocado dire-
tamente o comando, este iniciaria um subshell que seria encerrado tão logo o
script fosse inteiramente executado (ROBBINS; BEEBE, 2005).
Processo em Shell Script Linux 17

Independentemente da situação, o terminal aguardaria pela execução do


script invocado, seja diretamente na instância atual pelo comando source,
seja em um subshell. A execução de comandos ou scripts em segundo plano,
por sua vez, também cria um subshell, mas com a vantagem adicional de
garantir o controle sobre o terminal mesmo durante a execução do processo,
o que não seria possível ao criar um subshell de maneira direta, já que o inter-
pretador principal aguardaria pelo encerramento do segundo para prosseguir
(NEWHAM, 2005).
Repare, ainda, na Figura 16, que a pasta em que o terminal está locali-
zado não foi alterada ao se criar um subshell. A recíproca, no entanto, não é
verdadeira, ao encerrar um subshell que tenha alterado de diretório, pode-se
observar, pela Figura 17, que o diretório do terminal no interpretador principal
ainda é o mesmo do momento em que o subshell foi criado.

Figura 17. Mudança de diretório no subshell não


afeta a instância principal.

Além do diretório atual, outras características são herdadas da instância


atual pelo subshell criado, como variáveis de ambiente, descritores de arquivos
abertos e sinais ignorados. Todas as demais variáveis, bem como os sinais que
dispõem de um tratamento específico e não são simplesmente ignorados, não
são herdadas pelo subshell (NEWHAM, 2005).
Ainda, podemos criar um subshell aninhado (do inglês, nested subshell),
no qual, apesar de o código pertencer a mesma instância atual, parte ou a
totalidade dele está envolvida entre parênteses, indicando que o código deverá
ser executado em um subshell e partilhar de todas as restrições de acesso
a variáveis. A Figura 18 mostra que a variável var não existe para o shell
principal se declarada entre parênteses, ao contrário da segunda tentativa
sem os parênteses.
18 Processo em Shell Script Linux

Figura 18. Criação de uma variável em um subshell.

Aplicando as características que envolvem processos e trabalhos, bem


como a possibilidade de direcionar um código ou não para um subshell, você
poderá desenvolver códigos que tenham um controle maior, especialmente
sobre as variáveis e os desvios de sinais declarados. Sinais estes que podem
ser usados não apenas para encerrar ou suspender a execução e um script
demorado, mas também para possibilitar que o desenvolvedor controle um
processo por meio de outros.
Além disso, o uso de corrotinas é capaz de diminuir o tempo de execução
de um script aproveitando toda a disponibilidade de recursos que a máquina
provê, especialmente em computadores de múltiplos núcleos de processamento.

ALBING, C.; VOSSEN, J. P. bash cookbook: solutions and examples for bash users. Sebas-
topol: O’Reilly, 2017. 726 p.
BLUM, R. Linux command line and shell scripting. Indianapolis: Wiley, 2008. 840 p.
BOVET, D. P.; CESATI, M. Understanding the Linux kernel. 3. ed. Sebastopol: O’Reilly, 2006.
923 p.
NEWHAM, C. Learning the bash shell: Unix shell programming. 3. ed. Sebastopol: O’Reilly,
2005. 352 p.
ROBBINS, A.; BEEBE, N. H. F. Classic shell scripting: automate your Unix tasks. 3. ed.
Sebastopol: O’Reilly, 2005. 558 p.
Conteúdo:

S a
SOLUCÕES
G a H EDUCACIONAIS
INTEGRADAS

Você também pode gostar