Execute somente se for uma função bash

3

Estou à procura de algo semelhante ao command do Bash, que só executará algo se for uma função. Atualmente, tenho uma maneira insegura de fazer:

# Go through arguments in order
for i in $*; do
    if [ -z 'which $i' ]; then
        # Run function
        $i && echo -n ' '
    fi
done

Esta declaração if não funciona corretamente. De qualquer forma, mesmo se eu pudesse checar se é um comando e não uma função, não posso rodar explicitamente uma função, o que é ruim porque se alguém tiver algum programa em $ PATH que tenha o mesmo nome da minha função, eles serão corre. Se eu anular o PATH ou configurá-lo para qualquer outra coisa, qualquer um poderia usar o $ i para executar um programa explícito, o que também não é uma solução.

De qualquer forma, posso "proteger" meu script de shell?

    
por mrrhq 21.07.2015 / 05:14

3 respostas

4

Com bash , você pode fazer algo assim:

for f do
  if declare -F -- "$f" >/dev/null 2>&1; then
    : "$f" is a function, do something with it
  fi
done

declare -F -- "$f" >/dev/null 2>&1 retornará o código de sucesso se $f for uma função bash, não produzirá nada.

Você também pode querer desabilitar alguns comandos embutidos especiais quando bash rodar no modo POSIX adicionando builtin enable -n -- "$f" .

    
por 21.07.2015 / 05:59
1

Enquanto você provavelmente está bem posicionado em um bash shell, para fazer um portável semelhante, você pode empregar algo como o seguinte ...

eval "  for c in $(unalias -a
        for c do case ${c#function}  in
                (*[!_[:alnum:]]*) ;; (?*)
                PATH=   command -v "$c" >&2 &&
                PATH=   command -V "$c"     &&
                        printf '\n'"'$c'"'\
eval "  for c in $(unalias -a
        for c do case ${c#function}  in
                (*[!_[:alnum:]]*) ;; (?*)
                PATH=   command -v "$c" >&2 &&
                PATH=   command -V "$c"     &&
                        printf '\n'"'$c'"'\%pre%'
        esac 2>/dev/null; done  |
        tr  '\t\n%pre%' '  \n'     |
        sed -ne's/.* function .* / /p')"'
;do     "$c"; printf %02s
done'
' esac 2>/dev/null; done | tr '\t\n%pre%' ' \n' | sed -ne's/.* function .* / /p')"' ;do "$c"; printf %02s done'

Funciona em etapas:

  1. Primeiro, elimine todos os argumentos que contenham quaisquer caracteres que possam desqualificá-los como nomes de shell, em primeiro lugar.
  2. Se o argumento atual for um nome válido e um shell interno incorporado ou uma função, imprima seu nome e digite para stdout. Devido ao modo como a shell pode variar sua saída para command -V , sua saída não pode ser contada para ser uma única linha. Então, siga a saída com nosso nome verificado e um byte nulo.
  3. tr anslate todas as novas linhas e tabulações para espaços e todos os bytes nulos para novas linhas.
  4. com sed , s/// ubstituir todas as linhas do último caractere de espaço existente e imprimir os resultados se a linha também corresponder à sequência function .
  5. Toda a saída de sed é arredondada como iteradores para o loop for externo e cada um deles é executado por vez, e após cada execução printf grava dois espaços.
por 21.07.2015 / 17:39
1

A maneira formal que o Bash fornece para fazer isso é usar o type incorporado em type -t :

function fun() {
  : foo bar
}

if [ function = $(type -t fun) ]; then
  echo fun is a function.
fi

Se você suspeitar do nome da função na sua lista de parâmetros, basta citar duas vezes: $(type -t "$arg")

    
por 09.12.2016 / 16:27

Tags