2010-04-13 14 views
7

Tengo un script shell Bourne que tiene varias funciones en el mismo, y permite a llamarse de la siguiente manera:obtener una lista de nombres de función en una secuencia de comandos shell

my.sh <func_name> <param1> <param2>

Dentro func_name() lo hará ser llamado con param1 y param2.

Quiero crear una función de "ayuda" que simplemente enumere todas las funciones disponibles, incluso sin parámetros.

La pregunta: ¿cómo obtengo una lista de todos los nombres de funciones en un script desde el script?

Me gustaría evitar tener que analizar y buscar patrones de función. Demasiado fácil equivocarse.

Gracias, Alex

actualización del código. Quería que mi función help() fuera como main(): una función añadida al código se agrega automáticamente a la ayuda.

#!/bin/sh 

# must work with "set -e" 

foo() 
{ 
    echo foo: -$1-$2-$3- 
    return 0 
} 

# only runs if there are parameters 
# exits 
main() 
{ 
    local cmd="$1" 
    shift 
    local rc=0 
    $cmd "[email protected]" || rc=$? 
    exit $rc 
} 

if [[ "$*" ]] 
then 
    main "[email protected]" 
    die "how did we get here?" 
fi 
+1

"Bourne shell"? No conozco ** ninguna ** distribución de Linux que envíe Bourne de fábrica o que haya tenido; '/ bin/sh' es prácticamente universal POSIX sh (un estándar de décadas más nuevas con un conjunto diferente de incompatibilidades, por ejemplo, Bourne trata'^'como un personaje de tubería, POSIX sh no; Bourne usa' $ [] ' para matemáticas, POSIX sh usa '$ (())', etc.). –

+0

@CharlesDuffy ¿Tiene una fuente para eso? –

+0

@OleTange, ¿qué parte? (Ciertamente confiaba en "en Linux" para omitir casos como SunOS/Solaris anterior, donde POSIX sh ha estado en una ubicación diferente, pero (1) se hizo referencia en mi comentario, y (2) la pregunta es tan etiquetado). –

Respuesta

9

Puede obtener una lista de funciones en su secuencia de comandos utilizando el comando grep en su propia secuencia de comandos. Para que este enfoque funcione, tendrás que estructurar tus funciones de cierta manera para que grep pueda encontrarlas. Este es un ejemplo:

$ cat my.sh 
#!/bin/sh 

function func1() # Short description 
{ 
    echo func1 parameters: $1 $2 
} 

function func2() # Short description 
{ 
    echo func2 parameters: $1 $2 
} 

function help() # Show a list of functions 
{ 
    grep "^function" $0 
} 

if [ "_$1" = "_" ]; then 
    help 
else 
    "[email protected]" 
fi 

Aquí está una demostración interactiva:

$ my.sh 
function func1() # Short description 
function func2() # Short description 
function help() # Show a list of functions 


$ my.sh help 
function func1() # Short description 
function func2() # Short description 
function help() # Show a list of functions 


$ my.sh func1 a b 
func1 parameters: a b 

$ my.sh func2 x y 
func2 parameters: x y 

Si tiene la función de "privado" que no desea que aparezca en la ayuda, a continuación, omitir la "función "parte:

my_private_function() 
{ 
    # Do something 
} 
+0

el camino de doxygen :) Esto es bueno ya que permite que se imprima una descripción, no solo el nombre de la función. Pero ninguna aplicación, un comentario olvidado es una función olvidada. Gracias –

+0

en realidad, esto se ve bien y simple. El uso de la "función" para diferenciar entre público y privado es bueno, y es lo suficientemente robusto para mis propósitos –

+1

si está interesado, su solución con eval desordena los espacios en los parámetros, es decir, my.sh func1 "1 1" 2 3 llame a func1 con $ 1 = 1 $ 2 = 1 $ 3 = 2 $ 4 = 3 en lugar de $ 1 = "1 1", etc. Evitar eval ayuda –

1

Lo mejor que puede hacer es hacer una matriz (que está utilizando bash) que contiene funciones que desee hacer publicidad y que su función de ayuda iterar sobre e imprimirlas.

Llamando set solo producirá las funciones, pero en su totalidad. Todavía tendría que analizar eso buscando las cosas que terminan en() para obtener los símbolos proverbiales.

También es probablemente más sensato usar algo como getopt para convertir --function-name en function_name con argumentos. Pero, bueno, cuerdo es relativo y no has publicado el código :)

Tu otra opción es crear un loadable for bash (un tenedor de set) que lo consiga. Honestamente, preferiría ir con el análisis antes de escribir una carga para esta tarea.

+0

@Tim +1 por su sugerencia. @ n-alexander Creo que no hay forma de obtener metadatos sobre las funciones. Podría analizar, pero no desea hacer eso. –

+0

te refieres a utilizar la misma matriz para llamar a las funciones y para imprimirlas?Esto es lo que haría en C. En shell, esperaba poder agregar una función y no tener que cambiar más código. Pero gracias –

+0

@ n-alexander - Exceptuando escribir su propio bash interno para exportar solo 'símbolos', tendrá que hacer dos cambios por función. Sé que parece que deberías poder "obtener" las funciones, ya que bash las conoce, pero no hay una interfaz incluida para hacerlo. De hecho, me has hecho pensar en hacer una carga para hacer esto. –

2

se llama a esta función sin argumentos y escupe un 'espacios en blanco' lista de separados nombres de funciones solamente.


function script.functions() { 
    local fncs=`declare -F -p | cut -d " " -f 3`; # Get function list 
    echo $fncs; # not quoted here to create shell "argument list" of funcs. 
} 

Para cargar las funciones en una matriz:


declare MyVar=($(script.functions)); 

Por supuesto, el sentido común dicta que cualesquiera funciones que no tienen sido originado en el archivo actual antes de llamar al no aparecerá en la lista .

Para que la lista de sólo lectura y disponible para la exportación a otros scripts llamados por este script:


declare -rx MyVar=($(script.functions)); 

Para imprimir la lista entera como nueva línea separados:


printf "%s\n" "${MyVar[@]}"; 
+0

'declare -F' funciona solo (como se pretendía) en' bash', pero no en 'ksh' (breaks), y no en' zsh' (donde '-F' declara una variable de coma flotante). – mklement0

+0

Sospecho que lo siguiente sería un poco más portátil y dará el mismo resultado: 'declare -f | grep '^. * \ s()' | corte -f 1 -d '' '. – destenson

10

typeset -f devuelve las funciones con sus cuerpos, por lo que un simple script awk se utiliza para arrancar los nombres de las funciones

f1() { :; } 
f2() { :; } 
f3() { :; } 
f4() { :; } 
help() { 
    echo "functions available:" 
    typeset -f | awk '/ \(\) $/ && !/^main/{print $1}' 
} 
main() { help; } 
main 

Este salidas de guión:

functions available: 
f1 
f2 
f3 
f4 
help 
+3

+1 para una solución limpia que _potentially_ funciona en 'bash',' ksh', 'zsh': dado que la salida de' typeset -f' difiere ligeramente entre shells, el comando 'awk' necesita ser más flexible:' typeset -f | awk '!/^ main [(]/&&/^ [^ {}] + * \ (\)/{gsub (/ [()] /, "", $ 1); print $ 1}' '. Pequeña advertencia en 'bash': si tiene _exported_ funciones en su entorno,' typeset -f' las listará también. – mklement0

Cuestiones relacionadas