La forma en que normalmente incluirías un script es con "fuente"
p.ej:
main.sh:
#!/bin/bash
source incl.sh
echo "The main script"
incluido sh:
echo "The included script"
El resultado de ejecutar "./main.sh" es:
The included script
The main script
... Ahora, si intenta ejecutar ese script de shell desde otra ubicación, no puede encontrar la inclusión a menos que esté en su ruta.
¿Cuál es una buena manera de asegurarse de que su script pueda encontrar el script de inclusión, especialmente si, por ejemplo, el script necesita ser portátil?
Respuestas:
Tiendo a hacer que todos mis guiones sean relativos entre sí. De esa manera puedo usar dirname:
fuente
which $0
será útilBASH_SOURCE
matriz y cómo el primer elemento de esta matriz siempre apunta a la fuente actual.Sé que llego tarde a la fiesta, pero esto debería funcionar sin importar cómo inicies el script y use exclusivamente los builtins:
.
El comando (punto) es un alias parasource
,$PWD
es la ruta para el directorio de trabajo,BASH_SOURCE
es una variable de matriz cuyos miembros son los nombres de los archivos de origen, las${string%substring}
tiras coinciden más cortas de $ substring desde la parte posterior de $ stringfuente
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
necesita la línea ? Puedo encontrar una necesidad si los comandos se pegan en un indicador de bash para que se ejecute. Sin embargo, si se ejecuta dentro de un contexto de archivo de script, no puedo ver la necesidad de hacerlo ...source
(es decir, sisource
un scriptsource
es otro en otro directorio, etc., todavía funciona).${BASH_SOURCE[0]}
como solo quieres la última llamada? Además, el usoDIR=$(dirname ${BASH_SOURCE[0]})
le permitirá deshacerse de la condiciónUna alternativa a:
es:
.. la ventaja es que no depende de dirname, que no es un comando incorporado (y no siempre está disponible en emuladores)
fuente
basePath=$(dirname $0)
me dio un valor en blanco cuando se obtiene el archivo de script que contiene.Si está en el mismo directorio, puede usar
dirname $0
:fuente
$0
es./t.sh
y retornos dirname.
; 2) después de quecd bin
la devolución.
no sea correcta.$BASH_SOURCE
No es mejor.source "$(dirname $0)/incl.sh"
funciona para esos casosCreo que la mejor manera de hacer esto es usar la forma de Chris Boran, PERO debes calcular MY_DIR de esta manera:
Para citar las páginas del manual para readlink:
Nunca he encontrado un caso de uso donde
MY_DIR
no se calcule correctamente. Si accede a su script a través de un enlace simbólico en su$PATH
, funciona.fuente
$0
directamente?/home/you/script.sh
posiblecd /home
y ejecute su secuencia de comandos desde allí, ya que./you/script.sh
en este casodirname $0
volverá./you
e incluirá otra secuencia de comandos que fallaráMY_DIR=$(dirname $(readlink -f $0)); source $MY_DIR/incl.sh
Una combinación de las respuestas a esta pregunta proporciona la solución más sólida.
Funcionó para nosotros en scripts de grado de producción con gran soporte de dependencias y estructura de directorios:
El método admite todos estos:
readlink
)${BASH_SOURCE[0]}
es más robusto que$0
fuente
readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0
si su readlink es BusyBox v1.01DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0)) # https://stackoverflow.com/a/34208365/
fuente
./incl.sh
resuelve en la misma ruta quecd
+pwd
. Entonces, ¿cuál es la ventaja de cambiar el directorio?Esto funciona incluso si el script tiene su origen:
fuente
BASH_SOURCE
es una matriz de rutas, que corresponde a una pila de llamadas. El primer elemento corresponde al script más reciente en la pila, que es el script que se está ejecutando actualmente. En realidad,$BASH_SOURCE
llamado como una variable se expande a su primer elemento por defecto, por[0]
lo que no es necesario aquí. Vea este enlace para más detalles.1. Neatest
Exploré casi todas las sugerencias y aquí está la más bonita que me funcionó:
script_root=$(dirname $(readlink -f $0))
Funciona incluso cuando el script está vinculado a un
$PATH
directorio.Véalo en acción aquí: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent
2. El más genial
Esto es en realidad de otra respuesta en esta misma página, ¡pero también lo agregaré a mi respuesta!
2. El más confiable
Alternativamente, en el raro caso de que no funcionó, aquí está el enfoque a prueba de balas:
Puedes verlo en acción en la
taskrunner
fuente: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunnerEspero que esto ayude a alguien por ahí :)
Además, déjelo como comentario si alguno no funcionó para usted y mencione su sistema operativo y emulador. ¡Gracias!
fuente
Debe especificar la ubicación de los otros scripts, no hay otra forma de evitarlo. Recomiendo una variable configurable en la parte superior de su script:
Alternativamente, puede insistir en que el usuario mantenga una variable de entorno que indique dónde se encuentra el inicio de su programa, como PROG_HOME o somesuch. Esto se puede proporcionar al usuario automáticamente creando un script con esa información en /etc/profile.d/, que se obtendrá cada vez que un usuario inicie sesión.
fuente
Le sugiero que cree un script setenv cuyo único propósito sea proporcionar ubicaciones para varios componentes en su sistema.
Todas las demás secuencias de comandos generarían esta secuencia de comandos para que todas las ubicaciones sean comunes en todas las secuencias de comandos que utilizan la secuencia de comandos setenv.
Esto es muy útil cuando se ejecutan cronjobs. Obtiene un entorno mínimo cuando ejecuta cron, pero si hace que todos los scripts cron incluyan primero el script setenv, podrá controlar y sincronizar el entorno en el que desea que se ejecuten los cronjobs.
Utilizamos nuestra técnica en nuestro mono de construcción que se utilizó para la integración continua en un proyecto de aproximadamente 2,000 kSLOC.
fuente
La respuesta de Steve es definitivamente la técnica correcta, pero debe ser refactorizada para que su variable installpath esté en un script de entorno separado donde se realicen todas esas declaraciones.
Luego, todas las secuencias de comandos obtienen esa secuencia de comandos y deberían instalarse cambios de ruta, solo necesita cambiarla en una ubicación. Hace las cosas más, er, a prueba de futuro. Dios, odio esa palabra! (-:
Por cierto, debería referirse a la variable usando $ {installpath} cuando la use de la manera que se muestra en su ejemplo:
Si se dejan las llaves, algunos shells intentarán expandir la variable "installpath / incl.sh".
fuente
Shell Script Loader es mi solución para esto.
Proporciona una función llamada include () que se puede invocar muchas veces en muchos scripts para referir un solo script, pero solo cargará el script una vez. La función puede aceptar rutas completas o rutas parciales (el script se busca en una ruta de búsqueda). También se proporciona una función similar llamada load () que cargará los scripts incondicionalmente.
Funciona para bash , ksh , pd ksh y zsh con scripts optimizados para cada uno de ellos; y otros shells que son genéricamente compatibles con el sh original como ash , dash , heirloom sh , etc., a través de un script universal que optimiza automáticamente sus funciones dependiendo de las características que el shell pueda proporcionar.
[Ejemplo de Fowarded]
start.sh
Este es un script de inicio opcional. Colocar los métodos de inicio aquí es solo una conveniencia y se puede colocar en el script principal. Este script tampoco es necesario si se van a compilar los scripts.
main.sh
ceniza
b.sh
salida:
Lo mejor es que los scripts basados en él también pueden compilarse para formar un solo script con el compilador disponible.
Aquí hay un proyecto que lo usa: http://sourceforge.net/p/playshell/code/ci/master/tree/ . Puede ejecutarse de forma portátil con o sin compilar los scripts. Compilar para producir un solo script también puede suceder, y es útil durante la instalación.
También creé un prototipo más simple para cualquier partido conservador que quiera tener una breve idea de cómo funciona un script de implementación: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype .bash . Es pequeño y cualquiera puede incluir el código en su script principal si lo desea, si su código está destinado a ejecutarse con Bash 4.0 o posterior, y tampoco lo usa
eval
.fuente
eval
código ed para cargar dependencias. Oucheval
bloques cerca del fondo siempre se ejecuta. Entonces, ya sea que sea necesario o no, seguro lo está usandoeval
.eval
es pura maldad, y no sabe cómo usarlo bien.eval
es malo.Ponga personalmente todas las bibliotecas en una
lib
carpeta y use unaimport
función para cargarlas.estructura de carpetas
script.sh
contenidoTenga en cuenta que esta función de importación debe estar al comienzo de su script y luego puede importar fácilmente sus bibliotecas de esta manera:
Agregue una sola línea en la parte superior de cada biblioteca (es decir, utils.sh):
Ahora tiene acceso a funciones dentro
utils.sh
yrequirements.sh
desdescript.sh
TODO: escriba un vinculador para crear un solo
sh
archivofuente
Usar source o $ 0 no te dará la ruta real de tu script. Puede usar la identificación del proceso del script para recuperar su ruta real
Estoy usando este script y siempre me ha servido bien :)
fuente
Puse todos mis scripts de inicio en un directorio .bashrc.d. Esta es una técnica común en lugares como /etc/profile.d, etc.
El problema con la solución usando globbing ...
... es posible que tenga una lista de archivos que es "demasiado larga". Un enfoque como ...
... se ejecuta pero no cambia el entorno como se desea.
fuente
Por supuesto, para cada uno lo suyo, pero creo que el siguiente bloque es bastante sólido. Creo que esto implica la "mejor" forma de encontrar un directorio, y la "mejor" forma de llamar a otro script bash:
Por lo tanto, esta puede ser la "mejor" forma de incluir otros scripts. Esto se basa en otra "mejor" respuesta que le dice a un script bash dónde está almacenado
fuente
Esto debería funcionar de manera confiable:
fuente
solo necesitamos encontrar la carpeta donde se almacenan nuestros archivos incl.sh y main.sh; solo cambia tu main.sh con esto:
main.sh
fuente
echo
y uso incorrecto desed
lag
opción. -1.SCRIPT_DIR=$(echo "$0" | sed "s/${SCRIPT_NAME}//")
y luegosource "${SCRIPT_DIR}incl.sh"
Según el
man hier
lugar adecuado para la secuencia de comandos incluye es/usr/local/lib/
Personalmente prefiero
/usr/local/lib/bash/includes
para incluye. Hay bash-helper lib para incluir libs de esa manera:fuente
También puedes usar:
fuente