Qual a melhor forma para receber retornos de função em Shell Script [RESOLVIDO]

1. Qual a melhor forma para receber retornos de função em Shell Script [RESOLVIDO]

Geraldo Albuquerque
AprendiNoLinux

(usa Ubuntu)

Enviado em 16/04/2012 - 19:03h

Este tópico é mais para discutir modalidades de retorno e funcionamento de funções.

Tenho procurado estudar muitos scrips em sh e venho que a forma como retornam dados de funções são bem diferentes.
Tenho sofrido com o retorno de funções onde coloco uma variável para receber dados exemplo:

minhavar=$(minha_function "param1" "param2" "param3" param4")

Qualquer coisa que mando ecoar via echo dentro da função vai sendo adicionada ao que irei pegar dentro da variável $minhavar
Existe algum forma de anular dentro da função o comportamento de ao usar o echo retornar junto com os dados que desejo ?

Nas funções zzzz do Aurélio tenho visto que na última linha onde ele manda ecoar o retorno as vezes ele faz um tratamento na variável para filtrar os dados. Acho isto muito complicado de administrar.

Vamos a um exemplo prático.


#!/bin/bash
# file: retorna_function.sh

function myfunc1()
{
local minhavar
# fazendo algum processamento e em dado momento um aviso.
echo "Olá mundo !!! "
# Outro processamento qualquer
echo "O dia está chuvoso !!!"
# Quando a função cumpriu o seu papel, retorna dados.
minhavar="SoQueroEstaLinhaRetornando"
echo $minhavar # Forçando o echo para me retornar dados.
return
}

# Chamando esta função.
carregando_function=$(myfunc1 "param1" "param2")

# Quando mando ecoar na tela o que volta ?
echo $carregando_function

exit


Quem puder fazer o teste para discutirmos o assunto ficarei agradecido.
Já andei lendo em alguns lugares que tudo que você manda via echo vai incorporar no retorno, mas queria controlar esta atividade. Tem jeito ?





  


2. MELHOR RESPOSTA

Raimundo Alves Portela
rai3mb

(usa Outra)

Enviado em 17/04/2012 - 10:17h

Pensei em algo como:


# o que vc ja tinha feito....
# ....
export RETORNO=$minhavar # Forçando o echo para me retornar dados.
return
}

# Chamando esta função.
myfunc1 "param1" "param2"

# Quando mando ecoar na tela o que volta ?
echo $RETORNO

exit


Ou seja crie uma variável de retorno e exporte ela, quando precisar ver o resultado da função apenas o conteúdo desejado estará na variável.

3. Re: Qual a melhor forma para receber retornos de função em Shell Script [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 17/04/2012 - 03:05h

Meu estilo é encapsular em um procedimento próprio cada saída de dados assim como suas validações.



4. Re: Qual a melhor forma para receber retornos de função em Shell Script [RESOLVIDO]

Geraldo Albuquerque
AprendiNoLinux

(usa Ubuntu)

Enviado em 17/04/2012 - 09:09h

./.ronin escreveu:

Meu estilo é encapsular em um procedimento próprio cada saída de dados assim como suas validações.


Então o que você quer dizer é que para cada etapa o ideal é criar uma função. É isso ?


#!/bin/bash
# file: retorna_function.sh

function Ola()
{
echo "Olá mundo !!! "
}

function Dia()
{
echo "O dia está chuvoso !!!"
}
function myfunc1()
{
local minhavar
# fazendo algum processamento e em dado momento um aviso.
Ola $1
# Outro processamento qualquer
Dia $2
# Quando a função cumpriu o seu papel, retorna dados.
minhavar="SoQueroEstaLinhaRetornando"
echo $minhavar # Forçando o echo para me retornar dados.
return
}

# Chamando esta função.
carregando_function=$(myfunc1 "param1" "param2")

# Quando mando ecoar na tela o que volta ?
echo $carregando_function

exit



Problema persiste.




5. Re: Qual a melhor forma para receber retornos de função em Shell Script [RESOLVIDO]

Osama Jr.
/bin/laden

(usa Void Linux)

Enviado em 17/04/2012 - 14:17h

Declara como globais as variáveis e depois chama elas:


#!/bin/bash
#
function mostraMensagem() {
declare -g a b
a="$1"
b='Hello World!'
}

mostraMensagem 'Olá Mundo!'
echo $b





6. Re: Qual a melhor forma para receber retornos de função em Shell Script [RESOLVIDO]

Geraldo Albuquerque
AprendiNoLinux

(usa Ubuntu)

Enviado em 17/04/2012 - 15:18h

rai3mb escreveu:

Pensei em algo como:


# o que vc ja tinha feito....
# ....
export RETORNO=$minhavar # Forçando o echo para me retornar dados.
return
}

