É possível adicionar uma função dentro de uma função?

3

Aqui está o meu código:

function update_profile
{
    echo "1. Update Name"
    echo "2. Update Age"
    echo "3. Update Gender"
    echo "Enter option: "
    read option

    case $option in
         1) update_name ;;
         2) update_age ;;
         3) update_gender ;;
    esac

    function update_name
    {
        echo "Enter new name: "
        read name
    }
}

Só quero ter certeza de que é possível fazer isso. Eu sei que posso jogar todos os códigos no caso, mas será confuso, então eu estava pensando em criar uma função autônoma, dentro de uma função, e ser chamada quando necessário para executar seus comandos.

    
por Zac 17.01.2015 / 17:09

3 respostas

4

Sim, é possível.

It is even possible to nest a function within another function, although this is not very useful.

f1 ()
{

  f2 () # nested
  {
    echo "Function \"f2\", inside \"f1\"."
  }

}  

f2  #  Gives an error message.
    #  Even a preceding "declare -f f2" wouldn't help.

echo    

f1  #  Does nothing, since calling "f1" does not automatically call "f2".
f2  #  Now, it's all right to call "f2",
    #+ since its definition has been made visible by calling "f1".

    # Thanks, S.C.

Fonte: O projeto de documentação do Linux

    
por 17.01.2015 / 17:33
2

Você pode definir uma função em qualquer lugar em que o shell esteja esperando um comando, incluindo em uma função. Observe que a função é definida no momento em que o shell executa sua definição, não quando o shell analisa o arquivo. Portanto, seu código não funcionará se o usuário escolher a opção 1 na primeira vez que update_profile for executado, porque quando update_name for chamado na instrução case , a definição da função update_name não será executada ainda. Assim que a função update_profile for executada uma vez, a função update_name também será definida.

Você precisa mover a definição de update_name antes do ponto em que ela é usada.

Definir update_name dentro de update_profile não é particularmente útil. Isso significa que update_name não será definido até a primeira vez que update_profile for executado, mas permanecerá disponível depois disso. Se você quiser que update_name esteja disponível somente dentro de update_profile , defina a função dentro dela e chame unset -f update_name antes de retornar da função. Você não vai ganhar nada fazendo isso, em vez de fazer a coisa simples e definir todas as funções globalmente.

    
por 19.01.2015 / 00:14
1

Sim, e isso fica mais claro quando você considera o que realmente é uma função de shell.

Para shells compatíveis com POSIX, uma definição de função é padronizada da seguinte forma:

  • 2.9.5 Comando de Definição de Função

    • Uma função é um nome definido pelo usuário que é usado como comando simples para chamar um comando composto com novo posicionamento parâmetros . Uma função é definida com um " comando de definição de função " ... como segue:

    fname() compound-command[io-redirect ...]

    • A função é denominada fname ... A implementação deve manter espaços de nomes separados para funções e variáveis .

    • O argumento composto-comando representa um comando composto , conforme descrito em Comandos Compostos .

      • Quando a função é declarada, nenhuma das expansões em Expansões de palavras serão realizadas no texto em compound-command ou <<&io-redirect&> ; todos os ${expansions} serão executados normalmente a cada vez que a função for chamada. Da mesma forma, os redirecionamentos <<&io-redirect&> opcionais e qualquer variable=assignments dentro de compound-command devem ser executados durante a execução da própria função, não a definição da função. Veja Consequências dos erros da Shell para as consequências das falhas dessas operações em mídias interativas e não conchas interativas.

E assim, em sua essência, uma função de shell chamada fname é uma cadeia literal composta de pelo menos um comando composto que o shell chamará da memória e executará no lugar de fname quando ocorre na entrada na posição do comando - o que significa que qualquer cmd irá fazer. Esta definição abre muitas possibilidades para o uso de uma função em um shell POSIX. Qualquer um dos itens a seguir é aceitável:

fn() {
    command; list;
    fn() { : redefines itself upon first execution; }
}

... e ...

fn() {
    helper1() { : defines another function which it can call; }
    helper2() { : and another; }
    helper1 "$@" | helper2 "$@"     #processes args twice over pipe
    command "$@"; list;             #without altering its args
}

Mas esse é um pequeno exemplo. Se você considerar o significado de comando composto , poderá começar a ver que a forma convencional fn() { : cmds; } é apenas um caminho em que uma função pode funcionar. Considere alguns tipos diferentes de comandos compostos:

 { compound; list; of; commands;} <>i/o <i >o
 (subshelled; compound; list; of; commands) <>i/o <i >o
 if ...; then ...; fi <>i/o <i >o
 case ... in (...) ...;; esac <>i/o <i >o
 for ... [in ... ;] do ...; done <>i/o <i >o
 (while|until) ...; do ....; done <>i/o <i >o

E outros além disso. Qualquer uma das opções acima deve funcionar como ...

 fname() compound list

... e dentre aqueles que podem ser aninhados quando não atribuídos como uma função, ainda podem ser aninhados, mesmo se definidos como um comando.

Aqui está uma maneira de escrever sua função:

update_prof(){
    cat >&3
    read "${2-option}" <&3
    case "${1-$option}" in
    1) update_prof '' name      ;;
    2) update_prof '' age       ;;
    3) update_prof '' gender    ;;
    *) unset option             ;;
    esac
} <<-PROMPT 3<>/dev/tty
${1-
    1. Update Name
    2. Update Age
    3. Update Gender
}
    Enter ${2:-option}: $(
        printf '3%s' \[A @
)
PROMPT

Algumas notas sobre o que precede:

  1. O read na atualização está sujeito à interpretação do IFS e da contrabarra. Robustamente, poderia ser IFS= read -r "$1" , mas não tenho certeza de como você deseja que essas coisas sejam interpretadas. Procure outras respostas neste site para obter mais e melhores informações sobre essa pontuação.
  2. O printf '3%s... no aqui-doc supõe que /dev/tty esteja vinculado a um terminal compatível com VT100 e, nesse caso, as fugas usadas devem impedir que a nova linha final do here-doc seja exibida na tela. Robustamente tput seria usado. do man termcap para mais informações.
    • O melhor é que a suposição da VT100 esteja correta e você pode fazer sem printf ou tput inserindo os caracteres de escape literalmente no documento here como ^V{esc}[A^V{esc}@ em que ^V é uma maneira de representar o CONTROL+V combinação de teclas e {esc} é a chave ESC do seu teclado.

A função acima irá puxar em dobro, dependendo do seu conjunto de parâmetros - ele será executado duas vezes e reavaliar o prompt somente quando necessário - e, portanto, não precisa de uma segunda função - porque ele pode fazer as duas coisas, desde que é inicialmente chamado sem parâmetros em primeiro lugar.

Então, se eu correr ...

update_prof; printf %s\n "$name"

A atividade do terminal resultante é semelhante:

1. Update Name
2. Update Age
3. Update Gender

Enter option: 1

Enter name: yo mama
yo mama
    
por 18.01.2015 / 21:21