Funciones y Comandos Personalizados en Fish Shell

Cómo crear, editar, guardar y autocargar funciones en Fish Shell. Cubre event handlers, parseo de argumentos, scope y ejemplos prácticos.

Funciones y Comandos Personalizados en Fish Shell

Las funciones en Fish son la forma de construir comandos reutilizables. Si ya usaste funciones en Bash, la idea es la misma, pero la implementación de Fish es más limpia — sin llaves, manejo explícito de argumentos a través de $argv, y un sistema de carga diferida (lazy-loading) que mantiene el arranque rápido.

Uso funciones para todo, desde atajos rápidos hasta herramientas específicas de proyecto. Esta guía cubre cómo crearlas, guardarlas permanentemente y las características propias de Fish que las hacen más útiles que las funciones de Bash.

Crear una función básica

function greet
    echo "Hello, $argv"
end

Ejecuta greet World e imprime Hello, World. La variable $argv contiene todos los argumentos pasados a la función. Puedes acceder a argumentos individuales con $argv[1], $argv[2], etc.

Esa función solo dura la sesión actual del shell. Cierra la terminal y desaparece. Enseguida explico cómo hacer funciones permanentes.

Funciones con argumentos con nombre

Fish no tiene parámetros con nombre como Python, pero --argument-names ofrece algo parecido:

function mkcd --argument-names dir
    mkdir -p $dir
    cd $dir
end

Ahora mkcd projects/new-app crea el directorio y te mueve a él. El primer argumento se asigna a $dir. Los argumentos extra siguen disponibles a través de $argv.

Puedes listar múltiples nombres de argumento:

function connect --argument-names host port
    ssh -p $port $host
end

Agregar una descripción

function ll --description "List files in long format"
    ls -la $argv
end

La descripción aparece cuando ejecutas functions o type ll. Es documentación para tu yo del futuro.

Guardar funciones permanentemente

Fish tiene tres formas de conservar funciones entre sesiones.

Método 1: funcsave (la forma Fish)

Crea una función interactivamente y luego guárdala:

function weather --argument-names city
    curl "wttr.in/$city?format=3"
end

funcsave weather

Esto guarda la función en ~/.config/fish/functions/weather.fish. Fish la carga automáticamente cuando usas el comando en cualquier sesión futura — no al arrancar, sino bajo demanda. Esta carga diferida es la razón por la que Fish se mantiene rápido sin importar cuántas funciones guardadas tengas.

Método 2: Crear el archivo directamente

Escribe el archivo de la función tú mismo:

# ~/.config/fish/functions/weather.fish
function weather --argument-names city
    curl "wttr.in/$city?format=3"
end

Mismo resultado que funcsave. Prefiero este método para funciones que quiero versionar con mis dotfiles.

Método 3: Ponerla en config.fish

# ~/.config/fish/config.fish
function weather --argument-names city
    curl "wttr.in/$city?format=3"
end

Funciona, pero tiene dos desventajas: la función se carga en cada arranque del shell (no de forma diferida), y tu config.fish se hace más largo. Usa archivos de autocarga para cualquier función que no sea trivial.

Reglas de autocarga

Para que la autocarga funcione, el nombre del archivo debe coincidir con el nombre de la función. Una función llamada weather debe estar en weather.fish. Si el archivo contiene múltiples funciones, solo la que coincida con el nombre del archivo se autocargará.

Editar funciones

Fish tiene un editor de funciones integrado:

funced weather

Esto abre la función en $EDITOR (o $VISUAL). Cuando guardas y cierras el editor, Fish carga la función actualizada en tu sesión actual. Luego puedes hacerla permanente con funcsave weather.

Para ver la definición actual de una función sin editarla:

functions weather
# o
type weather

Ejemplos prácticos de funciones

Atajo de Git con valores por defecto

function gc --description "Git commit with message"
    if test (count $argv) -eq 0
        echo "Usage: gc <message>"
        return 1
    end
    git add --all
    git commit -m "$argv"
end

Uso: gc "fix login redirect" añade todo al staging y hace commit con ese mensaje.

Cambio rápido de proyecto