# Chamando esta função.
myfunc1 "param1" "param2"

# Quando mando ecoar na tela o que volta ?
echo $RETORNO

exit


Ou seja crie uma variável de retorno e exporte ela, quando precisar ver o resultado da função apenas o conteúdo desejado estará na variável.


Fala profs rai3mb, fiz conforme a original com a sua alteração.



#!/bin/bash
# file: retorna_function.sh
#set -x # Liga Debug de tudo.
#trap read DEBUG # Debugando tudo.

function myfunc1()
{
local minhavar
# fazendo algum processamento e em dado momento um aviso.
echo "Olá mundo !!! "
# Outro processamento qualquer
echo "O dia está chuvoso !!!"
# Quando a função cumpriu o seu papel, retorna dados.
minhavar="SoQueroEstaLinhaRetornando"
#echo $minhavar # Forçando o echo para me retornar dados.
export RETORNO=$minhavar
return
}

# Chamando esta função. Agora sem encapsular uma variável para retorno.
myfunc1 "param1" "param2"

# Quando mando ecoar na tela o que volta ?
echo "Pegando o retorno: $RETORNO"

exit


Funcionou. As Diferenças foram.
1 - As mensagens dentro da função podem ser enviadas no fluxo normal da tela.
2 - A utilização do retorno acontece em uma segunda etapa e não diretamente com o retorno.
3 - Se a chamada da função foi feita como antes RETORNO=$(myfunc1 "param1" "param2") não surte o efeito esperado.

Valeu rai3mb. Acho que você entendeu legal onde quero chegar ;)




7. Qual a melhor forma de retornar dados da função remove_da_lista_de_tarefas

Geraldo Albuquerque
AprendiNoLinux

(usa Ubuntu)

Enviado em 17/04/2012 - 15:49h

/bin/laden escreveu:

Declara como globais as variáveis e depois chama elas:


#!/bin/bash
#
function mostraMensagem() {
declare -g a b
a="$1"
b='Hello World!'
}

mostraMensagem 'Olá Mundo!'
echo $b




A solução terrorista do /bin/laden kkk, funcionou igual a do Raimundo.


#!/bin/bash
# file: retorna_function.sh
#set -x # Liga Debug de tudo.
#trap read DEBUG # Debugando tudo.

function myfunc1()
{
# local minhavar - Nesta opção não declaro mais a variável como local.
declare -g minhavar
# fazendo algum processamento e em dado momento um aviso.
echo "Olá mundo !!! "
# Outro processamento qualquer
echo "O dia está chuvoso !!!"
# Quando a função cumpriu o seu papel, retorna dados.
minhavar="SoQueroEstaLinhaRetornando"
# echo $minhavar # Forçando o echo para me retornar dados.
return
}

# Chamando esta função. Agora sem encapsular uma variável para retorno.
myfunc1 "param1" "param2"

# Quando mando ecoar na tela o que volta ?
echo "Pegando o retorno: $minhavar "

exit


Não posso fazer a chamada encapsulando uma variável. É necessário chamar a função assim: $ myfunc1 "param1" "param2"
Ambas soluções funcionaram. A diferença é que no caso do Raimundo a variável foi exportada e não valerá em outras sub-shells. Já a variável do /bin/laden irá valer para todas as novas chamadas e para novos sub-shells. Um perigo se for analisado a questão de nomenclatura.




