Alias és function a fish shellben

enlightened Ez az oldal a közösségért készül. heart Kövess minket máshol is:  Linux Mint Magyar Közösség a Mastodon-on  Telegram csatorna – csak hírek  Beszélgessünk a Telegram – Linux csevegő csoport  Hírek olvasása RSS segítségével  Linux Mint Hivatalos Magyar Közösség a Facebook-on      Linux Mint Baráti Kör a Facebook-on
wink Ha hasznosnak találod, és szeretnéd, hogy folytatódjon, támogasd a munkát Ko-fi vagy Paypal segítségével. laugh

A fish két, egymásra épülő eszközt kínál a gyakran ismételt parancsok lerövidítésére: az aliast és a functiont. Elsőre könnyű azt hinni, hogy ez a bash-ből ismert alias egy az egyben átültetve, de a fish ennél jóval mélyebbre megy — sőt, ahogy látni fogjuk, az alias valójában maga is egy function, csak egy nagyon egyszerűsített formában. Ez a cikk sorra veszi mindkettőt: hogyan működnek a motorháztető alatt, mire jók, mikor érnek véget a lehetőségeik, és hol kezdődik az, amit már csak valódi functionnel érdemes megoldani.

1. Az alias — gyors parancshelyettesítés

1.1 Mi történik valójában

A fish alias parancsa nem külön nyelvi elem, hanem egy beépített wrapper a function köré. Amikor ezt írod:

alias rmi="rm -i"

a fish a háttérben pontosan ezt hozza létre:

function rmi --wraps rm --description 'alias rmi=rm -i'
    rm -i $argv
end

Ez két fontos dolgot árul el. Egyrészt: az alias automatikusan hozzáfűzi a $argv-t a parancs végéhez, tehát minden paramétert, amit az alias után írsz, továbbad az eredeti parancsnak. Másrészt: mivel valódi function jön létre, ugyanazokkal az eszközökkel (functions, funced, funcsave) kezelhető, mint bármelyik kézzel írt function.

1.2 Létrehozás és szintaxis

Kétféle írásmód is működik:

alias ll 'ls -la'
alias ll='ls -la'

Szóközt tartalmazó parancsoknál figyelni kell az idézőjelezésre — pontosan úgy, mintha a parancssorba írnád be:

alias chrome='/opt/google/chrome/chrome\ browser'

1.3 Ideiglenes vagy végleges

Alapból az alias csak az aktuális shell-munkamenetre érvényes, új terminál nyitásakor eltűnik. Három út vezet a véglegesítéshez:

a) --save (vagy -s) kapcsoló — ez a legrövidebb út, egy lépésben menti a ~/.config/fish/functions/ könyvtárba:

alias --save ll 'ls -la'

b) funcsave külön lépésben:

alias ll 'ls -la'
funcsave ll

Ez ugyanoda ment egy ll.fish fájlt, ami a fish autoload-mechanizmusa miatt minden induláskor automatikusan elérhető lesz — nem kell semmit sourceolni.

c) Bekerül a config.fish-be:

# ~/.config/fish/config.fish
alias ll 'ls -la'
alias gs 'git status'
alias update 'sudo apt update; and sudo apt upgrade'

Ez minden induláskor újra lefuttatja az alias parancsokat. Előnye, hogy az összes rövidítésed egy helyen, jól áttekinthetően, verziókezelhető dotfiles-ként tartható. Hátránya, hogy sok tucat alias esetén ez apró késést ad a shell indulásához, míg a funcsave-es, autoload-olt fájlok csak akkor töltődnek be, amikor ténylegesen meghívod őket.

1.4 Az alias korlátai

Itt érdemes megállni egy fontos csapdánál. Mivel az alias mindig hozzáfűzi a $argv-t a teljes definíció végéhez, összetett, több parancsból álló alias esetén az eredmény meglepő lehet:

alias mentes "cp -r ~/dokumentumok /backup; du -sh /backup"

Ha ezt paraméterrel hívod (mentes valami), a valami csak a második parancshoz, a du -sh /backup-hoz fog odaragadni, nem oda, ahová logikusan várnád. Az alias tehát jól működik egyetlen parancs egyszerű kiegészítésére vagy rövidítésére, de amint elágazás, ciklus, több lépés vagy paraméterek okos elhelyezése kell, a function az útja.

1.5 Aliasok kezelése

alias                 # listázza az összes, alias-ból származó functiont
functions -e ll        # törli (az alias-t is így kell törölni, nincs külön "unalias")

2. A function — amikor az aliasnál többre van szükség

2.1 Alapszintaxis

function nev
    parancsok
end

A $argv itt is elérhető, de listaként, index szerint is olvasható ($argv[1], $argv[2], $argv[1..2]), és a count $argv megadja a paraméterek számát. A fish 1-től indexel, nem 0-tól. Mert sok helyen 0-tól indul, így biztosan lesz aki ebbe jól belekeveredik. A Linux és egyéb dolgok nagy hátránya, hogy nincsenek szabványosított dolgok, csak szokásjogi elvárások. Amit vagy betartanak, vagy nem.