function proj --argument-names name
    set -l base ~/projects
    if test -z "$name"
        ls $base
        return
    end
    if test -d $base/$name
        cd $base/$name
    else
        echo "Project '$name' not found in $base"
        return 1
    end
end

Ejecuta proj para listar proyectos, o proj myapp para saltar a ~/projects/myapp. También puedes agregar completado con Tab:

# ~/.config/fish/completions/proj.fish
complete -c proj -f -a "(ls ~/projects)"

Ahora proj seguido de Tab lista tus directorios de proyecto. Consulta mi guía de autocompletado para más detalles sobre cómo escribir completions.

Backup con marca de tiempo

function bak --argument-names file
    if test -z "$file"
        echo "Usage: bak <file>"
        return 1
    end
    cp $file $file.bak.(date +%Y%m%d-%H%M%S)
end

bak config.yaml crea config.yaml.bak.20260224-141500.

Limpieza de Docker

function docker-clean --description "Remove stopped containers, dangling images, unused volumes"
    echo "Removing stopped containers..."
    docker container prune -f
    echo "Removing dangling images..."
    docker image prune -f
    echo "Removing unused volumes..."
    docker volume prune -f
end

Event handlers

Las funciones pueden responder a eventos. Esto es útil para ejecutar código cuando cambian variables, cuando un comando termina o cuando Fish se cierra.

Ejecutar código cuando cambia una variable

function __on_pwd_change --on-variable PWD
    if test -f .node-version
        echo "Node version: "(cat .node-version)
    end
end

Cada vez que cambias de directorio, esto verifica si existe un archivo .node-version. El flag --on-variable PWD dispara la función cada vez que $PWD cambia.

Ejecutar código al salir de Fish

function __on_exit --on-event fish_exit
    echo "Goodbye!"
end

Ejecutar código después de que un comando termina

function __notify_long_command --on-event fish_postexec
    if test $CMD_DURATION -gt 10000
        echo "Command took "(math $CMD_DURATION / 1000)" seconds"
    end
end

Esto imprime un aviso cuando cualquier comando tarda más de 10 segundos. $CMD_DURATION es una variable especial de Fish que contiene el tiempo de ejecución del último comando en milisegundos.

Nombres de event handlers

Las funciones de event handler deberían empezar con doble guion bajo o un prefijo único para evitar colisiones de nombres. Además, los event handlers en archivos de autocarga no se dispararán hasta que la función se haya cargado una vez. Para handlers que necesiten funcionar desde el inicio, ponlos en config.fish o conf.d/.

Scope y visibilidad de variables

Las funciones tienen su propio scope local por defecto. Las variables definidas con set -l dentro de una función no son visibles fuera de ella:

function test_scope
    set -l secret "hidden"
    echo $secret
end
test_scope  # prints "hidden"
echo $secret  # prints nothing

Usa set -g para variables globales (visibles en toda la sesión) o set -U para variables universales (persisten en todas las sesiones de Fish):

set -g session_var "I last until you close this terminal"
set -U persistent_var "I survive restarts"

Funciones vs abreviaturas vs alias

Fish tiene tres formas de crear atajos. Aquí cuándo usar cada una:

FuncionesAbreviaturasAlias
Mejor paraLógica compleja, comandos multilíneaAtajos de comandos simplesWrapping simple de comandos
ExpansiónSin expansión, se ejecuta tal cualSe expande en la línea de comandos antes de ejecutarSe envuelve como función internamente
El historial muestraNombre de la funciónComando expandidoNombre del alias
ArgumentosManejo completo con $argvLimitado (basado en posición/regex)Pass-through con $argv

Uso abreviaturas para atajos simples (gsgit status), y funciones para cualquier cosa que necesite lógica. Cubrí las abreviaturas vs alias en detalle en Fish Shell abreviaturas vs alias.

Gestión de funciones

functions                    # list all defined functions
functions -n                 # list function names only
functions weather            # show a function's definition
functions -e weather         # erase a function
funcsave weather             # save to autoload file
funced weather               # edit in $EDITOR

Para eliminar una función guardada permanentemente:

functions -e weather
funcsave weather

El segundo comando escribe el estado “borrado”, eliminando el archivo de ~/.config/fish/functions/.

Guías relacionadas