8. Re: Qual a melhor forma para receber retornos de função em Shell Script [RESOLVIDO]

Geraldo Albuquerque
AprendiNoLinux

(usa Ubuntu)

Enviado em 17/04/2012 - 15:50h

Queria mais opiniões sobre a questão de como chamar variáveis que possuem retorno.
Entendi o lance de globalizar ou mesmo exportar. Ambas cumprem o que prometem e só retornarão o que queremos de dentro da função. A qualquer momento também podemos inicializar a variável global para perder o valor.

Sou a favor de separar o máximo as funções, mas há casos em que isto é desnecessário. Segue um exemplo um pouco mais complexo onde preciso retorno de dados e não dividi em outras funções. Até poderia. Se acharem que vale a pena dividir vou fazer.

========================================================
É uma das várias funções de um gerenciador de tarefas. Tentei documentar para esclarecer o que ocorre.
========================================================


#-------------------------------------------------------#
# INÍCIO >>> function remove_da_lista_de_tarefas()
#-------------------------------------------------------#
function remove_da_lista_de_tarefas()
{
# Definindo variáveis locais.
local lc_prefix_file_erro lc_dir_arq_start
# Prefixo para usar como arquivo de erro.
lc_prefix_file_erro="$1"
# Diretório para enviar o arquivo de start/início.
lc_dir_arq_start="$2"
# Sufixo para comandos. Poderá ser passado via parâmetros no futuro.
lc_sufix_comando=' cut -f1 -d":"'
# Variável que possui o comando completo para gerar o arquivo.
lc_comando_completo="${lc_dir_arq_start}/${lc_prefix_file_erro} |
${lc_sufix_comando}"
# Variável que orienta dados e o arquivo de erro.
lc_corrigir_erro_trf="${dir_pasta_start}/Corrigir_Erro_${$}.trf"
# Endereço do arquivo temporário para determinada tarefa.
lc_tmp_tarefa="${dir_pasta_servicos}/tmp_tarefa.$$"
# Endereço completo para o arquivo que irá receber os erros.
lc_tmp_corrigir="${dir_pasta_servicos}/tmp_corrigir.$$"
# Linha de Debug (função) parametrizado para passar informações ao sistema de deputação.
Debug 4 "comandos [ eval cat completo ${lc_comando_completo} > ${lc_corrigir_erro_trf} ] Função:$FUNCNAME Linha:$LINENO]"
# Linha de comando para gerar o arquivo. Fiz com eval porque não estava funcionando de outra forma. Detesto usar eval.
eval cat "${lc_comando_completo}" > ${lc_corrigir_erro_trf} 2>/dev/null
# Testa se o arquivo de erro foi encontrado.
if [ -f ${lc_corrigir_erro_trf} ]; then
StatusErroTarefa="1"
# Linha de debug para acompanhamento.
Debug 1 "Entrou no se existe arquivo: ${lc_corrigir_erro_trf} [Função:$FUNCNAME Linha:$LINENO]"
# Vai rodar o arquivo de erro para tomar providências.
while read linha;do
Debug 1 "Dentro do while lendo $linha do arquivo: ${lc_corrigir_erro_trf} [Função:$FUNCNAME Linha:$LINENO]"
# Salva o arquivo de erro com 1 item a menos porque estamos tomando providências.
grep -v $linha ${ARQ_TAREFA} > $lc_tmp_tarefa
# Move o arquivo de tarefa encontrado nas definições de erro.
mv $lc_tmp_tarefa ${ARQ_TAREFA}
# Definindo o caminho e nome do arquivo de da tarefa. Se existe, vou apagar porque antes deu erro.
lc_arq_erro_trf="${dir_pasta_start}/Erro_${$}_Start_Service_${ORIGEM}_canal_${linha}.trf"

if [ -f ${lc_arq_erro_trf} ]; then
rm ${lc_arq_erro_trf}
fi
done < "${lc_corrigir_erro_trf}" # Arquivo que contém os erros.
# Finalmente, após ter verificado o arquivo de erro, vou apaga-lo para não fica sujeita no HD.
if [ -f ${lc_corrigir_erro_trf} ]; then
rm ${lc_corrigir_erro_trf}
fi
else
StatusErroTarefa="0"
fi
# Agora a lista está limpa para disparar os scripts.
echo $StatusErroTarefa
}



