Por que a fonte lib / * não funciona?

11

Eu tenho um pequeno programa que contém a seguinte estrutura de pastas:

- main.sh
- lib/
  - clean.sh
  - get.sh
  - index.sh
  - test.sh

Cada arquivo contém uma única função que eu uso em main.sh .

main.sh :

source lib/*

get_products
clean_products
make_index
test_index

No acima, as duas primeiras funções funcionam, mas as outras duas não.

No entanto, se eu substituir o source lib/* por:

source lib/get.sh
source lib/clean.sh
source lib/index.sh
source lib/test.sh

Tudo funciona como esperado.

Alguém sabe por que source lib/* não funciona como esperado?

    
por Philip Kirkbride 04.10.2017 / 16:21

3 respostas

21

O source incorporado do Bash usa apenas um único nome de arquivo:

source filename [arguments]

Qualquer coisa além do primeiro parâmetro se torna um parâmetro posicional para filename .

Uma ilustração simples:

$ cat myfile
echo "param1: $1"
$ source myfile foo
param1: foo

Saída completa de help source

source: source filename [arguments]

Execute commands from a file in the current shell.

Read and execute commands from FILENAME in the current shell.  The
entries in $PATH are used to find the directory containing FILENAME.
If any ARGUMENTS are supplied, they become the positional parameters
when FILENAME is executed.

Exit Status:
Returns the status of the last command executed in FILENAME; fails if
FILENAME cannot be read.

(Isto também se aplica ao equivalente "dot source" embutido em . , que, vale a pena notar, é o modo POSIX e, portanto, mais portátil.)

Quanto ao comportamento aparentemente contraditório que você está vendo, você pode tentar executar main.sh depois de fazer set -x . Ver quais instruções estão sendo executadas e quando podem fornecer uma pista.

    
por 04.10.2017 / 16:24
7

Documentação do Bash indica que source funciona em um único nome do arquivo :

. (a period)

. filename [arguments]

Read and execute commands from the filename argument in the current shell context. If filename ...

E o código-fonte ...  para a fonte ... confirma isso:

result = source_file (filename, (list && list->next));

Em que source_file é definido em evalfile.c para chamar _evalfile :

rval = _evalfile (filename, flags);

e _evalfile só abrem um único arquivo:

fd = open (filename, O_RDONLY);
    
por 04.10.2017 / 16:32
5

Complementando a resposta útil da b-layer , eu sugeriria nunca usar uma expansão gulosa se você não tiver certeza se os arquivos do tipo que tentam expandir estão lá.

Quando você fez o seguinte, existe a possibilidade de um arquivo (não ter .sh extension) apenas um arquivo temporário contendo alguns comandos nocivos (por exemplo, rm -rf * ) que poderiam ser executados (supondo que eles tenham permissões de execução)

source lib/*

Portanto, sempre faça a expansão glob com o próprio conjunto de limites, no seu caso, embora você possa fazer um loop apenas nos arquivos *.sh

for globFile in lib/*.sh; do
    [ -f "$globFile" ] || continue
    source "$globFile"
done

Aqui, o [ -f "$globFile" ] || continue cuidará da devolução do loop se nenhum padrão de globalização corresponder à pasta atual, ou seja, equivalente às opções de shell estendidas nullglob in bash shell.

    
por 04.10.2017 / 16:32

Tags