 | Nível: Intermediário Thomas Myer, Principal, Triple Dog Dare Media
19/Mai/2009 Saiba como integrar melhor os scripts com as ferramentas de linha de comando. Examine o uso de shell_exec(), exec(), passthru()e system()a transmissão segura de informações para a linha de comando; e a recuperação segura de
informações da mesma.
Se já trabalhou com PHP, você sabe que ela é uma excelente ferramenta para a criação de páginas da Web cheias de recursos. Como
uma linguagem de script geral, a PHP:
- É fácil de aprender.
- Possui muitas estruturas poderosas, como CakePHP e CodeIgniter, para tornar você tão produtivo quanto qualquer programador
de Rails.
- Pode se comunicar com MySQL, PostgreSQL, Microsoft® SQL Server e até Oracle.
- Integra-se facilmente com estruturas JavaScript, como script.aculo.us e jQuery.
Em algum ponto, porém, você quer fazer mais, ou é forçado a fazer mais. Com isso, quero dizer que você tem que
trabalhar diretamente com o sistema de arquivos do servidor onde a PHP está em execução. Você acaba precisando trabalhar
com arquivos no sistema de arquivos, entender quais processos estão em execução ou executar alguma outra tarefa.
Primeiro, você está contente utilizando comandos como file() na PHP para abrir arquivos. Em
algum ponto, a única maneira de se obter algo pronto é poder executar comandos shell no servidor e receber de volta algum
tipo de saída. Por exemplo, talvez você precise saber quantos arquivos existem em certo diretório. Ou talvez você precise
saber quantas linhas foram gravadas em um grupo de arquivos de log. Ou talvez seja necessário operar nesses arquivos para
copiá-los em outro diretório ou utilizar rsync para transportá-los para outro local.
Em "Command-line PHP? Yes, you can!,"
Roger McCoy mostra como utilizar PHP diretamente a partir da linha de comando — sem a necessidade de um navegador da Web. Neste
artigo, eu abordo o tema a partir de outro ponto de vista, mostrando como se integrar fortemente com comandos shell subjacentes, e a integração de quaisquer valores de retorno às suas interfaces e seus processos.
Esse tipo de coisa só funciona se você estiver em execução no Linux®, Berkeley Software Distribution (BSD) ou algum
ou tipo de UNIX®. Eu suponho que você esteja executando em uma pilha Linux-Apache-MySQL-PHP (LAMP). Sua milhagem pode variar se você
estiver executando um tipo de UNIX diferente, pois a disponibilidade dos comandos varia de instalação para instalação. Eu sei que muitos
de vocês também estão desenvolvendo no Mac OS X, que executa um tipo de BSD, por isso mantenho os comandos de amostra o mais genéricos
possível para garantir a portabilidade.
Visão Geral da Linha de Comandos
A PHP Command Line Interface (CLI) Server Application Programming Interface (SAPI) foi liberada de forma experimental
na PHP V4.2.0. A partir da V4.3.0, ela passou a ser totalmente suportada e ativada por padrão. A PHP CLI SAPI permite que você
desenvolva scripts baseados em shell e até em desktop desenvolvidos com PHP. De fato, é possível criar ferramentas na PHP que
são executadas diretamente a partir da linha de comandos. Trabalhando dessa maneira, os desenvolvedores do PHP podem ser
tão produtivos quanto Perl, AWK, Ruby ou algum outro tipo de shell nesse contexto.
Este artigo considera as ferramentas baseadas em PHP que permitem que você tenha acesso ao ambiente shell subjacente
e ao sistema de arquivos no qual a PHP está em execução. A PHP fornece inúmeras funções para a execução de comandos externos,
entre eles shell_exec(), exec(), passthru() e system(). Esses comandos são semelhantes, mas fornecem interfaces diferentes para o programa externo que você está
executando. Cada um desses comandos gera um processo-filho para executar o comando ou script designados por você e cada um
captura a saída do seu comando conforme ela é gravada na saída padrão (stdout).
shell_exec()
O comando shell_exec() é, de fato, apenas um alias para o operador backtick (`). Caso já tenha
feito algum shell script ou Perl, você sabe que pode capturar a saída de outros comandos dentro de operadores backtick. Por
exemplo, a Listagem 1 mostra como utilizar o backtick para obter uma contagem de palavras para cada arquivo de texto (.txt) no
diretório atual.
Listagem 1. Utilizando Backtick para Contagem de Palavras
#! /bin/sh
number_of_words=`wc -w *.txt`
echo $number_of_words
#resultado seria algo como:
#165 readme.txt 388 results.txt 588 summary.txt
#e assim por diante....
|
Em seu script PHP, é possível executar esse comando simples dentro de shell_exec(), conforme
mostrado na Listagem 2, e obter os resultados necessários, supondo que você tenha alguns arquivos de texto no mesmo diretório.
Listagem 2. Executando o Mesmo Comando em shell_exec()
<?php
$results = shell_exec('wc -w *.txt');
echo $results;
?>
|
Como é possível ver na Figura 1, você obtém os mesmos resultados que obteria do shell script. Isso é porque o
shell_exec() permite que você execute um programa externo via shell e retorne o resultado como uma cadeia.
Figura 1. Resultados da Execução de um Comando Shell através de shell_exec()
Observe que você obtém os mesmos resultados se apenas utilizar operadores backtick, conforme mostrado abaixo.
Listagem 3. Utilizando Apenas os Operadores Backtick
<?php
$results = `wc -w *.txt`;
echo $results;
?>
|
A Listagem 4 mostra um método ainda mais simples.
Listagem 4. Um Método Mais Simples
<?php
echo `wc -w *.txt`;
?>
|
É importante notar que muito do que você pode fazer na linha de comando UNIX ou em um shell script é permitido aqui. Por
exemplo, você pode utilizar barras verticais para cadeias junto com comandos. Você pode até criar um shell script com todas as suas
operações nele e apenas chamar o shell script, com ou sem argumentos, conforme necessário.
Por exemplo, se quiser contar apenas as palavras nos cinco primeiro arquivos de texto no diretório, você pode utilizar
uma barra vertical (|) para unir os comandos wc e head. Além
disso, você pode agrupar os resultados de saída em tags pre para deixá-los mais agradáveis no navegador
da Web, conforme mostrado abaixo.
Listagem 5. Um Comando Shell Mais Complexo
<?php
$results = shell_exec('wc -w *.txt | head -5');
echo "<pre>".$results . "</pre>";
?>
|
A Figura 2 mostra os resultados da execução do script da Listagem 5.
Figura 2. Resultados da Execução de um Comando Shell Mais Complexo através de shell_exec()
Mais adiante neste artigo, você vai aprender como passar argumentos para esses scripts utilizando PHP. Por enquanto,
você pode pensar nisso como uma forma de executar comandos shell, desde que se lembre de que só verá a saída padrão. Se houver algum erro
em seu script ou comando, você não verá um erro padrão (stderr) a menos que utilize nele uma barra vertical
para stdout.
passthru()
O comando passthru() permite que você execute um programa externo e exiba seus resultados na
tela. Você não precisa utilizar echo ou return para ver esses resultados;
eles são simplesmente exibidos no navegador. É possível incluir um argumento opcional, uma variável que contenha o código de
retorno do programa externo, como 0 para êxito, que fornece um melhor mecanismo para depuração.
Na Listagem 6, utilizo o comando passthru() para executar o pequeno script de contagem de
palavras que executei na seção anterior. Como você pode ver, também incluí uma variável $returnval
que contém o código de retorno.
Listagem 6. Utilizando o Comando passthru() para Executar o Script de
Contagem de Palavras
<?php
passthru('wc -w *.txt | head -5',$returnval);
echo "<hr/>".$returnval;
?>
|
Observe que eu não preciso repetir nada. Os resultados terminam na tela, conforme mostrado
abaixo.
Figura 3. Resultados da Execução do Comando passthru() com um
Código return
Na Listagem 7, apresento um pequeno erro no código removendo o hífen antes do 5 na parte superior do script.
Listagem 7. Introduzindo um Erro no Script de Contagem de Palavras
<?php
//introduzimos um erro abaixo (removendo o - do comando superior)
passthru('wc -w *.txt | head 5',$returnval);
echo "<hr/>".$returnval;
?>
|
Observe que o script não é executado da forma desejada. Conforme mostrado na Figura 4, você obtém uma tela em branco e
um valor de retorno de 1. Esse código de retorno geralmente indica que algum tipo de erro ocorreu. Poder testar esse código
de retorno facilita a descoberta do que precisa ser resolvido.
Figura 4. Vendo o Código de Erro durante o Uso de passthru()
exec()
O comando exec() é semelhante ao shell_exec(), exceto por retornar
a última linha da saída e, opcionalmente, preencher uma array com a saída completa do comando, junto com o código de erro. A
Listagem 8 é um exemplo do que acontece se você executar exec() sem capturar os resultados em uma
array de dados.
Listagem 8. Executando exec() sem Capturar os Resultados em uma Array de
Dados
<?php
$results = exec('wc -w *.txt | head -5');
echo $results;
#imprimiria apenas a última linha ou resultados, isto é:
#3847 myfile.txt
?>
|
Para capturar os resultados em uma array, inclua o nome da array como o segundo argumento para exec(). Fiz
isso na Listagem 9 utilizando $data como nome da array.
Listagem 9. Capturando os Resultados de exec() em uma Array de Dados
<?php
$results = exec('wc -w *.txt | head -5',$data);
print_r($data);
#imprimiria a array de dados:
#Array ( [0]=> 555 text1.txt [1] => 283 text2.txt)
?>
|
Após você capturar os resultados em uma array, é possível fazer qualquer coisa em cada linha. Por exemplo, você pode
dividir o primeiro espaço localizado e armazenar valores distintos em uma tabela de banco de dados, ou você pode aplicar
formatação específica ou tags em cada linha.
system()
O comando system(), mostrado na Listagem 10, é meio híbrido. Como passthru(), ele
emite tudo que recebe diretamente do programa externo. Como exec(), ele também retorna a última linha e
disponibiliza o código de retorno.
Listagem 10. O comando system()
<?php
system('wc -w *.txt | head -5');
#imprimiria:
#123 file1.txt 332 file2.txt 444 file3.txt
#e assim por diante
?>
|
Alguns Exemplos
Agora que aprendeu a utilizar todos esses comandos da PHP, provavelmente você tem algumas perguntas. Por exemplo, quais
comandos você deveria utilizar e quando? Isso fica totalmente a seu critério e às suas necessidades.
A maior parte do tempo, utilizo o comando exec() com a array de dados para fazer qualquer processamento. Caso
contrário, utilizo shell_exec() para comandos mais simples, principalmente se eu não me preocupar com a saída. Se
eu preciso apenas executar um shell script, utilizo passthru(). Muitas vezes, me pego utilizando as funções
por razões diferentes e, às vezes, alternadamente. Tudo isso depende do meu humor e do que estou tentando fazer.
Outra pergunta que você pode ter é "Para que serve tudo isso?" Caso você esteja sem ideias ou se um projeto que pareça
uma boa maneira de você usar comandos shell não tiver aparecido, ofereço novas ideias aqui.
Se estiver gravando um aplicativo que ofereça qualquer tipo de backup ou recurso de transferência de arquivos, você
seria muito inteligente ao utilizar shell_exec() ou um dos outros comandos aqui para executar
um shell script desenvolvido com rsync. Você pode gravar o shell script que contém os comandos
rsync necessários e, então utilizar passthru() para executá-lo com
base em um comando de usuário ou tarefa cron.
Por exemplo, um usuário com os privilégios apropriados em seu aplicativo, como admin, pode precisar transferir 50 PDFs
de um servidor para outro. O usuário navegaria para o local correto em seu aplicativo, clicaria em Transferir, selecionaria
os PDFs para transferir e clicaria em Enviar. Como ação, o formulário teria um script PHP que executa seu script
rsync via passthru() com uma variável de opção de retorno, assim
você vai saber quando ocorrer um problema, conforme mostrado abaixo.
Listagem 11. Script PHP de Amostra que Executa um Script rsync via
passthru()
<?php
passthru('xfer_rsync.sh',$returnvalue);
if ($returnvalue != 0){
//temos um problema!
//inclua o código de erro aqui
}else{
//está tudo bem
//redirecione para outra página
}
?>
|
Se você tiver um aplicativo que precisa listar processos ou arquivos, ou algum dado sobre esses processos ou arquivos,
você pode utilizar facilmente qualquer um dos comandos resumidos neste artigo para fazer isso. Por exemplo, um simples
comando grep pode ajudá-lo a localizar arquivos que correspondem a certos critérios de procura. Utilizar
esse comando junto com o comando exec() e efetuar dump dos resultados para uma array permite que você
construa um formulário ou uma tabela HTML que, por sua vez, permite a execução de outros comandos.
Até aqui, discuti eventos gerados pelo usuário — se o usuário pressionar um botão ou clicar em um link, a PHP
executará um script. Você também pode obter alguns efeitos interessantes executando scripts PHP independentes com cron ou
outro planejador. Por exemplo, se você tiver um script de backup, é possível executá-lo de forma independente via cron, ou você pode quebrá-lo em um script PHP e depois executá-lo. Por que você faria isso? Isso parece redundante e uma perda
de tempo, certo? Bem, não — não se você considerar que pode executar o script de backup através de exec() ou passthru(), e depois desempenhar um comportamento com base no código de retorno. Se você obtiver um erro, é
possível gravar uma entrada em um log de erros ou banco de dados ou enviar uma mensagem de e-mail de aviso. Se o script for
bem-sucedido, você poderá efetuar dump da saída bruta do script para o banco de dados (por exemplo, rsync possui
um modo detalhado útil no diagnóstico de problemas futuros).
Segurança
Uma instrução rápida sobre segurança: Se você estiver aceitando entrada de usuário e passando essas informações ao longo
do shell, é melhor limpar essa entrada do usuário. Descarte quaisquer comandos que você acha que possam ser prejudiciais e
desautorize certas coisas, como sudo (execução com privilégios de superusuário) ou rm (excluir).
De fato, talvez você queira desautorizar os usuários a enviar pedidos abertos e permitir apenas que eles façam escolhas em uma lista de
possíveis alternativas.
Por exemplo, se estiver executando um programa de transferência que aceite uma lista de arquivos como argumento, talvez
você queira listar todos os seus arquivos em uma linha com uma caixa de opção próxima de cada um. Os usuários podem selecionar
e cancelar a seleção de arquivos e clicar em Enviar para ativar o shell script rsync. Eles
não teriam permissão para digitar em uma lista de arquivos ou utilizar algum tipo de expressão regular.
Resumo
Neste artigo, mostrei os princípios básicos do uso de comandos PHP como shell_exec(), exec(), passthru() e
system() para executar shell scripts e outros comandos. Agora
fica a seu critério colocar este conhecimento em prática em seus próprios aplicativos.
Recursos Aprender
Obter produtos e tecnologias
Discutir
Sobre o autor  | 
|  | Thomas Myer é consultor, autor e desenvolvedor em Austin, Texas. Acompanhe @myerman no Twitter para manter-se atualizado sobre seus feitos. |
Avalie esta página
|  |