Gostaria de retornar uma string de uma função Bash.
Vou escrever o exemplo em java para mostrar o que gostaria de fazer:
public String getSomeString() {
return "tadaa";
}
String variable = getSomeString();
O exemplo abaixo funciona no bash, mas existe uma maneira melhor de fazer isso?
function getSomeString {
echo "tadaa"
}
VARIABLE=$(getSomeString)
string
bash
function
return-value
Tomas F
fonte
fonte
function funcName {
é a sintaxe legada pré-POSIX herdada do início do ksh (onde havia diferenças semânticas que o bash não honra).funcName() {
, com nofunction
, deve ser usado; consulte wiki.bash-hackers.org/scripting/obsoletefunction myFunction { blah; }
bem; nãofunction myFunction() { blah }
é bom, ou seja, o uso de parênteses com a função de palavra-chave.Respostas:
Não há melhor maneira que eu conheça. O Bash conhece apenas códigos de status (números inteiros) e seqüências de caracteres gravadas no stdout.
fonte
somefunction && echo 'success'
). Se você pensa em uma função como apenas outro comando, faz sentido; Os comandos não "retornam" nada na saída que não seja um código de status, mas podem produzir coisas nesse meio tempo que você pode capturar.Você pode fazer com que a função pegue uma variável como o primeiro argumento e modifique a variável com a string que você deseja retornar.
Imprime "foo bar rab oof".
Edit : adicionada citação no local apropriado para permitir espaço em branco na string para tratar do comentário de @Luca Borrione.
Editar : Como demonstração, consulte o seguinte programa. Esta é uma solução de uso geral: permite até receber uma string em uma variável local.
Isso imprime:
Edit : demonstrando que o valor da variável original está disponível na função, como foi incorretamente criticado por @Xichen Li em um comentário.
Isso fornece saída:
fonte
fgm
resposta escrita de maneira simplificada? Isso não funcionará se a stringfoo
contiver espaços em branco, enquanto a stringfgm
irá .. como ele está mostrando.\$$1
. Se você estiver procurando por algo diferente, entre em contato.printf '%q' "$var"
. % q é uma string de formato para escape do shell. Depois é só passá-lo cru.Todas as respostas acima ignoram o que foi declarado na página de manual do bash.
Código de exemplo
E saída
Também sob pdksh e ksh, este script faz o mesmo!
fonte
O Bash, desde a versão 4.3, fevereiro de 2014 (?), Possui suporte explícito para variáveis de referência ou referências de nome (namerefs), além de "eval", com o mesmo desempenho benéfico e efeito indireto, o que pode ser mais claro em seus scripts e mais difícil para "esqueça de 'eval' e precise corrigir este erro":
e também:
Por exemplo ( EDIT 2 : (obrigado Ron), coloca o nome da variável interna da função (prefixado), para minimizar conflitos de variáveis externas, o que deve finalmente responder adequadamente, a questão levantada nos comentários de Karsten):
e testando este exemplo:
Observe que o bash "declare" builtin, quando usado em uma função, torna a variável declarada "local" por padrão, e "-n" também pode ser usado com "local".
Prefiro distinguir variáveis "importante declarar" de variáveis "chatas locais", portanto, usar "declarar" e "local" dessa maneira atua como documentação.
EDIT 1 - (Resposta ao comentário abaixo por Karsten) - Não consigo mais adicionar comentários abaixo, mas o comentário de Karsten me fez pensar, então eu fiz o seguinte teste que FUNCIONA MUITO BEM, AFAICT - Karsten se você ler isso, forneça um conjunto exato das etapas de teste na linha de comando, mostrando o problema que você supõe existir, porque essas etapas a seguir funcionam perfeitamente:
(Fiz isso agora, depois de colar a função acima em um termo básico - como você pode ver, o resultado funciona muito bem.)
fonte
declare
criar variáveis locais dentro de funções (essas informações não são fornecidas porhelp declare
): "... Quando usadas em uma função, declare e typeset torne cada nome local, como no comando local, a menos que - a opção g é fornecida ... "K=$1; V=$2; eval "$A='$V'";
, mas um erro (por exemplo, um parâmetro vazio ou omitido), e seria mais perigoso. @zenaan, o problema levantado por @Karsten se aplica se você escolher "message" como o nome da variável de retorno, em vez de "ret".Como bstpierre acima, eu uso e recomendo o uso de nomear explicitamente variáveis de saída:
Observe o uso de citar o $. Isso evitará a interpretação do conteúdo
$result
como caracteres especiais do shell. Eu descobri que esta é uma ordem de magnitude mais rápida que aresult=$(some_func "arg1")
idioma de capturar um eco. A diferença de velocidade parece ainda mais notável usando o bash no MSYS, onde a captura de stdout de chamadas de função é quase catastrófica.Não há problema em enviar variáveis locais, pois os locais têm um escopo dinâmico no bash:
fonte
echo
interior de uma função, combinada com a substituição de comandos!Você também pode capturar a saída da função:
Parece estranho, mas é melhor do que usar variáveis globais IMHO. A passagem de parâmetros funciona como de costume, basta colocá-los dentro das chaves ou dos backticks.
fonte
A solução mais direta e robusta é usar a substituição de comandos, como outras pessoas escreveram:
A desvantagem é o desempenho, pois isso requer um processo separado.
A outra técnica sugerida neste tópico, ou seja, passar o nome de uma variável a ser atribuída como argumento, tem efeitos colaterais, e eu não a recomendaria em sua forma básica. O problema é que você provavelmente precisará de algumas variáveis na função para calcular o valor de retorno, e pode acontecer que o nome da variável destinada a armazenar o valor de retorno interfira em uma delas:
Obviamente, você não pode declarar variáveis internas da função como local, mas realmente deve sempre fazê-lo, caso contrário, por outro lado, pode substituir acidentalmente uma variável não relacionada do escopo pai, se houver uma com o mesmo nome .
Uma solução possível é uma declaração explícita da variável passada como global:
Se o nome "x" for passado como argumento, a segunda linha do corpo da função substituirá a declaração local anterior. Mas os nomes em si ainda podem interferir; portanto, se você pretende usar o valor anteriormente armazenado na variável passada antes de gravar o valor de retorno, lembre-se de copiá-lo para outra variável local logo no início; caso contrário, o resultado será imprevisível! Além disso, isso funcionará apenas na versão mais recente do BASH, a saber 4.2. Um código mais portátil pode utilizar construções condicionais explícitas com o mesmo efeito:
Talvez a solução mais elegante seja reservar um nome global para os valores de retorno da função e usá-lo consistentemente em todas as funções que você escreve.
fonte
eval
edeclare -n
. A solução alternativa de ter um único nome de variável dedicado, comoresult
para todos os parâmetros de saída, parece a única solução que não requer que uma função conheça todos os seus chamadores para evitar conflitos.Como mencionado anteriormente, a maneira "correta" de retornar uma seqüência de caracteres de uma função é com substituição de comando. Caso a função também precise ser enviada para o console (como @Mani menciona acima), crie um fd temporário no início da função e redirecione para o console. Feche o fd temporário antes de retornar sua string.
executar script sem parâmetros produz ...
espero que isso ajude as pessoas
-Andy
fonte
3>&1
no início do script e manipulando&1
&3
e outro espaço reservado&4
na função. Feio o tempo todo, no entanto.Você poderia usar uma variável global:
Isto dá
fonte
Para ilustrar meu comentário sobre a resposta de Andy, com manipulação adicional de descritor de arquivo para evitar o uso de
/dev/tty
:Ainda desagradável, no entanto.
fonte
Do jeito que você tem, é a única maneira de fazer isso sem quebrar o escopo. O Bash não tem um conceito de tipos de retorno, apenas códigos de saída e descritores de arquivo (stdin / out / err, etc)
fonte
Abordando a cabeça de Vicky Ronnen , considerando o seguinte código:
darei
Talvez o cenário normal seja usar a sintaxe usada na
test_inside_a_func
função, portanto, você pode usar os dois métodos na maioria dos casos, embora capturar a saída seja o método mais seguro sempre trabalhando em qualquer situação, imitando o valor retornado de uma função que você pode encontre em outros idiomas, conformeVicky Ronnen
indicado corretamente.fonte
As opções foram todas enumeradas, eu acho. A escolha de uma pode se resumir a uma questão do melhor estilo para sua aplicação específica e, nesse sentido, quero oferecer um estilo em particular que achei útil. No bash, variáveis e funções não estão no mesmo espaço para nome. Portanto, tratar a variável com o mesmo nome que o valor da função é uma convenção que acho que minimiza conflitos de nome e melhora a legibilidade, se eu a aplicar com rigor. Um exemplo da vida real:
E, um exemplo do uso de tais funções:
Como você pode ver, o status de retorno está lá para você usar quando precisar, ou ignorar se não precisar. A variável "retornada" também pode ser usada ou ignorada, mas é claro que somente após a função ser chamada.
Claro, isso é apenas uma convenção. Você é livre para deixar de definir o valor associado antes de retornar (daí a minha convenção de sempre anulá-lo no início da função) ou atropelar seu valor chamando a função novamente (possivelmente indiretamente). Ainda assim, é uma convenção que acho muito útil se eu estiver fazendo uso pesado das funções do bash.
Ao contrário do sentimento de que este é um sinal que se deve, por exemplo, "mudar para perl", minha filosofia é que as convenções são sempre importantes para gerenciar a complexidade de qualquer idioma.
fonte
Você pode criar
echo
uma string, mas pegue-a canalizando (|
) a função para outra coisa.Você pode fazê-lo
expr
, embora o ShellCheck reporte esse uso como obsoleto.fonte
myfunc | read OUTPUT ; echo $OUTPUT
não produz nada.myfunc | ( read OUTPUT; echo $OUTPUT )
obtém o valor esperado e esclarece o que está acontecendo no lado direito. Mas é claro OUTPUT não estará disponível quando você precisar dele ...Eles são o principal problema de qualquer esquema de 'variável de saída nomeada' em que o chamador possa passar o nome da variável (usando
eval
oudeclare -n
) é um aliasing inadvertido, ou seja, conflitos de nome: Do ponto de vista do encapsulamento, é horrível não poder adicionar ou renomear uma variável local em uma função sem verificar primeiro todos os chamadores da função para garantir que eles não desejam passar o mesmo nome que o parâmetro de saída. (Ou na outra direção, não quero ler a fonte da função que estou chamando apenas para garantir que o parâmetro de saída que pretendo usar não seja local nessa função.)A única maneira de contornar isso é usar uma única variável de saída dedicada como
REPLY
(como sugerido pelo Evi1M4chine ) ou uma convenção como a sugerida por Ron Burk .No entanto, é possível que as funções usem internamente uma variável de saída fixa e, em seguida, adicione um pouco de açúcar por cima para ocultar esse fato do chamador , como fiz com a
call
função no exemplo a seguir. Considere isso uma prova de conceito, mas os pontos principais sãoREPLY
e também pode retornar um código de saída como de costumeREPLY
(veja owrapper
exemplo). O código de saída da função é passado, portanto, usá-los em, por exemplo, umif
ouwhile
ou construções semelhantes obras como esperado.O motivo disso funcionar é porque a
call
função em si não possui locais e não usa variáveis além deREPLY
, evitando potencial para conflitos de nomes. No ponto em que o nome da variável de saída definida pelo chamador é atribuído, estamos efetivamente no escopo do chamador (tecnicamente no escopo idêntico dacall
função), e não no escopo da função que está sendo chamada.Resultado:
fonte
padrão bash para retornar objetos de valor escalar e de matriz :
definição
invocação
fonte
Nos meus programas, por convenção, é para isso que serve a
$REPLY
variável preexistente , que éread
usada exatamente para esse propósito.Isso
echo
esMas, para evitar conflitos, qualquer outra variável global servirá.
Se isso não for suficiente, recomendo a solução do Markarian451 .
fonte
fonte