Me gustaría devolver una cadena de una función Bash.
Escribiré el ejemplo en Java para mostrar lo que me gustaría hacer:
public String getSomeString() {
return "tadaa";
}
String variable = getSomeString();
El siguiente ejemplo funciona en bash, pero ¿hay una mejor manera de hacerlo?
function getSomeString {
echo "tadaa"
}
VARIABLE=$(getSomeString)
string
bash
function
return-value
Tomás F
fuente
fuente
function funcName {
es la sintaxis heredada pre-POSIX heredada de ksh temprano (donde tenía diferencias semánticas que bash no respeta).funcName() {
, con nofunction
, debe usarse en su lugar; ver wiki.bash-hackers.org/scripting/obsoletefunction myFunction { blah; }
está bien; esfunction myFunction() { blah }
que no está bien, es decir, el uso de paréntesis con la función de palabra clave.Respuestas:
No hay mejor manera que yo sepa. Bash solo conoce códigos de estado (enteros) y cadenas escritas en el stdout.
fuente
somefunction && echo 'success'
). Si piensa en una función como un comando más, tiene sentido; los comandos no "devuelven" nada a la salida que no sea un código de estado, pero pueden generar cosas mientras tanto que puede capturar.Puede hacer que la función tome una variable como el primer argumento y modifique la variable con la cadena que desea devolver.
Imprime "foo bar rab oof".
Editar : se agregaron citas en el lugar apropiado para permitir espacios en blanco en la cadena para abordar el comentario de @Luca Borrione.
Editar : Como demostración, vea el siguiente programa. Esta es una solución de uso general: incluso le permite recibir una cadena en una variable local.
Esto imprime:
Editar : demostrando que el valor de la variable original está disponible en la función, como fue criticado incorrectamente por @Xichen Li en un comentario.
Esto da salida:
fuente
fgm
respuesta escrita de manera simplificada? Esto no funcionará si la cadenafoo
contiene espacios en blanco, mientras que lafgm
de uno sí ... como lo muestra.\$$1
. Si está buscando algo diferente, hágamelo saber.printf '%q' "$var"
. % q es una cadena de formato para el escape de shell. Entonces solo pásalo crudo.Todas las respuestas anteriores ignoran lo que se ha indicado en la página de manual de bash.
Código de ejemplo
Y salida
¡También bajo pdksh y ksh este script hace lo mismo!
fuente
Bash, desde la versión 4.3, febrero de 2014 (?), Tiene soporte explícito para variables de referencia o referencias de nombre (namerefs), más allá de "eval", con el mismo rendimiento beneficioso y efecto indirecto, y que puede ser más claro en sus scripts y también más difícil para "olvidar 'evaluar' y corregir este error":
y también:
Por ejemplo ( EDIT 2 : (gracias, Ron), el espacio de nombres (prefijado) del nombre de la variable interna de la función, para minimizar los conflictos de variables externas, que finalmente deberían responder adecuadamente, el problema planteado en los comentarios de Karsten):
y probando este ejemplo:
Tenga en cuenta que el bash "declare" incorporado, cuando se usa en una función, hace que la variable declarada sea "local" por defecto, y "-n" también se puede usar con "local".
Prefiero distinguir las variables de "declaración importante" de las variables "locales aburridas", por lo que usar "declarar" y "local" de esta manera actúa como documentación.
EDITAR 1 - (Respuesta al comentario de Karsten a continuación) - No puedo agregar más comentarios a continuación, pero el comentario de Karsten me hizo pensar, así que hice la siguiente prueba que FUNCIONA BIEN, AFACIO - Karsten si lees esto, proporciona un conjunto exacto de pasos de prueba desde la línea de comando, que muestra el problema que usted supone que existe, porque estos pasos siguientes funcionan bien:
(Ejecuté esto justo ahora, después de pegar la función anterior en un término bash, como puede ver, el resultado funciona bien).
fuente
declare
crea variables locales dentro de las funciones (esa información no está dada porhelp declare
): "... Cuando se usa en una función, declare y componga que cada nombre sea local, como con el comando local, a menos que el - se proporciona la opción g ... "K=$1; V=$2; eval "$A='$V'";
, pero un error (por ejemplo, un parámetro vacío u omitido), y sería más peligroso. @zenaan el problema planteado por @Karsten se aplica si elige "mensaje" como el nombre de la variable de retorno, en lugar de "ret".Al igual que bstpierre arriba, uso y recomiendo el uso de nombrar explícitamente variables de salida:
Tenga en cuenta el uso de citar los $. Esto evitará interpretar el contenido
$result
como caracteres especiales de shell. He descubierto que este es un orden de magnitud más rápido que elresult=$(some_func "arg1")
idioma de capturar un eco. La diferencia de velocidad parece aún más notable usando bash en MSYS, donde la captura estándar de llamadas a funciones es casi catastrófica.Está bien enviar variables locales ya que los locales se definen dinámicamente en bash:
fuente
echo
de una función dentro de una función, combinada con la sustitución de comandos!También puede capturar la salida de la función:
Parece extraño, pero es mejor que usar variables globales en mi humilde opinión. Pasar parámetros funciona como de costumbre, solo colóquelos dentro de los corchetes o puntos de control.
fuente
La solución más directa y robusta es usar la sustitución de comandos, como escribieron otras personas:
La desventaja es el rendimiento ya que esto requiere un proceso separado.
La otra técnica sugerida en este tema, es decir, pasar el nombre de una variable para asignar como argumento, tiene efectos secundarios, y no la recomendaría en su forma básica. El problema es que probablemente necesitará algunas variables en la función para calcular el valor de retorno, y puede suceder que el nombre de la variable destinada a almacenar el valor de retorno interfiera con una de ellas:
Por supuesto, es posible que no declare las variables internas de la función como locales, pero realmente siempre debe hacerlo, de lo contrario, puede, por otro lado, sobrescribir accidentalmente una variable no relacionada del ámbito primario si hay una con el mismo nombre .
Una posible solución es una declaración explícita de la variable pasada como global:
Si se pasa el nombre "x" como argumento, la segunda fila del cuerpo de la función sobrescribirá la declaración local anterior. Pero los nombres mismos pueden interferir, por lo que si tiene la intención de usar el valor previamente almacenado en la variable pasada antes de escribir el valor de retorno allí, tenga en cuenta que debe copiarlo en otra variable local desde el principio; De lo contrario, el resultado será impredecible. Además, esto solo funcionará en la versión más reciente de BASH, es decir, 4.2. Un código más portátil podría utilizar construcciones condicionales explícitas con el mismo efecto:
Quizás la solución más elegante es simplemente reservar un nombre global para los valores de retorno de la función y usarlo de manera consistente en cada función que escriba.
fuente
eval
ydeclare -n
. La solución de tener un único nombre de variable dedicado comoresult
para todos los parámetros de salida parece ser la única solución que no requiere una función para conocer a todas las personas que llaman para evitar conflictos.Como se mencionó anteriormente, la forma "correcta" de devolver una cadena de una función es mediante la sustitución de comandos. En el caso de que la función también necesite salir a la consola (como se menciona anteriormente en @Mani), cree un fd temporal al comienzo de la función y redirija a la consola. Cierre el fd temporal antes de devolver su cadena.
ejecutar script sin parámetros produce ...
espero que esto ayude a las personas
-Andy
fuente
3>&1
el encabezado del script, luego manipulando&1
&3
y otro marcador de posición&4
dentro de la función. Feo en general, sin embargo.Podría usar una variable global:
Esto da
fuente
Para ilustrar mi comentario sobre la respuesta de Andy, con manipulación adicional del descriptor de archivo para evitar el uso de
/dev/tty
:Todavía desagradable, sin embargo.
fuente
La forma en que lo tiene es la única forma de hacerlo sin romper el alcance. Bash no tiene un concepto de tipos de retorno, solo códigos de salida y descriptores de archivo (stdin / out / err, etc.)
fuente
Abordar la cabeza de Vicky Ronnen , considerando el siguiente código:
daré
Tal vez el escenario normal es usar la sintaxis utilizada en la
test_inside_a_func
función, por lo que puede usar ambos métodos en la mayoría de los casos, aunque capturar la salida es el método más seguro que siempre funciona en cualquier situación, imitando el valor de retorno de una función que puede encontrar en otros idiomas, comoVicky Ronnen
se señaló correctamente.fuente
Las opciones se han enumerado todas, creo. Elegir uno puede reducirse a una cuestión del mejor estilo para su aplicación particular, y en ese sentido, quiero ofrecer un estilo particular que he encontrado útil. En bash, las variables y las funciones no están en el mismo espacio de nombres. Entonces, tratar la variable del mismo nombre como el valor de la función es una convención que encuentro minimiza los conflictos de nombres y mejora la legibilidad, si la aplico rigurosamente. Un ejemplo de la vida real:
Y, un ejemplo del uso de tales funciones:
Como puede ver, el estado de devolución está ahí para que lo use cuando lo necesite, o ignore si no lo necesita. La variable "devuelta" también se puede usar o ignorar, pero, por supuesto, solo después de invocar la función.
Por supuesto, esto es solo una convención. Usted es libre de no establecer el valor asociado antes de regresar (de ahí mi convención de anularlo siempre al comienzo de la función) o de pisotear su valor llamando a la función nuevamente (posiblemente indirectamente). Aún así, es una convención que encuentro muy útil si me encuentro haciendo un uso intensivo de las funciones bash.
A diferencia del sentimiento de que esta es una señal, uno debería, por ejemplo, "pasar a Perl", mi filosofía es que las convenciones siempre son importantes para manejar la complejidad de cualquier idioma.
fuente
Puede hacer
echo
una cadena, pero atraparla canalizando (|
) la función a otra cosa.Puede hacerlo
expr
, aunque ShellCheck informa que este uso está en desuso.fuente
myfunc | read OUTPUT ; echo $OUTPUT
no produce nada.myfunc | ( read OUTPUT; echo $OUTPUT )
obtiene el valor esperado y aclara lo que está sucediendo en el lado derecho. Pero, por supuesto, OUTPUT no está disponible donde lo necesitas ...El problema clave de cualquier esquema de 'variable de salida con nombre' donde la persona que llama puede pasar el nombre de la variable (ya sea usando
eval
odeclare -n
) es un alias involuntario, es decir, conflictos de nombres: desde el punto de vista de la encapsulación, es horrible no poder agregar o renombrar una variable local en una función sin verificar TODAS las llamadas de la función primero para asegurarse de que no quieran pasar el mismo nombre que el parámetro de salida. (O en la otra dirección, no quiero tener que leer la fuente de la función que estoy llamando solo para asegurarme de que el parámetro de salida que pretendo usar no sea un local en esa función).La única forma
REPLY
de evitarlo es usar una única variable de salida dedicada como (como lo sugiere Evi1M4chine ) o una convención como la sugerida por Ron Burk .Sin embargo, es posible que las funciones utilicen una variable de salida fija internamente y luego agreguen algo de azúcar por encima para ocultar este hecho a la persona que llama , como he hecho con la
call
función en el siguiente ejemplo. Considere esto como una prueba de concepto, pero los puntos clave sonREPLY
, y también puede devolver un código de salida como de costumbreREPLY
(ver elwrapper
ejemplo). El código de salida de la función se pasa a través, por lo que su uso en, por ejemplo unaif
owhile
o construcciones similares funciona como se espera.La razón por la que esto funciona es porque la
call
función en sí no tiene locales y no utiliza otras variables que no seanREPLY
, evitando posibles conflictos de nombres. En el punto donde se asigna el nombre de la variable de salida definida por el llamador, estamos efectivamente en el alcance del llamador (técnicamente en el alcance idéntico de lacall
función), en lugar del alcance de la función que se llama.Salida:
fuente
patrón bash para devolver objetos de valor escalar y de matriz :
definición
invocación
fuente
En mis programas, por convención, esto es para lo que sirve la
$REPLY
variable preexistente , que seread
usa para ese propósito exacto.Esto
echo
esPero para evitar conflictos, cualquier otra variable global servirá.
Si eso no es suficiente, recomiendo la solución de Markarian451 .
fuente
fuente