diff --git a/.obsidian/plugins/languagetool/data.json b/.obsidian/plugins/languagetool/data.json index 8dfc5ff..9a203c1 100644 --- a/.obsidian/plugins/languagetool/data.json +++ b/.obsidian/plugins/languagetool/data.json @@ -9,7 +9,11 @@ "ca": "ca-ES" }, "dictionary": [ - "Git" + "Git", + "bash", + "subshells", + "subshells", + "Bash" ], "syncDictionary": false, "remoteDictionary": [], diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 3f42e94..5645232 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -14,7 +14,7 @@ "type": "markdown", "state": { "file": "Documentación personal/Mecanica de Unix/Bash Scripting Avanzado/Funciones puras.md", - "mode": "preview", + "mode": "source", "source": false }, "icon": "lucide-file", @@ -74,13 +74,11 @@ "title": "Marcadores" } } - ], - "currentTab": 1 + ] } ], "direction": "horizontal", - "width": 221.5, - "collapsed": true + "width": 317.5 }, "right": { "id": "519d5773673c1040", @@ -196,7 +194,7 @@ "obsidian-git:Open Git source control": false } }, - "active": "6baa04c1fda7d92f", + "active": "e615f9321ff830be", "lastOpenFiles": [ "Documentación personal/Mecanica de Unix/Bash Scripting Avanzado/Funciones puras.md", "Documentación personal/Mecanica de Unix/Mecánica Unix - Manejo de la shell.md", diff --git a/Documentación personal/Mecanica de Unix/Bash Scripting Avanzado/Funciones puras.md b/Documentación personal/Mecanica de Unix/Bash Scripting Avanzado/Funciones puras.md index 6f8388d..8991e31 100644 --- a/Documentación personal/Mecanica de Unix/Bash Scripting Avanzado/Funciones puras.md +++ b/Documentación personal/Mecanica de Unix/Bash Scripting Avanzado/Funciones puras.md @@ -38,3 +38,89 @@ total=$(sumar 10 5) echo "El total es: $total" ``` Esta función es aislada. Recibe datos, calcula y "devuelve" el dato imprimiéndolo (stdout), sin tocar nada más. + +### Ejemplo avanzado: +Procesamiento de texto puro +```Shell +trim_string() { + local input="$1" + # Elimina espacios al inicio y al final + sed 's/^[[:space:]]*//;s/[[:space:]]*$//' <<< "$input" + # Sed modifica streams, pero no toca el sistema, por ende, es válido. + # Es válido porque opera sobre datos y no modifica el sistema. + # En Bash, un comando externo sigue siendo "puro" mientras: + # - "No escriba en ficheros" + # - "No cambie el entorno" + # - "No tenga efectos secundarios" + + # Usar comandos externos != impuro +} + +texto_sucio=" Hola Mundo " +texto_limpio=$(trim_string "$texto_sucio") + +echo "'$texto_sucio' -> '$texto_limpio'" +``` +Aquí una función que limpia un string (quita espacios extra). Nota cómo no toca ningún archivo ni variable externa. + + +--- +# El problema de BASH: +Es posible aplicar funciones puras a bash, pero el problema es que bash está pensado para tener efectos secundarios, como la modificación de ficheros y directorios (I/O). Así que es imposible tener un script de bash que tenga solo funciones puras, a no ser que su propósito no sea modificar ningún fichero ni directorio. Es decir, si tienes una función que se llama `crear_carpeta_local()`, ten por seguro de que será una función impura. + +En Bash, es muy fácil romper la pureza, porque está orientado a efectos secundarios. +El motivo: +- Usa subshells +- Depende del entorno +- Depende del estado del sistema +- Incluso `echo`, en algunos casos mal utilizados, puede dar impureza al código por dar un valor que la función base no debe devolver. `echo` es aceptable en funciones puras siempre que imprima **únicamente** el valor que la función devuelve. Cualquier salida extra rompe la pureza. + +>*Una "Función Pura" es solo un modelo conceptual: Una función que **se comporta como pura**, aunque en la práctica nunca es 100% pura.* + + + +Es innecesario e incluso contraproducente intentar que *todo* sea puro en Bash. Hay que aplicar pureza **solo a funciones que procesan datos**. + +--- +# Reglas de purificación +Esto empieza a sonar como culto religioso. + +Si quieres hacer funciones puras en bash, asegúrate de seguir estas reglas: +1. Usa siempre `local`. + Por defecto, todas las variables son globales. Si defines `i=1` dentro de una función, estás cambiando `i` en todo el script. + - **Regla**: Declara cada variable interna con local (o `declare -n` para referencias). + +2. **Entradas solo por argumentos (`$1`, `$2`...)** + Nunca, NUNCA leas una variable global dentro de una función. + Si la función necesita un dato, pásalo como parámetro. + - *Mal*: `echo $USUARIO_GLOBAL` + - *Bien*: `local usuario=$1` + + +3. **Salidas por `stdout` (echo/printf)** + No asignes el resultado a una variable global. Imprímelo. Quien llame a la función va a capturar ese valor usando *command substitution* `var=$(mi_funcion). + - *Nota*: Usa `return` solo para indicar éxito (0) o error (no-0), no para devolver data. + +4. **No escribas en stdout si la función se usa como "función pura interna"**. + Me explico; Si la función forma parte de un pipeline complejo, imprimir cosas rompe la composición. + + Ejemplo de lo que NO se debe hacer: + ```Shell + sumar() { + echo "$(( $1 + $2 ))" + echo "Suma completada" # Esto lo caga todo. + } + ``` + El problema no es imprimir como tal. + El problema es imprimir *cualquier cosa que no sea el valor* que la función debe devolver. + + Es decir: + - **Pura**: Imprime SOLO el resultado. + - **Impura**: Imprime logs, mensajes, basura, colores, etc. + +--- +# El porqué de todo esto +Tal vez pienses que es más trabajo, pero los beneficios son enormes: +1. **La depuración es más sencilla**: Si algo, por alguna razón, falla, sabes que el error está *dentro* de ella o en los argumentos que recibió. No tienes que rastrear variables globales por todo el fichero. +2. **Reutilización**: Puedes hacer *copy-paste* de una función pura en otro script y funcionará inmediatamente sin romper nada (en la gran mayoría de casos). +3. **Seguridad**: Evita las colisiones de nombres de variables (el típico error donde dos bucles usan la variable `i` y uno rompe al otro).