Bash source - selecione a função certa quando dois arquivos originados tiverem o mesmo nome de função?

2

Meu script bash cria um arquivo de script (chame de file2.sh ) de acordo com um argumento. (Ele é originado ou não.) O script file2.sh contém uma função "foo" (chame-a de uma versão modificada ou melhorada do foo).

O script também origina outro arquivo ( file1.sh ) que contém a função original "foo". Este ( file1.sh ) é sempre originado (e tem outras funções que são necessárias).

Eu preciso da função "foo" em file2.sh para substituir o "foo" em file1.sh (se file2.sh for originado).

Além disso, preciso fazer isso em qualquer script que meu script principal chamar. Vários desses arquivos de script chamados também originam file1.sh . Eles estão esperando a função "foo" original. Mas eu preciso fazê-los chamar o melhor "foo" sem modificá-los.

Em outras palavras, se meu script principal incluir file2.sh , eu quero que qualquer script (chamado pelo meu script principal) utilize file1.sh para usar "foo" de file2.sh . E não posso alterar nenhum dos outros arquivos, exceto meu script principal. (Ou, se eu alterá-los, preciso que eles funcionem corretamente quando file2.sh não for originado pelo script principal.)

    
por MountainX 03.08.2013 / 00:13

4 respostas

4

Você pode sinalizar sua função como 'somente leitura' em file2.sh.

Nota: isso causará avisos quando file1.sh mais tarde tentar definir (redefinir) a função.

Esses avisos aparecerão no stderr e podem causar problemas. Não sei se podem ser desativados.

Outra observação: isso pode fazer com que os scripts falhem, se eles estiverem verificando o status de retorno da definição da função. Eu acho que há também uma opção bash que pode ser configurada, o que faria com que um status de retorno diferente de zero em qualquer lugar abortasse a execução do script. Boa sorte!

Você pode modificar o arquivo1.sh? O simples uso de condicionais para verificar se a função está definida antes de defini-la seria uma solução mais robusta.

Aqui está um exemplo de uso de 'readonly'.

hobbes@metalbaby:~/scratch$ mkdir bash-source-test
hobbes@metalbaby:~/scratch$ cd bash-source-test
hobbes@metalbaby:~/scratch/bash-source-test$ cat > file2.sh
#!/bin/bash
fname(){
  echo "file2"
}

readonly -f fname
hobbes@metalbaby:~/scratch/bash-source-test$ cat > file1.sh
#!/bin/bash
fname(){
  echo "file1"
}

readonly -f fname
hobbes@metalbaby:~/scratch/bash-source-test$ cat >top.sh
#!/bin/bash 
if [ $1 ]; then
source file2.sh
fi  
source file1.sh

fname
hobbes@metalbaby:~/scratch/bash-source-test$ chmod +x *.sh
hobbes@metalbaby:~/scratch/bash-source-test$ ./top.sh 
file1
hobbes@metalbaby:~/scratch/bash-source-test$ ./top.sh hello
file1.sh: line 4: fname: readonly function
file2
hobbes@metalbaby:~/scratch/bash-source-test$ 
    
por 03.08.2013 / 00:58
1

Se esses scripts forem realmente interpretados por bash (e não em sh modo de emulação), e se file1.sh definir, mas não usar foo , você poderá redefinir o Os comandos source e . de tal forma que, após o fornecimento de file1.sh , garante que file1.sh não redefina foo , por exemplo, aliaseando-o para outra coisa:

$ cat file1.sh
foo() { echo original foo; }
$ cat file2.sh
foo() { echo new foo; }
$ cat main.sh
file2_sourced=false
source() {
  case ${1##*/} in
    (file2.sh)
      file2_sourced=true;;
    (file1.sh)
      if "$file2_sourced"; then
        alias foo=original_foo

        builtin source "$@"; local "ret=$?"
        unalias foo
        return "$ret"
      fi;;
  esac
  builtin source "$@"
}
.() { source "$@"; }

source file2.sh
source file1.sh

foo
original_foo

$ bash main.sh
new foo
original foo

Ou possivelmente mais apropriado:

source() {
  shopt -s expand_aliases
  if [ "${1##*/}" = file1.sh ] && command -v foo > /dev/null 2>&1; then
    # foo already defined, make file1.sh define original_foo this time
    alias foo=original_foo
    builtin source "$@"; local "ret=$?"
    unalias foo
    return "$ret"
  else
    builtin source "$@"
  fi
}

Ou seja: apenas impeça file1.sh de re -definir foo .

Agora, se esses outros scripts que main.sh chamam são executados em vez de serem originados , você precisa exportar esses source e . funções em main.sh :

export -f source .

Na verdade, também é possível fazer isso sem modificar main.sh , chamando-o como:

env BASHOPTS=expand_aliases source='() {
  if [ "${1##*/}" = file1.sh ] && command -v foo > /dev/null 2>&1; then
    # foo already defined, make file1.sh define original_foo this time
    alias foo=original_foo
    builtin source "$@"; local "ret=$?"
    unalias foo
    return "$ret"
  else
    builtin source "$@"
  fi
}' .='() { source "$@"; }' bash main.sh
    
por 03.08.2013 / 13:57
1

Eu nunca escrevi nada além de pequenos scripts bash para tarefas básicas de sysadmin e automação, por isso tenho pouca experiência em funções de sourcing de outros arquivos. Isso pode ser muito ingênuo ou não portátil ou algo assim, mas pelo menos no meu sistema e na maioria das linguagens de programação eu sei, se você redefinir algo, ele será redefinido.

Em outras palavras, contanto que você tenha certeza que você está comprando file1.sh antes file2.sh e exportar a função depois, você deve sempre estar exportando a função de file2.sh se tiver sido carregado e de file1.sh se não tiver:

$ cat file1.sh 
#!/bin/bash
fname(){
  echo "BBBBB"
}

$ cat file2.sh 
#!/bin/bash
fname(){
  echo "CCCCC"
}

$ cat foobar.sh 
#!/bin/bash
source file1.sh
if [ $1 ]; then 
    source file2.sh;
fi
export -f fname;
./run_function.sh

$ cat run_function.sh 
#!/bin/bash
fname

Todos os scripts chamados (por exemplo, ./run_function.sh ) terão qualquer fname definido último , então eles carregarão o de file2.sh se ele tiver sido originado e de file.sh se não tem:

$ ./foobar.sh
BBBBB
$ ./foobar.sh hello
CCCCC
    
por 03.08.2013 / 17:25
0

Acho que provavelmente a resposta mais fácil aqui é tirar a função desses arquivos de script.

#file[12]
#foo() { : removed; }
. ./foo.fn
#and rest of script

#foo.fn
foo() { : old version is always defined; }
[ -z "${NEWFOO:+1}" ] ||
foo() { : new version defined only if $NEWFOO; }

#your caller script
export NEWFOO=1
case $srcarg in (./file1) unset NEWFOO;; esac
#or however ^that part^ is usually done
. "$srcarg"
#and rest of script
    
por 15.01.2015 / 19:06