function udvozles
    echo "Szia, $argv[1]!"
end

2.2 Feltételek, ciklusok, elágazás

A function belsejében a teljes fish nyelv rendelkezésre áll — if/else, for, while, switch/case, and/or láncolás (a fish ezeket használja &&/|| helyett), saját exit kóddal (return), és set -l-lel lokális, csak a functionre érvényes változókkal.

function ujdir --description 'Könyvtár létrehozása és belépés'
    if test (count $argv) -eq 0
        echo "Használat: ujdir "
        return 1
    end
    mkdir -p $argv[1]; and cd $argv[1]
end

2.3 --description, --wraps és a completion-öröklés

A --description a functions és a tab-completion súgójában jelenik meg. A --wraps ennél praktikusabb: azt mondja a fishnek, hogy ez a function egy másik parancsot csomagol be, ezért örökölje annak tab-completionjeit.

function ll --wraps ls --description 'ls, olvashatóbb kimenettel'
    ls -lh --color=auto $argv
end

Emiatt a ll után nyomott Tab pontosan úgy kínálja fel a fájlneveket és az ls kapcsolóit, mintha ls-t írnál — ezt egy sima alias is megkapja automatikusan, de saját, több lépéses functionnél ezt külön be kell írnod, ha szeretnéd ezt a kényelmet.

2.4 Komplex kapcsolók: argparse

Amint egy function több kapcsolót is kezel (-c, --jobs 4 stb.), a kézi $argv-turkálás helyett az argparse a helyes eszköz:

function build --description 'Projekt fordítása make-kel'
    argparse 'c/clean' 'j/jobs=' -- $argv
    or return

    if set -q _flag_clean
        make clean
    end

    if set -q _flag_jobs
        make -j $_flag_jobs
    else
        make
    end
end

Itt a c/clean egy kapcsoló nélküli (bool) flaget definiál, a j/jobs= pedig egy értéket vár. Az argparse minden felismert kapcsolóhoz létrehoz egy _flag_x / _flag_hosszunev változót, a fel nem ismert argumentumok pedig a megtisztított $argv-ban maradnak.

2.5 Automatikus futás eseményekre: --on-variable, --on-event

A function nem csak kézzel hívható — kötheted változó módosulásához vagy fish-eseményhez is. Klasszikus példa: automatikus ls minden könyvtárváltáskor:

function _auto_ls --on-variable PWD --description 'Automata ls könyvtárváltáskor'
    ls
end

Ez a functions mappába mentve minden cd után lefut, mert a PWD universal-szerűen figyelt változó módosulására reagál.

2.6 Fenntartott nevek és óvatosság

A functionök nem viselhetik a fish nyelvi kulcsszavainak nevét (if, for, function, switch, argparse stb.), és kerülendő, hogy egy meglévő beépített paranccsal azonos nevű, de eltérő viselkedésű functiont hozz létre — ez könnyen okoz zavart saját magadnak vagy más scriptjeidnek, amik a beépített viselkedésre számítanak. Ha csak interaktív kényelemről van szó (pl. ls mindig színesen fusson), arra inkább az abbr illik jobban (lásd lentebb).

3. Mentés, szerkesztés, autoload

Parancs Mit csinál
functions Kilistázza az összes definiált functiont
functions nev Kiírja egy adott function forráskódját
functions -e nev Törli a functiont (aliasnál is ezt kell)
funced nev Megnyitja a functiont $EDITOR-ban szerkesztésre
funcsave nev Elmenti a jelenleg memóriában lévő definíciót fájlba

A fish autoload a lényeg, ami miatt ritkán kell bármit is kézzel sourceolni: ha egy nev.fish fájl bekerül a ~/.config/fish/functions/ könyvtárba (pontosan a function nevével egyező fájlnévvel), a fish az első hívásnál automatikusan betölti — nem kell újraindítani a shellt, nem kell semmit source-olni. Ezért a legegyszerűbb módja egy új function létrehozásának, ha közvetlenül ide írsz egy fájlt:

# ~/.config/fish/functions/extract.fish
function extract --description 'Archívum kicsomagolása kiterjesztés alapján'
    if not test -f $argv[1]
        echo "A fájl nem létezik: $argv[1]"
        return 1
    end
    switch $argv[1]
        case '*.tar.bz2'
            tar xjf $argv[1]
        case '*.tar.gz' '*.tgz'
            tar xzf $argv[1]
        case '*.zip'
            unzip $argv[1]
        case '*.rar'
            unrar x $argv[1]
        case '*.7z'
            7z x $argv[1]
        case '*'
            echo "Nem támogatott formátum: $argv[1]"
            return 1
    end
end

Ezt elmentve azonnal használható, semmilyen config.fish-módosítás nem kell hozzá.

4. Életszerű példák

4.1 Alias-szintű rövidítések

alias --save ll 'ls -lh --color=auto'
alias --save la 'ls -lha --color=auto'
alias --save gs 'git status'
alias --save gp 'git pull'
alias --save vim 'nvim'
alias --save df 'df -h'

