¿Por qué la fuente lib / * no funciona?

11

Tengo un pequeño programa que contiene la siguiente estructura de carpetas:

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

Cada archivo contiene una sola función que uso en main.sh.

main.sh:

source lib/*

get_products
clean_products
make_index
test_index

En lo anterior las dos primeras funciones funcionan pero las dos últimas no.

Sin embargo, si lo reemplazo source lib/*con:

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

Todo funciona como se esperaba.

Alguien sabe por qué source lib/*no funciona como se esperaba?

Philip Kirkbride
fuente
2
Sin responder a la pregunta, si quieres hacerlo de una sola vez, mira /etc/bashrccómo utiliza un forbucle para tratar /etc/profile.d/*.sh. Si confía en el contenido lib/, puede reducirse a una frase:for i in lib/*.sh; do . "$i"; done
Rico el

Respuestas:

21

El sourcecódigo incorporado de Bash solo toma un único nombre de archivo:

source filename [arguments]

Cualquier cosa más allá del primer parámetro se convierte en un parámetro posicional para filename.

Una simple ilustración:

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

Salida 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.

(Esto también se aplica a la "fuente de puntos" incorporada equivalente .que, vale la pena señalar, es la forma POSIX y, por lo tanto, más portátil).

En cuanto al comportamiento aparentemente contradictorio que está viendo, puede intentar ejecutar main.sh después de hacerlo set -x. Ver qué declaraciones se ejecutan y cuándo pueden proporcionar una pista.

Capa B
fuente
7

La documentación de Bash indica que sourcefunciona en un solo nombre de archivo :

. (un período)

. nombre de archivo [argumentos]

Lea y ejecute comandos del argumento del nombre de archivo en el contexto actual del shell. Si nombre de archivo ...

Y el código fuente ... para fuente ... respalda esto:

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

Donde source_filese define en evalfile.cllamar _evalfile:

rval = _evalfile (filename, flags);

y _evalfilesolo abre un solo archivo:

fd = open (filename, O_RDONLY);
Jeff Schaller
fuente
5

Complementando la útil respuesta de b-layer , sugeriría que nunca use una expansión codiciosa global si no está seguro de si los archivos del tipo que intenta expandirse están allí.

Cuando lo hizo a continuación, existe la posibilidad de que un archivo (que no tenga .shextensión) sea solo un archivo temporal que contenga algunos comandos dañinos (por ejemplo rm -rf *) que podrían ejecutarse (suponiendo que tengan permisos de ejecución)

source lib/*

Por lo tanto, siempre realice la expansión global con el conjunto enlazado adecuado, en su caso, aunque solo podría recorrer los *.sharchivos solo

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

Aquí [ -f "$globFile" ] || continuese encargaría de regresar del bucle si no coincide el patrón global en la carpeta actual, es decir, el equivalente de las opciones de shell extendidas nullgloben bashshell.

Inian
fuente
Usar la sustitución del proceso con cattambién funcionaría:source <(cat lib/*.sh)
Xophmeister
@Xophmeister, ... por un valor más limitado para "trabajo". Si trató de depurar con set -xy un PS4que pone BASH_SOURCEy LINENOen sus registros, ya no podría ver de qué archivo y línea proviene un comando dado.
Charles Duffy el
2
@Xophmeister, ... también, un script puede cortocircuitar su ejecución con return. Siguiendo esa práctica, cualquier script que haga eso evitaría que todos los siguientes se ejecuten.
Charles Duffy el
1
Esto está bastante cerca de cómo se hace /etc/bashrccuando se procesa /etc/profile.d/*.sh.
Rico