¿De qué sirven los paréntesis `()` en la definición de la función de shell?


20

Una función se define como:

do_something () {
    do it
}

Podía entender su nombre 'do_something' y el paréntesis para encapsular el código de acciones, pero no puedo entender cuál es el propósito ()aquí, ya que no hay parámetros con nombre en los scripts de Bash. Puede ser mejor y sencillo definirlo como

do_something {
  do it
}

Lo que no entra en conflicto con su sintaxis actual, y aún más declara que no hay parámetros con nombre. ¿De qué sirve ()aquí?


Respuestas:


44

Sin el (), la sintaxis sería realmente ambigua.

Tiene que haber una sintaxis inequívoca para definir una función, y sin alterar sustancialmente otra sintaxis de shell, no puede ser esto:

do_something {
    # one or more commands go here
}

Dijiste que esto "no entra en conflicto con su sintaxis actual", ¡pero lo hace! Tenga en cuenta que no obtiene ningún tipo de error de sintaxis cuando intenta ejecutar la primera línea de ese . Obtiene un error, pero no es un error sobre la sintaxis. La segunda línea, con }, es un error de sintaxis, pero la primera línea no lo es. En su lugar, do_something {intenta ejecutar un comando llamado do_somethingy pasar {como argumento a ese comando:

$ do_something {
do_something: command not found

Si ya hay un comando llamado do_something, lo está ejecutando. Si ya hay una función llamada do_something, la está llamando . Es importante en general que la sintaxis no sea ambigua, pero también es importante específicamente que sea posible redefinir una función sin llamarla accidentalmente. Definir una función y llamarla no debería verse igual.

Cómo se trata la concha {y (.

Como type {te diré, {es una palabra clave de shell. Esto lo hace como [[. Si se usa en una situación en la que de otra manera sería un comando, {lleva una semántica especial. Específicamente, realiza la agrupación de comandos. En otras situaciones, sin embargo, puede usarse sin escape para denotar un {carácter literal . Esto incluye la situación de pasarlo como una segunda palabra o una palabra posterior de un comando.

Por supuesto, Bash podría haber sido diseñado para tratar de manera {diferente de lo que lo hace actualmente. Sin embargo, su sintaxis ya no habría sido compatible con el shell POSIX, y Bash realmente no sería un shell de estilo Bourne y no sería capaz de ejecutar muchos scripts de shell.

En contraste, (es un metacarácter de concha. Siempre se trata de forma especial si aparece en una orden y no se cita (con ' ', " "o \). Por lo tanto, no hay ambigüedad en la sintaxis:

do_something() {
    # one or more commands go here
}

Eso no podría significar nada más. Si Bash no tuviera funciones, entonces sería un error de sintaxis, por la misma razón echo foo(bar)es un error de sintaxis.

Si realmente no le gusta la ()notación, puede usar la palabra clave functiony omitirla, como menciona sudodus . Tenga en cuenta que esto no es parte de la sintaxis para definir funciones en la mayoría de los otros shells de estilo Bourne, y en algunos, es compatible, pero las funciones definidas de esa manera tienen una semántica diferente, por lo que un script que lo use no será portátil. (La razón por la cual esta sintaxis es inequívoca es que functiones una palabra clave en Bash que significa que lo que sigue es el comienzo de una definición de función).

Finalmente, tenga en cuenta que, si bien la mayoría de las definiciones de funciones se usan {en la práctica, se permite cualquier comando compuesto. Si tuvieras una función cuyo cuerpo siempre quisieras ejecutar en una subshell, podrías usar en ( )lugar de { }.


1
En un nivel superior, se trata de expectativas humanas y legibilidad. En la mayoría de los lenguajes, particularmente en C, fortran y otros comunes en la época en que se desarrollaron los lenguajes de scripts de bash, una función / subrutina siempre tiene una lista de argumentos, posiblemente vacía, entre paréntesis. Cambia ese paradigma y dificultas la comprensión del idioma.
jamesqf

15

El ()token es decirle al intérprete de shell que estás declarando una función.

$ do_something () { echo 'do it'; } ; do_something
do it

Una alternativa en bashesfunction

function do_something {
 echo 'do it'
}

o como una sola línea puedes probar

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it

2
La functionpalabra clave es un ksh-ism anterior a POSIX que bash admite para la compatibilidad con versiones anteriores; sin embargo, bash lo soporta mal (no hace que las variables sean locales por defecto, como lo hizo ksh en funciones declaradas en esa forma). En consecuencia, no es ideal para un nuevo código. Ver wiki.bash-hackers.org/scripting/obsolete
Charles Duffy el

@CharlesDuffy, gracias por esta explicación :-)
sudodus

1
@CharlesDuffy ¿Estás diciendo que ksh y bash aceptan alguna sintaxis que hace que una variable sea local en ksh pero global en bash? Eso no suena bien. Mi comprensión, basada en parte en esa publicación y comprobación (en ksh93), es que ksh crea variables locales en menos situaciones que bash, nunca más. En bash, typesetsin -gen una función siempre declara una variable local. Con las functionvariables de alcance de palabra clave, bash y ksh de la misma manera , ¿no es así?
Eliah Kagan

@EliahKagan Creo que lo que Charles se refiere se ha demostrado en la respuesta de Gille aquí
Sergiy Kolodyazhnyy

9

¡Qué estilo tan único de ti!

Considere otros estilos de llaves perfectamente finas:

foo
{
  ...
}
foo
  {
    ...
  }
foo
  while ...; do ...; done # Look, ma! No braces!
foo
( ... ) # Look, ma! No braces and a subshell to boot!

¿Cómo puede saber el shell que esas son definiciones de funciones y no simplemente un comando fooseguido de listas de comandos? En todos estos casos, es necesario un factor de desambiguación adicional, como ()o la functionpalabra clave.


Pensándolo bien, eso no es realmente OTBS, ya que el único lugar donde OTBS permite llaves en una nueva línea es para las definiciones de funciones.
muru

3
Creo que hay que argumentar que tenía razón al caracterizarlo como OTBS, porque a diferencia de las funciones en C y C ++, una definición de función en un script de shell de estilo Bourne se puede anidar directamente en el cuerpo de otra definición de función y, por lo tanto, podría decirse que no merece ser distinguido (O tal vez este es el estilo popular en la programación Java donde los métodos obtienen su llave de apertura en la misma línea, en lugar de OTBS.) De cualquier manera, usted hace un excelente punto sobre cómo la sintaxis tendría que imponer restricciones estrictas en la colocación de llaves para funcionar en absoluto sin ()o algo así.
Eliah Kagan
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.