Notem que esta função precisa retornar a quem chamou informando se deu ou não problema. Basicamente [0] ou [1]
A função possui algumas variáveis que estão vindo de fora GLOBALIZADAS. Preciso estudar uma forma de não usa-las e passar tudo via parâmetro.

Perguntas:

Conseguem ver onde podemos dividir esta função em outras ?
Conseguirei pegar o retorno da função com certeza dividindo ela em outras ? Qual a melhor forma ?

Continuo batendo lata para encontrar a melhor forma de retornar dados de funções.

POR FAVOR!!! Limite seu código para 80 colunas. Não desfigure o site.



9. Re: Qual a melhor forma para receber retornos de função em Shell Script [RESOLVIDO]

Geraldo Albuquerque
AprendiNoLinux

(usa Ubuntu)

Enviado em 17/04/2012 - 22:43h

Estudando vários outros scripts shells na minha máquina, encontrei um biblioteca que tem uma construção interessante.

Trata-se do arquivo: libtoolize (GNU libtool) 2.4

Você irá encontra-lo na pasta usr/bin

Para contornar furos no bash quando retorna com erro de uma função na última linha, que é também o meu caso, ele criou algo interessante. Uma comunicação de mensagens simulando o comando echo que consegue burlar a limitação de retorno dentro de funções porque delega esta atividade a uma outra função. Então, para ecoar mensagens de retorno qualquer mensagem passa por 3 arquivos sempre. Só será possível entender olhando a biblioteca sh.

Não sei se hoje a limitação informada dentro da biblioteca ainda existe nas novas versões do shell, mas a sua abordagem me parece ser uma tremenda POG que poderá me ajudar a contornar o problema de retorno que estou tentando ajustar neste tópico.

Façam o teste ai:



$ test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'}


Esta é a parte onde é inicializa. Em uma outra função que recebe parâmetros você usa normalmente a variável + o que desejar. Desde que seja para mostrar mensagens. exemplo externo de uma função.


$ECHO "MINHA MENSAGEM"


Onde e como ela é usada:



# func_echo arg...
# Echo program name prefixed message, along with the current mode
# name if it has been set yet.
func_echo ()
{
$ECHO "$progname: ${opt_mode+$opt_mode: }$*"
}


Os feras em SH o que pensam sobre esta construção ? Vale a pena ?

Antes que me pergunte o que é este as_echo , já vou avisando que não entendi nada também kkkk


# Printing a long string crashes Solaris 7 /usr/bin/printf.
as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo


Bom é isso. Sugestões?





10. Re: Qual a melhor forma para receber retornos de função em Shell Script [RESOLVIDO]

Geraldo Albuquerque
AprendiNoLinux

(usa Ubuntu)

Enviado em 18/04/2012 - 06:53h


function Debug()
{

DEBUG=${DEBUG:-"0"} # Caso não exista a variável.

[ $1 -le $DEBUG ] || return

local prefixo arquivar

arquivar=$(if [ "$2" = "1" ]; then echo "1"; fi)

case "$1" in
1) prefixo="{TEXTO}33[33m-- $*{TEXTO}33[m";;
2) prefixo="{TEXTO}33[32m---- $*{TEXTO}33[m";;
3) prefixo="{TEXTO}33[31m------ $*{TEXTO}33[m";;
4) prefixo="{TEXTO}33[34m------ $*{TEXTO}33[m";;
5) prefixo="{TEXTO}33[41;33;1m---------- $*{TEXTO}33[m";;
*) echo -e "Mensagem não categorizada: {TEXTO}33[41;33;1m$*{TEXTO}33[m"; return;;
esac
shift

