1. Introducción
Pasos:
- Desde el programa python creamos un array que contenga la ruta del script (bash de linux) como primer componente. Cada componente restante estará formada por la cadena "clave=valor", donde las comillas dobles forman parte del parámetro. Ojo los espacios en blanco pueden dar problemas, para ello se pueden sustituir por algúna cadena , en mi caso he utilizado "¬spc¬"(sin colillas dobles)
- Desde el script de linux verificamos que recibimos obligatoriamente los parámetros "folder", "module","funcion" y "venv". Siendo "folder" la carpeta donde tenemos que situarnos, "venv" la carpeta dentro de folder donde está el entrono virtual, "module" el módulo a llamar y "function" la función del módulo a ejecutar. En el script cambiamos a la carpeta definida en la variable "folder" y activamos el entorno virtual de la variable "venv" y ejecutamos pyton xmexec.py "module=miModulo" "function=miFuncion" .....
- El módulo xmexec.py que se encuentra en la carpeta "folder" se encarga de recibir los parámetros, arreglar los mismos sustituyendo "¬spc¬" por espacios en blanco, cargando el módulo indicado en el parámetro "module" y ejecutando la función indicada "function".
2. Llamada al script desde python
Para ello supongamos que tenemos un diccionario con el nombre del script, del módulo, función a ejecutar, entorno virtual y otros parámetros.
Sustituimos los espacios en blanco por "¬spc¬" y construimos el array para llamar a "subproces.run".
Observar que el modulo se puede definir como:
- "carpeta.modulo" ó
- "carpeta/modulo.py"
siendo "carpeta" la carpeta que contien a "modulo.py" que vamos a llamar
El script a llamar está en la carpeta "static/scripts" y se llama "execShell.sh"
import subprocess
# Diccionario que contiene los parámetros PARA EL SHELL SCRIPT
execDict={
'shell':'static/scripts/execShell.sh',
'folder':'../02.llibreries',
'venv':'/static/scripts/llibreries.sh',
'module':'eni/xmexpeni.py',
#'module':'eni.xmexpeni',
'function':'expedientENI',
'param1':'1814787N',
'param2':12,
'param3':'TD02',
'param4':'INDEX_ACTES_PLE',
'param5':'ENIExpTemplate.xml',
}
if __name__ == '__main__':
# Recogemos la ruta del shell y lo quitamos del diccionario
myShell=execDict['shell']
del execDict['shell']
execProgs=[myShell]
#Creamos la cadena con los parámetros paraejecuar
for key,value in execDict.items():
value1=value.replace(" ","¬spc¬")
st=f'"{key}={value1}"'
execProgs.append(st) #Exec progs.
print(str(subprocess.run(execProgs, capture_output=True)))
3. Llamada al programa xmexec.
Desde el script vamos a llamar al programa pyhon xmexec, paa ello:
- Verificamos que recibimos los parámetros "folder", "venv", module" y "function"
- Nos situamos a la carpeta del parámetro "folder"
- Activamos el entorno virtual de la carpeta indicada en el parámetro "venv"
- Ejecutamos "python" al que pasamos el propgrama xmexec.py y el resto de parámetros (excepto "venv" y "folder")
#!/bin/bash
#--------------------------
# 0. Exemple de crida
# ./static/scripts/llibreries.sh \
# "folder=../02.llibreries" "venv=venv02" \
# "module=eni.xmexpeni" "function=expedientENI" \
# "docsFolderPath=docs/actes2022/" \
# "templatesPath=templates/ENI/" \
# "filter=*ord*aprov*sio*_signed.pdf" \
# "anyPle=2022" \
# "expCodi=1814787N" \
# "organo=L01462384" \
# "tDocu=TD02" \
# "prefPle=INDEX_ACTES_PLE" \
# "plantillaXML=ENIExpTemplate.xml" \
# "docxIndex=indexTemplate.docx"
#
#
# O també podriem executar en background
# Run the Python script in the background
# nohup python myscript.py > /dev/null 2>&1 &
#
#---------------------------
remove_quotes() {
local arg="$1"
# Remove leading double quote if present
arg="${arg#\"}"
# Remove leading double quote if present
arg="${arg%\"}"
echo "$arg"
}
#------------------------------------
# 1. Recogemos los parametros
#------------------------------------
# Inicialize variables
folder_value=""
venv_value=""
module_value=""
other_params=""
# Función para mostrar uso del script
usage() {
echo "Uso: $0 'folder=RUTA' 'venv=carpeta entorno virtual' 'module=carpeta.modulo.py' ... otros_parametros=VALOR ..."
echo "Ejemplo:"
echo " $0 folder=/mi/carpeta env=producción venv=venv02 module=eni.xmexpeni config=ejemplo version=1.0"
exit 1
}
# Verificar si se proporcionaron argumentos
if [ $# -lt 3 ]; then
usage
fi
# Procesar cada argumento
for argIni in "$@"; do
# Remover comillas
arg=$(remove_quotes "$argIni")
# Verificar si el argumento contiene un '='
if [[ "$arg" == *=* ]]; then
# Dividir el argumento en clave y valor
key="${arg%%=*}"
value="${arg#*=}"
#echo "$key AAAA $value AAAAA $arg"
case "$key" in
folder)
folder_value="$value"
;;
venv)
venv_value="$value"
;;
module)
module_value="$arg"
;;
*)
# Concatenar otros parámetros
other_params+=" \"$arg\""
;;
esac
#echo "$key BBBBB $value"
else
echo "Argumento inválido: $arg"
usage
fi
done
#echo "FOLDER= $folder_value"
#echo "VENV= $venv_value"
#echo "MODULE= $module_value"
# Verificar que 'folder' y 'env' hayan sido proporcionados
if [[ -z "$folder_value" || -z "$venv_value" || -z "$module_value" ]]; then
echo "Error: Se requieren al menos un argumento 'folder' 'venv'y 'module'."
usage
fi
#--------------------------------------
# 2. Nos cambiamos a la carpeta del modulo
#-------------------------------------
pwd
cd "$folder_value"
pwd
#--------------------------------------
# 3. Activamos el entorno virtual
#-------------------------------------
source "$venv_value"/bin/activate
# Crear la cadena concatenada
final_string1="python xmexec.py \"${module_value}\" ${other_params}"
echo "--------------------------------------"
echo "FINAL STRING=$final_string1"
echo "--------------------------------------"
python xmexec.py \"${module_value}\" ${other_params}
4. Verificar que la carpeta del entrono virtual "venv" está creado dentro de la carpteta "folder"
5. Crear el programa python "xmexec.py" en la crpeta folder.
Este programa:
- Recoge los argumentos y cambia "¬spc¬ por espacio " "
- Carga el módulo "module" y
- Ejecuta la función con el diccionario que recoge todos los parámetrso menos "module" y "function"
'''
Execute a function from a module
paramenters:
"module= module name"
"function= function name"
"param1 = value1"
"param2 = value2"
"param_n = value_n"
'''
import importlib
import sys
requiredProps=['module','function']
def arreglaArg(arg:str)->str:
if arg.startswith('"') or arg.startswith("'"):
arg=arg[1:]
if arg.endswith('"') or arg.endswith("'"):
arg=arg[:-1]
arg=arg.replace('¬spc¬',' ')
return arg
if __name__ == '__main__':
argDict={}
for i, arg1 in enumerate (sys.argv):
arg=arreglaArg(arg1)
#print(i, "--", arg1, '===', arg)
if i>0:
aArg=arg.split('=')
#print(aArg)
argDict[aArg[0]]=aArg[1]
keys=argDict.keys()
for prop in requiredProps:
if prop not in keys:
raise Exception (prop + ' not in arguments')
moduleName=argDict['module'].replace('.py','').replace("/",".")
#print (moduleName)
#module = __import__(argDict['module']) # No acaba d'anar be
module = importlib.import_module(moduleName)
#print (module)
aFunc=argDict['function']
func = getattr(module, aFunc)
# Remove module and function props
for prop in requiredProps: del argDict[prop]
# Execute the function with arguments
#print ("executing the function "+ aFunc)
#print (argDict)
#print ("=======================================================")
func (**argDict)
6. Verificaciones
Verificar que :
- Exista el módulo dentro de la carpeta correcta
- Que el módulo tenga la funcion que vamos a llamar
- Que los parámetros de la fucnión tengan los mismos nombres y sean del mismo tipo que los parámetros que le pasamos.
7. Parámetros regex pattern
Si los parámetros "regex pattern" pueden estar definidos en un fichero u¡yaml o dentro del programa python
Por ejemplo en este fichero yaml hay 2 elementos tipo pattern.
pattern1: '^Ple \d{2} ord \d{2}-\d{2}-\d{4} .* \d{2} \d{2} \d{4}(?:_|\.)signed(?:_|\.)signed\.pdf$'
params: [1, '^Acta d{2} .* \d{2} \d{2} \d{4}.pdf$', 'otro valor']
Para el caso de der definidos en un progama python vale la pena utilizar un string "rau" que se hace anteponiendo r al string
pattern = r'^Acta d{2} .* \d{2} \d{2} \d{4}.pdf$'
Los posibles problemas que pueden aparecer son:
- Las interrogaciones "?"
- Los espacios en blanco
Las interrogaciones pueden dar problemas al convertirlas en una cadena dela URL, mientras que los espacios en blanco pueden dar problemas al pasarlos a un script como parámetro, pues los trocea.
Para el caso de las interrogaciones, se puede pasar el parámetro codificado en base64 y a veces aún así puede fallar. A un caso se pueden cambiar por "¬qm¬" y luego deshacer el cambio. Para el caso de los espacios hemos optado por sustituirlos por "¬spc¬".