4.2 mkcd — könyvtár létrehozása és belépés egy lépésben

function mkcd --description 'mkdir -p, majd cd egy lépésben'
    if test (count $argv) -eq 0
        echo "Használat: mkcd "
        return 1
    end
    mkdir -p -- $argv[1]; and cd -- $argv[1]
end

4.3 backup — időbélyeges biztonsági másolat

function backup --description 'Fájl(ok) másolása .bak kiterjesztéssel, dátummal'
    if test (count $argv) -eq 0
        echo "Használat: backup fájl1 [fájl2 ...]"
        return 1
    end
    for file in $argv
        cp -a -- $file $file.(date +%Y%m%d_%H%M%S).bak
    end
end

4.4 gcm — git commit ellenőrzéssel

function gcm --description 'git commit -m üzenet-ellenőrzéssel'
    if test (count $argv) -eq 0
        echo "Használat: gcm 'commit üzenet'"
        return 1
    end
    git commit -m "$argv"
end

4.5 longrun — értesítés egy hosszabb parancs végén

Kifejezetten hasznos forduló build-eknél vagy nagyobb másolásoknál:

function longrun --description 'Parancs futtatása, majd asztali értesítés'
    eval $argv
    set -l exit_code $status
    if test $exit_code -eq 0
        notify-send "Kész ✔" "Lefutott: $argv"
    else
        notify-send "Hiba ✘" "Sikertelen ($exit_code): $argv"
    end
end

Használat: longrun make -j8.

5. Bónusz: az abbr, a modern alternatíva

A fish dokumentációja maga is azt javasolja, hogy interaktív, gyakran gépelt rövidítésekhez inkább az abbr-t érdemes használni alias helyett. A különbség lényegi: az abbr élőben, gépelés közben helyettesíti be a rövidítést a teljes paranccsal, mielőtt az lefutna — tehát a shell-előzményekben (és képernyőmegosztás, oktatóvideó esetén a nézőknek) mindig a teljes, valódi parancs látszik, nem a rövidítés.

abbr --add gco git checkout
abbr --add L --position anywhere "| less"

Itt a gco begépelése és szóköz/Enter lenyomása után a parancssorban ténylegesen git checkout jelenik meg, mielőtt lefutna. Fontos gyakorlati részlet: a fish 3.6 óta az abbr-eket nem menti automatikusan univerzális változóba — ezeket a config.fish-ba (vagy egy külön conf.d/ fájlba) kell felvenni, ugyanúgy, ahogy az aliasokat szokás.

Egy tömör ökölszabály a háromhoz:

Eszköz Mikor jó Korlát
abbr Interaktív gyorsírás, ahol jó látni a teljes parancsot Csak interaktív módban működik, scriptben nem
alias Egyetlen parancs egyszerű, fix kiegészítése Több parancs / összetett logika esetén megbízhatatlan a $argv elhelyezése
function Több lépés, feltétel, ciklus, saját kapcsolók, esemény-vezérelt futás Kicsit több gépelés, de cserébe teljes nyelv áll rendelkezésre

Összefoglalás

A fish-ben az alias valójában csak egy kényelmi „gyorsbillentyű” a function-építéshez — pont ezért nem véletlen, hogy ugyanazokkal a parancsokkal (functions, funcsave, funced) kezelhető, mint egy kézzel írt function. Amíg egyetlen parancs rövid formájáról van szó, az alias (vagy még inkább az interaktív használatra szánt abbr) tökéletesen elég. Amint feltétel, ciklus, több lépés, saját kapcsoló vagy automatikus esemény-vezérelt futás merül fel, érdemes egyenesen a functionhöz nyúlni, és a ~/.config/fish/functions/ könyvtárban, önálló fájlként tárolni — ez a fish natív, autoload-ra épülő megközelítése, ami gyors indulást és jól karbantartható, egy fájl/egy function szerkezetet ad.

Tipp:

Ha nem vagy jártas a programozásban, vagy nagyon összetett megoldásokat szeretnél, akkor használj mesterséges intelligenciát! Gyors és kényelmes megoldás, ha normálisan le tudod írni a kívánságaidat. Illetve az interneten sok előre kész megoldást találsz, hiszen a fish shell egy nagyon elterjedt, kényelmes shell, amit sok linux disztribúció alapból kínál.

Hozzászólások

Egy időben el sem tudtam

Értékelés: 

0
Még nincs értékelve

Egy időben el sem tudtam képzelni az életet fish nélkül, aztán amikor egyre több saját szkriptet kezdtem használni, akkor a hordozhatóság/kompatibilitás miatt ezek mindig bash-ben íródtak. Egy ideig játszottam, hogy mindennapi használat fish, szkript írás bash, aminek az lett a vége, hogy fish lassan kikopott az életemből...

De most meghoztad a kedvem, hogy ránézzek (a 4-es sorozatot még nem is láttam), és volt pár újdonság is, amit nem tudtam, köszi!