fish

fish

fish, „the friendly interactive shell”, este un shell de linie de comandă conceput să fie interactiv și prietenos cu utilizatorul.

fish nu este complet compatibil POSIX în mod intenționat - încearcă să corecteze inconsecvențele POSIX (așa cum le percep dezvoltatorii) printr-o sintaxă simplificată sau diferită. Asta înseamnă că inclusiv scripturi simple, compatibile POSIX, pot avea nevoie de adaptări semnificative sau chiar de o rescriere completă pentru a rula sub fish.

Instalare

Pachetul fish este disponibil în depozitele majorității distribuțiilor:

sudo pacman -S fish

sudo apt install fish

Dacă versiunea din depozitele oficiale este prea veche, pe Ubuntu poate fi folosit PPA-ul oficial pentru o versiune mai recentă.

sudo dnf install fish

sudo zypper install fish

După instalare, se poate intra în fish rulând comanda `fish` în terminal.

Documentația poate fi consultată direct din fish, prin comanda `help`, care o deschide într-un navigator. Este recomandată cel puțin secțiunea „Syntax overview”, întrucât sintaxa fish diferă semnificativ de a altor shell-uri.

Integrare cu sistemul

Există două moduri de a folosi fish: ca shell implicit, caz în care utilizatorul intră direct în fish la autentificare, sau ca shell interactiv, rulat ca proces copil al shell-ului implicit (de obicei bash). Diferența dintre cele două moduri:

  • fish ca shell implicit: necesită o înțelegere de bază a funcționării fish și a limbajului lui de scripting. Scripturile de inițializare și variabilele de mediu existente trebuie migrate în noul mediu fish.
  • fish ca shell interactiv: este modul mai puțin disruptiv - toate scripturile de inițializare bash rulează ca de obicei, iar fish pornește peste bash, în mod interactiv, conectat la un terminal.

Setarea fish ca shell implicit

Primul pas este schimbarea shell-ului utilizatorului curent la calea către fish (de obicei `/usr/bin/fish`, verificabilă cu `which fish`):

chsh -s $(which fish)

Următorul pas este portarea acțiunilor și configurărilor necesare din scripturile de inițializare bash (`/etc/profile`, `~/.bash_profile`, `/etc/bash.bashrc` și `~/.bashrc`) în fish.

În particular, conținutul variabilei `$PATH`, odată autentificat direct în fish, trebuie verificat și ajustat. În fish, `$PATH` este o variabilă globală de mediu - are scop global peste toate funcțiile, se pierde la repornire și este exportată către procesele copil. Modul recomandat de a adăuga locații suplimentare în path este prin comanda fish_add_path, apelată din `config.fish`:

fish_add_path -p /prima/cale /a-doua/cale /a-treia

Aceste trei locații vor fi adăugate la începutul path-ului.

Folosirea fish doar ca shell interactiv

Dacă fish nu este setat ca shell implicit, scripturile bash existente rulează normal la pornire, iar variabilele de mediu ale utilizatorului rămân neschimbate și sunt exportate către fish, care rulează apoi ca proces copil al bash. Mai jos sunt câteva moduri de a rula fish interactiv fără să fie shell implicit.

Modificarea .bashrc pentru a porni fish

Cea mai simplă variantă: bash rămâne shell implicit, iar linia `exec fish` se adaugă în fișierul de configurare bash potrivit, de exemplu `.bashrc`. Asta permite bash să încarce corect `/etc/profile` și toate fișierele din `/etc/profile.d`. Pentru că fish înlocuiește procesul bash, ieșirea din fish închide și terminalul. Comparativ cu variantele de mai jos, aceasta este soluția cea mai universală, funcționând atât pe mașina locală, cât și pe un server accesat prin SSH.

  • În această configurare, `bash –norc` intră manual în bash fără să execute comenzile din `~/.bashrc` (care ar rula `exec fish` și ar reveni în fish).
  • Se pot adăuga verificări care sar în fish doar dacă procesul părinte nu este deja fish - astfel se poate intra rapid în bash rulând comanda `bash`, fără să se piardă configurarea din `~/.bashrc`.
  • Merită verificată și variabila `$SHLVL` înainte de a intra în fish, pentru a evita alternarea nedorită bash-fish la shell-uri imbricate (util dacă se folosesc programe precum `nix-shell`).

Pentru a adăuga această logică în `~/.bashrc`:

if grep -qxv 'fish' /proc/$PPID/comm && [[ ${SHLVL} == [1,2] ]]
then
	shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION=''
	exec fish $LOGIN_OPTION
fi

Opțiuni ale emulatorului de terminal

O altă variantă este pornirea emulatorului de terminal cu o opțiune de linie de comandă care rulează fish direct. La majoritatea terminalelor, opțiunea este `-e`, de exemplu pentru gnome-terminal:

gnome-terminal -e fish

