Como posso fazer com que as funções desse arquivo de script sejam carregadas sem ter que fazer isso todas as vezes? “Comando não encontrado” (noções básicas de bash / script)

4

Como posso fazer com que as funções desse arquivo de script sejam carregadas sem precisar obtê-lo todas as vezes?

Eu criei um arquivo foo com funções de script que gostaria de executar. Está em /usr/bin , que está no PATH.

Arquivo foo :

#!/bin/bash
echo "Running foo"

function x {
    echo "x"
}

No entanto, quando eu digito um nome de função como x no terminal:
x: command not found

Quando digito foo :% Running foo aparece (então o arquivo está no PATH e é executável)

Depois de digitar source foo , posso digitar x para executar a função x

Uma pergunta básica, eu sei. Eu só estou tentando abstrair meus scripts para que eles sejam mais gerenciáveis (em comparação com despejar tudo em .profile ou .bashrc).

    
por mrgnw 17.07.2014 / 23:41

2 respostas

7

Seu problema é que o script no diretório .../bin é exec ed em outro ambiente shell - seu ambiente não sobrevive à sua execução e, portanto, a definição x() { ... ; } não sobrevive ao ambiente shell atual quando ele é concluído .

Quando você . ./somescript.sh (ou source em alguns shells, como bash e zsh ) o shell atual lê o script no ambiente atual e executa o conteúdo como se fosse emitido no prompt - então x() { ... ; } é definido no ambiente shell atual - seu shell interativo.

Basicamente, o problema pode ser demonstrado assim:

sh <<\HEREDOC_CMD_FILE #runs sh shell with heredoc input file
    { #begins current shell compound expression
        ( #begins subshell compound expression
            x() { echo 'I am function x.' ; } #define function x
            x #x invoked in subshell
        ) #ends subshell compound expression
        x #x invoked in current shell
    } #ends current shell compound expression
#v_end_v
HEREDOC_CMD_FILE

###OUTPUT###
I am function x.
sh: line 6: x: command not found

Da mesma forma, o ambiente definido no ( : subshell ) no exemplo acima não sobrevive à sua conclusão, nem é definido no script executado. Da mesma forma, quando sh lê o HEREDOC_CMD_FILE , está executando a mesma função que seu source ../file e executando seu conteúdo em seu ambiente de execução shell atual - embora seu ambiente seja um subshell filho para o shell que emite o prompt no qual ele é executado e, portanto, nenhum de seus ambientes sobrevive a sua conclusão a partir daí. Você pode fazer isso, no entanto, como:

. /dev/fd/0 <<\HEREDOC_CMD_FILE && x
    x() { echo 'I am function x.' ; }
HEREDOC_CMD_FILE

###OUTPUT###
I am function x.

... o que é exatamente o que você faz com source ${script} , exceto que aqui nós .dot obtemos o conteúdo de stdin enquanto você obtém o conteúdo de um arquivo no disco

A principal diferença, no entanto, entre um ( : subshell ) e um script executado é a aparência do ambiente de execução de um script executado. Quando você executa um script, é fornecido um ambiente novo no qual operar - portanto, quaisquer variáveis declaradas em seu shell atual não são transportadas para seu ambiente, a menos que sejam explicitamente exportadas ou declaradas em sua linha de comando. Esse comportamento pode ser demonstrado assim:

{   x() { printf "$FMT" "$0" "$var2" x ; } #describe env
    export FMT='argv0: %s\t\tvar2: %s\t\tI am function %s.\n' \
        var1=val1 #var1 and FMT are explicitly exported
    var2=shell_val2 #var2 is not
    cat >./script && #read out stdin to >./script
        chmod +x ./script && #then make ./script executable
        var3=val3 ./script #then define var3 for ./script's env and execute
} <<\SCRIPT ; x ; y #send heredoc to block's stdin then call {x,y}()
#!/usr/bin/sh
#read out by cat to >./script then executed in a separate environment
y() { printf "$FMT" "$0" "$var2" y ; } #describe env
echo "${var1:-#var1 is unset or null}" #$var1 if not unset or null else :-this} 
echo "${var2:-#var2 is unset or null}"
echo "${var3:-#var3 is unset or null}"
export var2=script_val2
x ; y #run x() ; y() in script
#v_end_v                                                                                                                                                                                          
SCRIPT

###OUTPUT###
val1
#var2 is unset or null
val3
./script: line 8: x: command not found
argv0: ./script         var2: script_val2               I am function y.
argv0: sh               var2: shell_val2                I am function x.
sh: line 18: y: command not found

Esse comportamento difere do de ( : subshells ) executado no prompt ou de outra forma como filhos do shell atual, porque herdam automaticamente o ambiente de seu pai, enquanto - como demonstrado acima - os filhos executados exigem que ele seja explicitamente export ed.

var1=val1 ; export var2=val2
x() { echo 'I am function x.' ; }
( 
    printf '%s\n' "$var1" "$var2" 
    x
)

###OUTPUT###
val1
val2
I am function x.

A última coisa que devo observar é que não há maneira portátil de exportar funções de um shell pai para um script executado sem .dot sourcing um arquivo contendo a definição de função dentro do script executado, como você faz com source na sua concha atual. Basicamente, você não pode portar export function como você pode com variáveis. Porém, suponho que você possa export fn_def='fn() { : fn body ; }' then eval "$fn_def" em seu script executado. E, claro, qualquer ambiente infantil - executado ou não - morre com a criança.

Portanto, se você quiser a função definida no script, mas não quiser fonte do próprio script, você terá que ler a função como saída de algum comando e eval it.

eval "$(cat ./script)"

Mas isso é praticamente a mesma coisa que . ./script - apenas menos eficiente. Você pode muito bem apenas fornecer isso.

A melhor maneira de fazer isso é remover a definição de função do próprio script e colocá-lo em seu próprio arquivo, depois fonte-lo do seu script e do shell atual quando você quiser. Assim:

{
    echo 'x() { echo "I am function x and my argv0 is ${0}." ; }' >./fn_x                                                                                                                                                         
    echo '. ./fn_x ; x' >./script                                                                                                                                                                                                 
    chmod +x ./script && ./script                                                                                                                                                                                                 
    . ./fn_x ; x                                                                                                                                                                                                                  
}
###OUTPUT###
I am function x and my argv0 is ./script.
I am function x and my argv0 is sh.

Para que isso esteja sempre disponível em um shell interativo, adicione um . /path/to/fn_x ao arquivo ENV do seu shell. Por exemplo, para bash com um script que contém funções chamadas foo localizadas em /usr/bin , você pode adicionar essa linha a ~/.bashrc :

. /usr/bin/foo

Se o script for por qualquer motivo não disponível nesse local durante a inicialização, o shell ainda lerá e fornecerá o restante do arquivo ENV conforme o esperado, embora haja uma mensagem de diagnóstico impressa em stderr para permitir Você sabe que houve um problema ao pesquisar seu arquivo de definição de função.

    
por 17.07.2014 / 23:47
3

a resposta do mikeserv é boa para os detalhes do que se passa "nos bastidores", mas sinto outra A resposta aqui é justificada, pois não contém uma resposta simples e utilizável para a pergunta exata do título:

How can I get this script file's functions to load without having to source it every time?

A resposta é: fonte de seu .bashrc ou seu .bash_profile para que ele esteja disponível em cada shell que você executa.

Por exemplo, tenho o seguinte no meu .bash_profile :

if [ -d ~/.bash_functions ]; then
  for file in ~/.bash_functions/*.sh; do
    . "$file"
  done
fi
    
por 10.05.2016 / 20:54