if [ "$arquivar" = "1" ]; then
if [ "$GRAVA_LOG" = "1" ]; then
echo " Chamar a função de log para gravar em aquivo. "
fi
fi

echo -e "$prefixo"


# Recebe parâmetros com os erros.
# 1 - Nível do erro.
# 1 - Mensagens genéricas de localização ("Estou neste local")
# 2 - Mensagens para localizar fluxo ("Entrei no loop")
# 3 - Mensagens com conteúdo de variáveis importantes.
# 4 - Mensagens com conteúdo de variáveis secundárias.
# 5 - Mensagens de conteúdo CRÍTICO e possíveis erros fatais.

}



AprendiNoLinux escreveu:

Debug 4 "comandos [ eval cat completo ${lc_comando_completo} > ${lc_corrigir_erro_trf} ] Função:$FUNCNAME Linha:$LINENO]"

POR FAVOR!!! Limite seu código para 80 colunas. Não desfigure o site.


Me diga como que modifico.


11. Estava dando o assunto como encerrado, mas...

Geraldo Albuquerque
AprendiNoLinux

(usa Ubuntu)

Enviado em 18/04/2012 - 11:50h

Parecia estar tudo se resolvendo na questão de retornar valores de funções. Digo, qual a forma certa em cada caso. Por favor, me corrijam se estiver falando besteira.

No livro do Aurélio (Script profissional) diz mais ou menos assim:

Quando criar uma função
-----------------------

o- Quando precisar usar o mesmo trecho de código duas vezes ou mais.
o- Quando quiser pôr em destaque um trecho de código especialista.
o- Quando precisar de recursividade.
o- Quando quiser, funções sempre ajudam.

extras: Sempre use o comando local para proteger todas as variáveis de uma função. [ESTOU TENTANDO kkkk]

Retorno de funções
------------------

o- Funções só podem retornar números de 0 a 255, usando o comando return.
o- Funções podem retornar strings usando o comando echo.
o- Funções podem gravar variáveis globais, MAS EVITE FAZER ISSO.

Então o trecho abaixo é POG.


$ test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'}


O que o livro do Aurélio NÃO DIZ
--------------------------------

o- Quando você trabalha com retorno de strings em funções, não deve ecoar dados dentro da função.
o- Se for para retornar dados de strings, procure faze-lo em array.
o- Como recuperar valores lógicos de funções.
o- Como tratar retornos lógicos de funções.
o- Se dentro de sua função possuir muitos componentes visuais (echo de várias etapas), evite fazer chamadas com retorno, ex: ret=$(myfunc "para" "oi")
o

Resumo da ópera
---------------
Hoje quem vai estar em campo não é a minha barcelusa kkkk


Seguindo as recomendações de quem já participou do tópico ou em outras discussões aqui do VOL, MINHAS conclusões. Por favor, é importante criticar o que não estiver certo. Continuar aprendendo errado e ter que mudar código porque se constrói a arquitetura errada é pior do que torcer pra time rebaixado pra segunda divisão rsrs. Você sabe que ele mais cedo ou mais tarde vai cair, só não sabe quando.

o- Dividir os procedimentos ao máximo. (Estou fazendo neste momento, versão em breve.)
o- Separar funções que precisam de retorno lógico em etapas de micro funções.
o- Investigar se preciso mesmo de chamadas de "retorno" na maioria das funções. Via de regra é porque não é genérica.
o- Funções generalizadas não podem conter qualquer variável pública. Se houver gera dependência e precisa de default.
o- Qualquer função tem que trabalhar isolada do código completo ou não deve ser convertida como tal.
o- Se a função tiver dependência de variável GLOBAL, deve ser documentada.
o- Se a função gerar alguma variável GLOBAL, deve via de regra ser lógica.

ps: Não sei se está correto. Estou chamando de variáveis lógicas aquelas que retornam 0,1,2 (ou códigos de erros).

