A maneira como você normalmente incluiria um script é com "source"
por exemplo:
main.sh:
#!/bin/bash
source incl.sh
echo "The main script"
incl.sh:
echo "The included script"
A saída da execução "./main.sh" é:
The included script
The main script
... Agora, se você tentar executar esse shell script de outro local, ele não poderá encontrar o include, a menos que esteja no seu caminho.
Qual é uma boa maneira de garantir que seu script encontre o script de inclusão, especialmente se, por exemplo, o script precisar ser portátil?
Respostas:
Eu costumo fazer com que meus scripts sejam relativos um ao outro. Dessa forma, eu posso usar o dirname:
fonte
which $0
irá ser útilBASH_SOURCE
matriz e como o primeiro elemento dessa matriz sempre aponta para a fonte atual.Sei que estou atrasado para a festa, mas isso deve funcionar, independentemente de como você inicia o script e usa exclusivamente os componentes internos:
.
O comando (ponto) é um alias parasource
,$PWD
é o Caminho para o Diretório de Trabalho,BASH_SOURCE
é uma variável de matriz cujos membros são os nomes de arquivos de origem,${string%substring}
retira a menor correspondência de $ substring na parte de trás de $ stringfonte
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
necessária? Posso encontrar uma necessidade, se os comandos estiverem sendo colados em um prompt do bash para execução. No entanto, se estiver rodando dentro de um contexto de arquivo de script, eu não posso ver uma necessidade para ela ...source
s (quero dizer, se você ésource
um script quesource
é outro em outro diretório e assim por diante, ele ainda funciona).${BASH_SOURCE[0]}
assim que você deseja apenas a última chamada? Além disso, usarDIR=$(dirname ${BASH_SOURCE[0]})
permitirá que você se livrar do se-condiçãoUma alternativa para:
é:
.. a vantagem de não depender da dirname, que não é um comando interno (e nem sempre está disponível nos emuladores)
fonte
basePath=$(dirname $0)
me deu um valor em branco quando o arquivo de script que contém é originado.Se estiver no mesmo diretório, você pode usar
dirname $0
:fonte
$0
é./t.sh
e dirname retorna.
; 2) apóscd bin
o retorno.
não está correto.$BASH_SOURCE
não é melhor.source "$(dirname $0)/incl.sh"
funciona para esses casosEu acho que a melhor maneira de fazer isso é usar o caminho de Chris Boran, mas você deve calcular MY_DIR desta maneira:
Para citar as páginas de manual do readlink:
Eu nunca encontrei um caso de uso em que
MY_DIR
não seja calculado corretamente. Se você acessar seu script através de um link simbólico no seu,$PATH
ele funcionará.fonte
$0
diretamente?/home/you/script.sh
Você podecd /home
e executar o script de lá como./you/script.sh
Neste casodirname $0
voltará./you
e inclusive outro script falharáMY_DIR=$(dirname $(readlink -f $0)); source $MY_DIR/incl.sh
Uma combinação das respostas a esta pergunta fornece a solução mais robusta.
Ele funcionou para nós em scripts de nível de produção com grande suporte a dependências e estrutura de diretórios:
O método suporta todos estes:
readlink
)${BASH_SOURCE[0]}
é mais robusto que$0
fonte
readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0
se o seu link de leitura for BusyBox v1.01DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0)) # https://stackoverflow.com/a/34208365/
fonte
./incl.sh
resolve para o mesmo caminho quecd
+pwd
. Então, qual é a vantagem de alterar o diretório?Isso funciona mesmo se o script for originado:
fonte
BASH_SOURCE
é uma matriz de caminhos, correspondente a uma pilha de chamadas. O primeiro elemento corresponde ao script mais recente na pilha, que é o script atualmente em execução. Na verdade,$BASH_SOURCE
chamado como variável se expande para seu primeiro elemento por padrão, portanto,[0]
não é necessário aqui. Veja este link para detalhes.1. Mais arrumado
Eu explorei quase todas as sugestões e aqui está a mais legal que funcionou para mim:
script_root=$(dirname $(readlink -f $0))
Funciona mesmo quando o script está vinculado a um
$PATH
diretório.Veja em ação aqui: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent
2. O mais legal
Na verdade, isso é de outra resposta nesta página, mas também estou adicionando à minha resposta!
2. O mais confiável
Como alternativa, nos raros casos em que eles não funcionaram, aqui está a abordagem à prova de balas:
Você pode vê-lo em ação na
taskrunner
fonte: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunnerEspero que isso ajude alguém lá fora :)
Além disso, deixe como comentário se um não funcionou para você e mencione seu sistema operacional e emulador. Obrigado!
fonte
Você precisa especificar a localização dos outros scripts, não há outra maneira de contornar isso. Eu recomendo uma variável configurável na parte superior do seu script:
Como alternativa, você pode insistir para que o usuário mantenha uma variável de ambiente indicando onde está sua página inicial do programa, como PROG_HOME ou algo assim. Isso pode ser fornecido ao usuário automaticamente, criando um script com essas informações em /etc/profile.d/, que será obtido toda vez que um usuário efetuar login.
fonte
Eu sugiro que você crie um script setenv cujo único objetivo seja fornecer locais para vários componentes em seu sistema.
Todos os outros scripts originariam esse script para que todos os locais sejam comuns em todos os scripts usando o script setenv.
Isso é muito útil ao executar cronjobs. Você obtém um ambiente mínimo ao executar o cron, mas se você fizer todos os scripts cron primeiro incluirem o script setenv, poderá controlar e sincronizar o ambiente em que deseja que os cronjobs executem.
Usamos essa técnica em nosso macaco de construção que foi usado para integração contínua em um projeto de cerca de 2.000 kSLOC.
fonte
A resposta de Steve é definitivamente a técnica correta, mas deve ser refatorada para que sua variável installpath esteja em um script de ambiente separado onde todas essas declarações sejam feitas.
Então, todos os scripts o originam e devem mudar de novo, só é necessário alterá-lo em um local. Torna as coisas mais, er, à prova de futuro. Deus, eu odeio essa palavra! (-:
BTW Você realmente deve se referir à variável usando $ {installpath} ao usá-la da maneira mostrada no seu exemplo:
Se as chaves forem deixadas de fora, algumas conchas tentarão expandir a variável "installpath / incl.sh"!
fonte
O Shell Script Loader é a minha solução para isso.
Ele fornece uma função chamada include () que pode ser chamada várias vezes em vários scripts para referenciar um único script, mas carregará o script apenas uma vez. A função pode aceitar caminhos completos ou parciais (o script é pesquisado em um caminho de pesquisa). Também é fornecida uma função semelhante chamada load () que carregará os scripts incondicionalmente.
Ele funciona para bash , ksh , pd ksh e zsh com scripts otimizados para cada um deles; e outros shells que são genericamente compatíveis com o sh original, como ash , dash , relíquia sh , etc., por meio de um script universal que otimiza automaticamente suas funções, dependendo dos recursos que o shell pode oferecer.
[Exemplo conhecido]
start.sh
Este é um script inicial opcional. A colocação dos métodos de inicialização aqui é apenas uma conveniência e pode ser colocada no script principal. Este script também não é necessário se os scripts forem compilados.
main.sh
cinza
b.sh
resultado:
O melhor é que os scripts baseados nele também possam ser compilados para formar um único script com o compilador disponível.
Aqui está um projeto que o utiliza: http://sourceforge.net/p/playshell/code/ci/master/tree/ . Pode ser executado de forma portável, com ou sem a compilação dos scripts. A compilação para produzir um único script também pode acontecer e é útil durante a instalação.
Também criei um protótipo mais simples para qualquer parte conservadora que queira ter uma breve idéia de como um script de implementação funciona: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype .bash . É pequeno e qualquer um pode incluir o código no script principal, se quiser, se o código for executado com o Bash 4.0 ou mais recente, e também não o usar
eval
.fonte
eval
código ed para carregar dependências. Oucheval
blocos próximos ao fundo é sempre executado. Então, seja necessário ou não, com certeza está usandoeval
.eval
é puro mal e, em vez disso, não sabe como fazer bom uso dele.eval
é mau.Coloque pessoalmente todas as bibliotecas em uma
lib
pasta e use umaimport
função para carregá-las.estrutura de pastas
script.sh
conteúdoObserve que essa função de importação deve estar no início do seu script e, em seguida, você pode importar facilmente suas bibliotecas como esta:
Adicione uma única linha na parte superior de cada biblioteca (ou seja, utils.sh):
Agora você tem acesso a funções internas
utils.sh
erequirements.sh
externasscript.sh
TODO: escreva um vinculador para criar um único
sh
arquivofonte
Usar source ou $ 0 não fornecerá o caminho real do seu script. Você pode usar a identificação do processo do script para recuperar seu caminho real
Estou usando esse script e ele sempre me serviu bem :)
fonte
Coloquei todos os meus scripts de inicialização em um diretório .bashrc.d. Essa é uma técnica comum em lugares como /etc/profile.d, etc.
O problema com a solução usando globbing ...
... é possível que você tenha uma lista de arquivos "muito longa". Uma abordagem como ...
... é executado, mas não altera o ambiente, conforme desejado.
fonte
Claro, cada um na sua, mas acho que o bloco abaixo é bastante sólido. Acredito que isso envolva a "melhor" maneira de encontrar um diretório e a "melhor" maneira de chamar outro script bash:
Portanto, essa pode ser a melhor maneira de incluir outros scripts. Isso se baseia em outra resposta "melhor" que informa ao script bash onde ele está armazenado
fonte
Isso deve funcionar de forma confiável:
fonte
nós apenas precisamos descobrir a pasta onde nosso incl.sh e main.sh estão armazenados; apenas mude seu main.sh com isto:
main.sh
fonte
echo
e uso incorreto desed
'sg
opção. -1.SCRIPT_DIR=$(echo "$0" | sed "s/${SCRIPT_NAME}//")
e depoissource "${SCRIPT_DIR}incl.sh"
O
man hier
local adequado para o script inclui é/usr/local/lib/
Pessoalmente, prefiro
/usr/local/lib/bash/includes
incluir. Existe uma biblioteca bash-helper para incluir bibliotecas dessa maneira:fonte
Você também pode usar:
fonte