La emulatoarele care nu suportă setarea shell-ului direct din linia de comandă, se poate folosi variabila `SHELL`:

SHELL=/usr/bin/fish lilyterm

De asemenea, în funcție de terminal, fish poate fi setat ca shell implicit direct din configurarea sau profilul terminalului, de exemplu la kitty:

~/.config/kitty/kitty.conf
shell /usr/bin/fish

Opțiuni ale multiplexorului de terminal

Pentru a seta fish ca shell pornit în tmux, se adaugă în `~/.tmux.conf`:

set-option -g default-shell "/usr/bin/fish"

La fiecare pornire a tmux, se va intra direct în fish.

Configurare

Fișierul de configurare rulează la fiecare autentificare și se află la `~/.config/fish/config.fish`. Comenzile sau funcțiile adăugate în acest fișier se execută, respectiv se definesc, la deschiderea unui terminal, similar cu `.bashrc`. Variabilele care trebuie păstrate între sesiuni trebuie setate ca universale, nu definite direct în fișierul de configurare.

Funcțiile utilizatorului se află în directorul `~/.config/fish/functions`, sub numele de fișier `nume_functie.fish`.

Interfața web

Culorile terminalului, prompt-ul, funcțiile, variabilele, istoricul, combinațiile de taste și abrevierile pot fi configurate printr-o interfață interactivă:

fish_config

Pornirea poate eșua dacă IPv6 este dezactivat pe sistem.

Completarea comenzilor

fish poate genera automat completări din paginile man. Completările sunt scrise în `~/.cache/fish/generated_completions` (locație controlată de variabila de mediu `XDG_CACHE_HOME`) și pot fi generate manual prin:

fish_update_completions

Completări proprii pot fi definite în `~/.config/fish/completions/`; câteva exemple se găsesc în `/usr/share/fish/completions/`.

fish include din start completări pentru comenzile specifice managerilor de pachete de pe distribuțiile majore - pacman pe Arch Linux, apt pe Debian/Ubuntu, dnf pe Fedora sau zypper pe openSUSE - întrucât politica dezvoltatorilor fish este să includă în arhiva upstream toate completările existente. Gestionarea memoriei este suficient de eficientă încât acest lucru nu afectează negativ resursele sistemului.

Trucuri și sfaturi

Substituția comenzilor

fish nu implementează substituția de istoric în stilul bash (de exemplu `sudo !!`), iar dezvoltatorii recomandă în FAQ-ul fish folosirea interfeței interactive de recuperare a istoricului: săgeata `Sus` recuperează linii întregi din istoric, `Alt+Sus` (sau `Alt+.`) recuperează argumente individuale, iar `Alt+s` adaugă `sudo` la începutul liniei curente.

Există totuși câteva soluții alternative descrise în wiki-ul fish: fără să ofere o substituție completă de istoric, unele funcții înlocuiesc `!!` cu comanda anterioară sau `!$` cu ultimul argument al comenzii anterioare.

Dezactivarea mesajului de întâmpinare

Din start, fish afișează un mesaj de întâmpinare la pornire. Pentru a-l dezactiva, se rulează o singură dată:

set -U fish_greeting

Comanda golește variabila universală `fish_greeting`, partajată de toate instanțele fish și păstrată la repornirea shell-ului.

Pornirea fish din su

Dacă `su` pornește bash pentru că bash este shell-ul implicit al utilizatorului țintă (root, dacă nu este specificat alt utilizator), se poate defini o funcție care redirecționează spre fish indiferent de shell-ul utilizatorului:

~/.config/fish/functions/su.fish
function su
   command su --shell=/usr/bin/fish $argv
end

Pornirea X la autentificare

Se adaugă următoarele la finalul `~/.config/fish/config.fish` (relevant doar pentru sistemele care pornesc X manual, prin xinit, fără display manager):

# pornește X la autentificare
if status is-login
    if test -z "$DISPLAY" -a "$XDG_VTNR" = 1
        exec startx -- -keeptty
    end
end

Pentru cei care folosesc fish doar ca shell interactiv, `status is-login` se înlocuiește cu `status is-interactive` în codul de mai sus.

Afișarea stării git în prompt

Pentru ca fish să afișeze branch-ul și starea repository-ului atunci când utilizatorul se află într-un director git, se poate defini următoarea funcție `fish_prompt`:

~/.config/fish/functions/fish_prompt.fish
function fish_prompt
  set -l last_status $status
 
  if not set -q __fish_git_prompt_show_informative_status
    set -g __fish_git_prompt_show_informative_status 1
  end
  if not set -q __fish_git_prompt_color_branch
    set -g __fish_git_prompt_color_branch brmagenta
  end
  if not set -q __fish_git_prompt_showupstream
    set -g __fish_git_prompt_showupstream "informative"
  end
  if not set -q __fish_git_prompt_showdirtystate
    set -g __fish_git_prompt_showdirtystate "yes"
  end
  if not set -q __fish_git_prompt_color_stagedstate
    set -g __fish_git_prompt_color_stagedstate yellow
  end
  if not set -q __fish_git_prompt_color_invalidstate
    set -g __fish_git_prompt_color_invalidstate red
  end
  if not set -q __fish_git_prompt_color_cleanstate
    set -g __fish_git_prompt_color_cleanstate brgreen
  end
 
  printf '%s%s %s%s%s%s ' (set_color $fish_color_host) (prompt_hostname) (set_color $fish_color_cwd) (prompt_pwd) (set_color normal) (fish_git_prompt)
 
  if not test $last_status -eq 0
    set_color $fish_color_error
  end
  echo -n '$ '
  set_color normal
end

Colorarea hostname-ului în prompt la conectare SSH

Pentru a colora dinamic hostname-ul din prompt atunci când sesiunea este conectată prin SSH, se adaugă următoarele linii fie în funcția `fish_prompt`, fie în fișierul de configurare fish (exemplul de mai jos folosește roșu):

~/.config/fish/functions/fish_prompt.fish
...
if set -q SSH_TTY
  set -g fish_color_host brred
end
...

De reținut că prompt-ul implicit din fish colorează deja hostname-ul galben la conectarea prin SSH; modificarea de mai sus nu este necesară pentru funcționalitatea de bază, ci doar pentru o culoare diferită.

Evaluarea ssh-agent

În fish, `eval (ssh-agent)` generează erori din cauza modului în care sunt setate variabilele. Soluția este folosirea opțiunii `-c`, în stil csh:

eval (ssh-agent -c)

Evaluarea keychain

Pentru că `keychain –eval` folosește variabila de mediu `SHELL` pentru a formata output-ul, funcționează normal cu fish setat ca shell implicit. Dacă fish este folosit doar ca shell interactiv, variabila trebuie setată explicit:

SHELL=/bin/fish keychain --eval --quiet -Q id_ed25519 | source

Hook-ul „command not found”

fish include un hook „command not found” care caută automat în depozitele oficiale atunci când se introduce o comandă necunoscută. Pe distribuțiile bazate pe Arch Linux, acest hook rulează prin pkgfile, cu fallback la `pacman -F` dacă pkgfile nu este instalat; pe alte distribuții, funcționalitatea echivalentă este oferită de pachete proprii, precum `command-not-found` pe Debian/Ubuntu sau modulul similar din dnf pe Fedora.

Dacă întârzierea introdusă de acest comportament este nedorită, hook-ul poate fi suprascris prin redefinirea `fish_command_not_found`, astfel încât să afișeze doar un mesaj de eroare:

function fish_command_not_found
      __fish_default_command_not_found_handler $argv[1]
  end

Pentru a păstra modificarea permanent, se folosește comanda `funcsave`:

funcsave fish_command_not_found

Eliminarea unui proces din lista de job-uri

fish închide orice job trecut în background la terminarea fish. Pentru ca un job să rămână activ și după terminarea fish, se folosește mai întâi comanda `disown`. De exemplu, comenzile de mai jos pornesc `firefox` în background și apoi îl „disown”-ează:

firefox &
disown

Astfel firefox nu se va închide odată cu procesul fish. Pentru detalii, vezi `disown(1)` în documentația fish.

Setarea unui alias permanent

Pentru a crea rapid un alias permanent:

alias lsl "ls -l"
funcsave lsl

Începând cu versiunea 3.0, `alias` suportă opțiunea `-s`/`–save`:

alias -s lsl "ls -l"

Aceasta creează funcția:

function lsl
    ls -l $argv
end

și o salvează ca funcție de shell permanentă. Toate funcțiile pot fi văzute și editate din `fish_config`, în tab-ul „Function”.

Citirea /etc/profile la autentificare

Spre deosebire de bash, fish nu citește `/etc/profile` la o autentificare pe tty. Dacă este nevoie de variabilele de mediu setate în `/etc/profile` și în fișierele din `/etc/profile.d`, se poate adăuga următoarea configurare:

~/.config/fish/config.fish
# citește /etc/profile prin bash
if status is-login
    if not set -q __sourced_profile
        set -x __sourced_profile 1
        exec bash -c "\
            test -e /etc/profile && source /etc/profile
            test -e $HOME/.bash_profile && source $HOME/.bash_profile
            exec fish --login
        "
    end
 
    # alte configurări aici
 
    set -e __sourced_profile
end

Această metodă permite folosirea fish ca shell de autentificare, păstrând totodată toate variabilele de mediu obișnuite dintr-o sesiune de autentificare bash.

Această abordare nu permite transmiterea de argumente suplimentare către invocarea shell-ului - comenzi precum `fish –login -c 'echo test'` se reduc la simplu `fish –login`.

Vezi și