Quem sabe por favor opinar. Quem não sabe é hora de perguntar :) NÃO LEIAM: http://www.fpepito.org/utils/conjugue.php?verbo=opinar


Do jeito que a coisa está indo o resultado do trabalho poderá se tornar um artigo no VOL.


12. +/- assim...

Perfil removido
removido

(usa Nenhuma)

Enviado em 18/04/2012 - 15:02h


#!/bin/bash

#--------------------------------------------------------------------#
# INÍCIO >>> function remove_da_lista_de_tarefas()
#--------------------------------------------------------------------#
function remove_da_lista_de_tarefas()
{
# Definindo variáveis locais.
local lc_prefix_file_erro lc_dir_arq_start
# Prefixo para usar como arquivo de erro.
lc_prefix_file_erro="$1"
# Diretório para enviar o arquivo de start/início.
lc_dir_arq_start="$2"
# Sufixo para comandos. Poderá ser passado via parâmetros no futuro.
lc_sufix_comando=' cut -f1 -d":"'
# Variável que possui o comando completo para gerar o arquivo.
lc_comando_completo="${lc_dir_arq_start}/${lc_prefix_file_erro} |
${lc_sufix_comando}"
# Variável que orienta dados e o arquivo de erro.
lc_corrigir_erro_trf="${dir_pasta_start}/Corrigir_Erro_${$}.trf"
# Endereço do arquivo temporário para determinada tarefa.
lc_tmp_tarefa="${dir_pasta_servicos}/tmp_tarefa.$$"
# Endereço completo para o arquivo que irá receber os erros.
lc_tmp_corrigir="${dir_pasta_servicos}/tmp_corrigir.$$"
# Linha de Debug (função) parametrizado para passar informações
# ao sistema de depuração.
Debug 4 "comandos [ eval cat completo ${lc_comando_completo} >
${lc_corrigir_erro_trf} ] Função:$FUNCNAME Linha:$LINENO]"
# Linha de comando para gerar o arquivo. Fiz com eval porque não estava
# funcionando de outra forma. Detesto usar eval.
eval cat "${lc_comando_completo}" >
${lc_corrigir_erro_trf} 2> /dev/null
# Testa se o arquivo de erro foi encontrado.
if [ -f ${lc_corrigir_erro_trf} ]
then
StatusErroTarefa="1"
# Linha de debug para acompanhamento
MSG="Entrou no se existe arquivo: ${lc_corrigir_erro_trf}"
Debug 1 "$MSG [Função:$FUNCNAME Linha:$LINENO]"
# Vai rodar o arquivo de erro para tomar providências.
MSG="No while lendo $linha do arquivo: ${lc_corrigir_erro_trf}"
while read linha
do
Debug 1 "$MSG [Função:$FUNCNAME Linha:$LINENO]"
# Salva o arquivo de erro com 1 item a menos porque estamos
# tomando providências.
grep -v $linha ${ARQ_TAREFA} > $lc_tmp_tarefa
# Move o arquivo de tarefa encontrado nas definições de erro.
mv $lc_tmp_tarefa ${ARQ_TAREFA}
# Definindo o caminho e nome do arquivo de da tarefa.
# Se existe, vou apagar porque antes deu erro.
MSG="${dir_pasta_start}/Erro_${$}_Start_Service"
lc_arq_erro_trf="$MSG_${ORIGEM}_canal_${linha}.trf"

if [ -f ${lc_arq_erro_trf} ]
then
rm ${lc_arq_erro_trf}
fi
done < "${lc_corrigir_erro_trf}"
# Arquivo que contém os erros.

# Finalmente, após ter verificado o arquivo de erro,
# vou apaga-lo para não fica sujeita no HD.
if [ -f ${lc_corrigir_erro_trf} ]
then
rm ${lc_corrigir_erro_trf}
fi
else
StatusErroTarefa="0"
fi
# Agora a lista está limpa para disparar os scripts.
echo $StatusErroTarefa
}





01 02



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts