Vas tancar el Mòdul 2 amb criteri per decidir qui accedeix a què. Comences el Mòdul 3 amb una cosa més íntima: el lloc on treballes. Cada vegada que obres una sessió a srv-tramontana, Bash construeix al teu voltant un entorn —un conjunt de variables, dreceres, rutes de cerca i memòria del que ja has fet— que determina com es comporten totes les ordres que executis després. Fins ara l'has fet servir tal com venia de fàbrica. A partir d'aquí l'entens i el modeles.

No és un caprici estètic. El 90 % dels «però si a mi em funcionava» d'un administrador de sistemes —l'script que va des del teu terminal i falla a cron, el sort que ordena diferent al teu portàtil i al servidor, l'ordre que existeix per a operador però no per a root— són problemes d'entorn. Entendre què s'hereta, què no i des de quin fitxer es carrega cada cosa és el que separa depurar en cinc minuts de perdre una tarda.

Contingut

  1. Què és l'entorn d'un procés
  2. Variables de shell i variables d'entorn
  3. L'herència cap als processos fills
  4. PATH: com troba Bash un executable
  5. Les altres variables que importen
  6. Expansió de variables i cometes, amb precisió
  7. Substitució d'ordres
  8. Àlies
  9. Shells de login, interactius i no interactius
  10. Personalitzar l'indicador PS1
  11. Historial persistent i el seu risc de seguretat
  12. El ~/.bashrc recomanat per a operador

  1. Què és l'entorn d'un procés

Quan el nucli arrenca un procés, li lliura tres coses: els arguments de la línia d'ordres, els descriptors de fitxer oberts (que veuràs a 03-04) i un vector de cadenes CLAU=valor anomenat entorn. No és màgia de Bash: és una estructura del sistema operatiu, disponible per a qualsevol programa escrit en qualsevol llenguatge.

Com que a Linux tot és un fitxer, el pots veure directament:

operador@srv-tramontana:~$ tr '\0' '\n' < /proc/$$/environ | head -6
SHELL=/bin/bash
PWD=/home/operador
LOGNAME=operador
HOME=/home/operador
LANG=ca_ES.UTF-8
USER=operador

$$ és el PID del shell actual. El fitxer environ guarda les variables separades per bytes nuls, per això cal tr per llegir-les. L'important: aquest contingut es va fixar quan el procés va arrencar. És una fotografia, no un enllaç viu.

  1. Variables de shell i variables d'entorn

Bash gestiona dos conjunts que s'assemblen i no són el mateix.

Variable de shell Variable d'entorn
Es crea amb VAR=valor export VAR=valor
La veu el shell actual Sí Sí
La veuen els processos fills No Sí
Es llista amb set env o printenv
Ús típic treball temporal a la sessió configurar programes

L'assignació té una regla que provoca errors el primer dia: no hi pot haver espais al voltant del =.

operador@srv-tramontana:~$ RELEASE=3.2.1
operador@srv-tramontana:~$ RELEASE = 3.2.1
RELEASE: command not found

Amb espais, Bash interpreta RELEASE com una ordre, i = i 3.2.1 com els seus arguments. Si el valor porta espais, cal posar-lo entre cometes: MISSATGE="desplegament ok".

Per promoure una variable ja existent a variable d'entorn n'hi ha prou amb export RELEASE. Per eliminar qualsevol de les dues, unset RELEASE.

Veure'n una en concret:

printenv VARIABLE imprimeix el seu valor i retorna 1 si la variable no és a l'entorn, encara que existeixi com a variable de shell. És la manera més ràpida de comprovar si et falta un export.

  1. L'herència cap als processos fills

Aquest és el concepte que cal deixar tancat, perquè explica una família sencera d'errors. L'herència és unidireccional i en el moment de l'arrencada: el fill rep una còpia de l'entorn del pare, i res del que faci el fill no torna al pare.

operador@srv-tramontana:~$ export ENTORN=produccio
operador@srv-tramontana:~$ bash -c 'echo "el fill veu: $ENTORN"; ENTORN=proves'
el fill veu: produccio
operador@srv-tramontana:~$ echo "el pare continua amb: $ENTORN"
el pare continua amb: produccio

El fill va canviar la seva còpia i va morir amb ella. Per això un script no pot canviar el directori de treball del teu terminal, ni definir-te una variable, tret que l'executis amb source (o el seu sinònim .), que no crea un procés fill: llegeix el fitxer al shell actual.

operador@srv-tramontana:~$ echo 'VERSIO_APP=3.2.1' > /tmp/vars.sh
operador@srv-tramontana:~$ bash /tmp/vars.sh; echo "[$VERSIO_APP]"     # fill: es perd
[]
operador@srv-tramontana:~$ source /tmp/vars.sh; echo "[$VERSIO_APP]"   # shell actual
[3.2.1]

Els parèntesis creen un subshell, que és un fill més i es comporta igual: ( cd /opt/tramontana/app && pwd ) imprimeix /opt/tramontana/app i et deixa on eres. Aquest patró és útil de debò: et desplaces, fas alguna cosa i tornes sol, sense arriscar-te a oblidar un cd -.

  1. PATH: com troba Bash un executable

Al Mòdul 2 va quedar com a noció. Ara en detall. PATH és una llista de directoris separats per :, i Bash la recorre d'esquerra a dreta, aturant-se a la primera coincidència.

operador@srv-tramontana:~$ echo "$PATH"
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
operador@srv-tramontana:~$ type -a python3
python3 is /usr/bin/python3

type -a mostra totes les coincidències en ordre; si hi hagués dues versions instal·lades, veuries quina guanya. A més, Bash guarda a la memòria cau les rutes resoltes: si instal·les un binari que n'emmascara un altre i continua executant-se l'antic, hash -r neteja aquesta memòria cau.

Per afegir /home/operador/scripts, la manera correcta és anteposar o afegir sense destruir el que ja hi havia:

operador@srv-tramontana:~$ export PATH="$HOME/scripts:$PATH"

Fixa't en dos detalls. Primer, es reutilitza $PATH dins del valor nou; escriure PATH=/home/operador/scripts a seques deixa el sistema sense ls ni sudo fins que tanquis la sessió. Segon, va entre cometes.

Posant-lo al davant, els teus scripts tenen prioritat sobre els del sistema, cosa que permet sobreescriure una ordre a propòsit; posant-lo al darrere (PATH="$PATH:$HOME/scripts") el sistema guanya sempre, que és més conservador. A Tramontana fem servir el segon ordre per als comptes administratius.

Per què mai no es posa . al PATH

Incloure el directori actual sembla còmode i és un forat de seguretat clàssic. Imagina que . és el primer i que en Luis, depurant, deixa a /srv/tramontana/backups/temporals/ un fitxer anomenat ls amb contingut maliciós. Hi entres a mirar, escrius ls, i en comptes de l'ordre del sistema executes el fitxer d'aquell directori amb els teus privilegis. Amb sudo pel mig, és una escalada de privilegis completa.

La regla és absoluta: . no va al PATH, ni al principi ni al final. Per executar alguna cosa del directori actual s'escriu ./programa, explícitament. Aquests dos caràcters són la diferència entre una decisió conscient i un accident.

  1. Les altres variables que importen

Variable Què conté Nota pràctica
HOME /home/operador És el que expandeix ~ i on va cd sense arguments
USER / LOGNAME operador Informatives; per a la identitat real fes servir id -un
SHELL /bin/bash És el teu shell de login, no necessàriament el que corre ara
PWD / OLDPWD directori actual i anterior cd - fa servir OLDPWD
LANG / LC_ALL idioma i configuració regional Afecta els missatges, l'ordre i el format de les dates
EDITOR / VISUAL editor per defecte El fan servir crontab -e, git, visudo
TERM tipus de terminal xterm-256color en local, vt100 en consoles antigues
PS1 cadena de l'indicador La personalitzem a l'apartat 10

La configuració regional mereix un paràgraf propi perquè produeix diferències silencioses entre màquines:

operador@srv-tramontana:~$ printf 'casa\nCasa\ncal-ferrer\nCan-Ventos\n' | sort | tr '\n' ' '
cal-ferrer Can-Ventos casa Casa
operador@srv-tramontana:~$ printf 'casa\nCasa\ncal-ferrer\nCan-Ventos\n' | LC_ALL=C sort | tr '\n' ' '
Can-Ventos Casa cal-ferrer casa

Amb ca_ES.UTF-8, sort ignora majúscules i guionets i ordena «com un diccionari». Amb LC_ALL=C ordena per valor de byte, i totes les majúscules van abans que les minúscules. Quan l'ordre ha de ser reproduïble —comparar dos llistats, generar una suma de verificació— fixa LC_ALL=C. Fixa't també que l'assignació va davant de l'ordre, sense export: això defineix la variable només per a aquella execució, sense tocar la teva sessió. És la manera més neta de provar coses.

LC_ALL trepitja totes les altres LC_* i LANG; per això és la que es fa servir per forçar. El mateix efecte apareix a les dates: date et donarà «dl. 18 d'ago» o «Mon Aug 18» segons la configuració regional, i si un script retalla aquesta sortida per posició, es trenca en canviar de màquina.

  1. Expansió de variables i cometes, amb precisió

El Mòdul 2 va donar les cometes per sobre. Aquí va la regla exacta, que és de les tres o quatre coses més rendibles de tot el curs.

Forma S'expandeixen variables Es fa globbing Es parteix per espais
$VAR (sense cometes) Sí Sí, sobre el resultat Sí
"$VAR" Sí No No
'$VAR' No No No

La diferència entre les dues primeres files trenca scripts cada dia:

operador@srv-tramontana:~$ RUTA="/home/operador/dades/informe agost.txt"
operador@srv-tramontana:~$ touch "$RUTA"
operador@srv-tramontana:~$ ls -l $RUTA
ls: cannot access '/home/operador/dades/informe': No such file or directory
ls: cannot access 'agost.txt': No such file or directory
operador@srv-tramontana:~$ ls -l "$RUTA"
-rw-r----- 1 operador operador 0 Aug 18 10:12 '/home/operador/dades/informe agost.txt'

Sense cometes, Bash parteix el valor per l'espai i ls rep dos arguments. La regla d'or: posa sempre "$VAR" entre cometes, tret que vulguis deliberadament que es parteixi en paraules.

Les claus ${VAR} delimiten el nom quan el que ve a continuació es podria confondre amb una part seva. Amb V=3.2.1, echo "release-$V_final" imprimeix release- —Bash busca una variable V_final, que no existeix— mentre que echo "release-${V}_final" imprimeix release-3.2.1_final.

Dues expansions amb valor per defecte que faràs servir constantment:

operador@srv-tramontana:~$ echo "Destí: ${BACKUP_DIR:-/srv/tramontana/backups/temporals}"
Destí: /srv/tramontana/backups/temporals
operador@srv-tramontana:~$ echo "Versió: ${VERSIO:?falta indicar la versió}"
bash: VERSIO: falta indicar la versió
  • ${VAR:-valor} fa servir valor si VAR és buida o no està definida, sense assignar-la. Ideal per a valors per defecte.
  • ${VAR:?missatge} avorta amb aquest missatge si falta. És la forma curta d'exigir un paràmetre obligatori.

També existeix ${VAR:=valor}, que a més assigna, i ${VAR:+valor}, que fa servir valor només si la variable sí que està definida.

  1. Substitució d'ordres

$(ordre) executa l'ordre i substitueix l'expressió per la seva sortida, traient-ne els salts de línia finals.

operador@srv-tramontana:~$ ACTIU="$(readlink /opt/tramontana/app)"
operador@srv-tramontana:~$ echo "Release actiu: $ACTIU, revisat el $(date +%F)"
Release actiu: releases/3.2.1, revisat el 2026-08-18

Ja el feies servir a la convenció de còpies de seguretat cp app.conf app.conf.bak-$(date +%F). Ara saps exactament què hi passa.

Existeix la forma antiga amb accents greus, `ordre`. Fes servir sempre $(...): es pot imbricar sense escapar res i no es confon visualment amb les cometes simples. I posa el resultat entre cometes, "$(...)", per la mateixa raó de l'apartat anterior.

  1. Àlies

Un àlies és una abreviatura que Bash substitueix al principi d'una ordre, abans d'executar-la.

operador@srv-tramontana:~$ alias ll='ls -lh --group-directories-first'
operador@srv-tramontana:~$ alias logs='cd /var/log/tramontana'
operador@srv-tramontana:~$ alias | head -3
alias ll='ls -lh --group-directories-first'
alias logs='cd /var/log/tramontana'
alias ls='ls --color=auto'

S'eliminen amb unalias ll. Per executar l'ordre original saltant-te l'àlies, anteposa una barra invertida:

operador@srv-tramontana:~$ alias rm='rm -i'
operador@srv-tramontana:~$ \rm /tmp/vars.sh

També serveixen command rm i la ruta absoluta /bin/rm. Això importa perquè els àlies són locals a la teva sessió interactiva: no existeixen en un script, ni a cron, ni quan un altre usuari executa el mateix. Un àlies rm='rm -i' et dona una falsa sensació de xarxa de seguretat que desapareix justament en el context on més mal faries.

Els àlies són per a la teva comoditat, no per automatitzar. Si necessites que alguna cosa es comporti igual sempre i a tot arreu, això és un script, i els scripts són el Mòdul 4. A l'apartat 12 veuràs el joc complet que fem servir a Tramontana.

  1. Shells de login, interactius i no interactius

Aquí hi ha l'embolic que cal desfer d'una vegada. Bash llegeix fitxers d'arrencada diferents segons com s'ha llançat.

Tipus Quan apareix Què llegeix
Login interactiu ssh operador@srv-tramontana, consola TTY, su - /etc/profile, després el primer que existeixi de ~/.bash_profile, ~/.bash_login, ~/.profile
No login interactiu obrir una pestanya de terminal, escriure bash /etc/bash.bashrc i ~/.bashrc
No interactiu bash script.sh, cron, ssh servidor 'ordre' Res de l'anterior (només $BASH_ENV si està definida)
flowchart TD
    S["Arrenca bash"] --> L{"És un shell<br/>de login?"}
    L -->|Sí| P["/etc/profile"] --> U["~/.bash_profile<br/>o ~/.bash_login<br/>o ~/.profile<br/>(el primer que existeixi)"]
    U --> R["~/.profile sol incloure:<br/>source ~/.bashrc"]
    R --> B["~/.bashrc"]
    L -->|No| I{"És<br/>interactiu?"}
    I -->|Sí| G["/etc/bash.bashrc"] --> B
    I -->|No| N["No llegeix cap fitxer<br/>d'arrencada"]
    B --> W["Sessió a punt"]
    N --> W
    W --> X{"Surt un shell<br/>de login?"}
    X -->|Sí| O["~/.bash_logout"]

D'aquest diagrama en surten tres conseqüències que resolen gairebé tots els dubtes:

  1. Les variables d'entorn van a ~/.profile; els àlies, les funcions i PS1 van a ~/.bashrc. El primer s'hereta per tots els processos de la sessió; el segon només té sentit quan hi ha algú teclejant.
  2. A Ubuntu, ~/.profile acaba amb un bloc que fa source ~/.bashrc. Per això, en connectar-te per SSH, veus els teus àlies: arriben per aquesta cadena, no perquè el shell de login llegeixi .bashrc per si mateix. Si crees un ~/.bash_profile, ~/.profile deixa de llegir-se i aquesta cadena es trenca.
  3. Cron no llegeix res d'això, i aquesta és la causa arrel de la fallada clàssica que estudiaràs a 03-07.

Per saber en quin tipus de shell ets: shopt -q login_shell && echo login et diu si és de login, i echo "$-" mostra les opcions actives, on la i indica interactiu. I ~/.bash_logout s'executa en tancar un shell de login: serveix, per exemple, per netejar la pantalla d'un TTY físic amb clear.

  1. Personalitzar l'indicador PS1

PS1 és la cadena que Bash imprimeix abans de cada ordre. Admet seqüències d'escapada pròpies:

Seqüència Significat
\u nom d'usuari
\h / \H nom curt / complet de l'amfitrió
\w / \W ruta completa (amb ~) / només l'últim directori
\$ # si ets root, $ si no
\t / \d hora HH:MM:SS / data
\n salt de línia

El color s'afegeix amb seqüències ANSI, i hi ha un detall imprescindible: tot el que no ocupa espai a la pantalla ha d'anar entre \\[ i \\]. Si te'l saltes, Bash calcula malament l'amplada de la línia i l'historial es pinta a sobre de l'indicador en recuperar ordres llargues.

operador@srv-tramontana:~$ PS1='\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]\$ '

En producció fem servir un altre color a propòsit:

operador@srv-tramontana:~$ PS1='\[\e[1;37;41m\] PROD \[\e[0m\] \u@\h:\w\$ '
 PROD  operador@srv-tramontana:~$

Text blanc sobre fons vermell (41). A Tramontana, l'indicador de srv-tramontana va en vermell i el del portàtil en verd, per una raó molt poc estètica: gairebé tots els esborrats catastròfics comencen executant a la finestra equivocada una ordre pensada per a l'altra. Un avís visual permanent i impossible d'ignorar és més eficaç que qualsevol norma escrita. La mateixa lògica s'aplica quan treballes com a root, on el \$ passa a # tot sol.

  1. Historial persistent i el seu risc de seguretat

Bash guarda les ordres a la memòria durant la sessió i les aboca a ~/.bash_history en sortir. Aquest comportament per defecte té dos problemes: si obres diversos terminals, l'últim a tancar-se sobreescriu l'historial dels altres, i si la sessió mor de cop, es perd tot.

Ajust Què fa
HISTSIZE=10000 ordres guardades a la memòria
HISTFILESIZE=20000 línies conservades al fitxer
HISTCONTROL=ignoreboth ignora duplicats consecutius i línies que comencen per espai
HISTIGNORE='ls:ll:pwd:exit:history:clear' no guarda aquestes ordres trivials
HISTTIMEFORMAT='%F %T ' afegeix data i hora a cada entrada
shopt -s histappend afegeix al fitxer en comptes de sobreescriure'l

ignoreboth inclou ignorespace, i això habilita un truc pràctic: si escrius una ordre precedida d'un espai, no es guarda. Amb HISTTIMEFORMAT posat, history 3 et mostra les tres últimes entrades amb la seva marca de temps, cosa que converteix l'historial en un registre de què vas fer i quan.

L'advertiment de seguretat

~/.bash_history és un fitxer de text pla. Tot el que escriguis a la línia d'ordres hi acaba, incloses les credencials:

operador@srv-tramontana:~$ mysql -u tramontana -pSecret123 tramontana_db   # MAI

Aquesta contrasenya queda escrita al teu historial, i a més és visible a la taula de processos per a qualsevol usuari del sistema mentre l'ordre corre (ho veuràs amb ps aux a 03-06). Dos problemes, no un.

Què fer en el seu lloc:

  • Deixa que l'eina la demani de manera interactiva (mysql -u tramontana -p, sense enganxar-la).
  • Guarda-la en un fitxer de credencials amb permisos 600 i passa-li la ruta.
  • Si ja l'has escrita: esborra-la de l'historial en memòria i del fitxer.
operador@srv-tramontana:~$ history -d 512          # esborra l'entrada 512 en memòria
operador@srv-tramontana:~$ history -w              # reescriu ~/.bash_history

I comprova els permisos, que han de ser 600. La gestió seriosa de secrets és la lliçó 06-05; aquí queda't amb la higiene mínima.

  1. El ~/.bashrc recomanat per a operador

Reunint-ho tot, aquest és el bloc que afegim al final de ~/.bashrc als comptes administratius de Tramontana. Aplica la convenció del curs: còpia de seguretat abans d'editar i diff -u després.

operador@srv-tramontana:~$ cp ~/.bashrc ~/.bashrc.bak-$(date +%F)
operador@srv-tramontana:~$ nano ~/.bashrc
# --- Ajustos Tramontana ---------------------------------------------
umask 027                                    # res per a "altres"

HISTSIZE=10000
HISTFILESIZE=20000
HISTCONTROL=ignoreboth
HISTIGNORE='ls:ll:pwd:exit:history:clear'
HISTTIMEFORMAT='%F %T '
shopt -s histappend                          # no sobreescriure entre terminals
shopt -s checkwinsize cdspell

export EDITOR=nano
export PATH="$PATH:$HOME/scripts"            # els nostres scripts, al final

alias ll='ls -lh --group-directories-first'
alias la='ls -lha'
alias grep='grep --color=auto'
alias df='df -h'
alias logs='cd /var/log/tramontana'
alias rel='ls -l /opt/tramontana/releases/'

PS1='\[\e[1;37;41m\] PROD \[\e[0m\] \[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]\$ '
# --------------------------------------------------------------------

S'activa sense tancar la sessió amb source ~/.bashrc, i es verifica el canvi amb diff -u ~/.bashrc.bak-2026-08-18 ~/.bashrc. Fixa't que umask 027 va aquí i no a ~/.profile perquè ens interessa per al treball interactiu; per als serveis es fixa en un altre lloc, i això és matèria de 05-05.

Errors Comuns i Consells

  • Espais al voltant del =. VAR = valor no és una assignació. És l'error número u.
  • Editar ~/.bashrc i esperar que s'apliqui sol. Cal fer source ~/.bashrc o obrir una sessió nova.
  • Trencar el PATH. Abans de tocar-lo, guarda'n una còpia: PATH_ORIG="$PATH". Si et quedes sense ordres, export PATH="$PATH_ORIG" et salva sense reconnectar.
  • Posar variables d'entorn a ~/.bashrc. Funciona en interactiu i desapareix en contextos no interactius. El seu lloc és ~/.profile.
  • Crear ~/.bash_profile sense saber que anul·la ~/.profile. Si el crees, inclou-hi a dins [ -f ~/.profile ] && . ~/.profile.
  • Confiar en alias rm='rm -i'. No existeix en scripts ni a cron. La xarxa de seguretat de debò és mirar amb ls abans de l'rm.
  • Oblidar \\[ \\] als colors del PS1. El símptoma és un indicador que es corromp en navegar per l'historial.
  • Consell: env -i ordre arrenca un programa sense cap variable. És la millor manera de reproduir el que veu cron, i hi tornaràs a 03-07. I com que set sense arguments llista variables i funcions, filtra sempre: set | grep '^HIST'.

Exercicis

Exercici 1. Demostra a la teva VM la diferència entre variable de shell i variable d'entorn. Defineix DESPLEGAMENT=3.3.0 sense exportar, comprova que un bash -c fill no la veu, exporta-la, comprova que ara sí, i explica per què printenv donava error abans.

Exercici 2. Afegeix /home/operador/scripts al PATH de manera que no tingui prioritat sobre les ordres del sistema i que el canvi sobrevisqui a reconnectar per SSH. Verifica que funciona sense tancar la sessió i explica en quin fitxer ho has posat i per què.

Exercici 3. Genera una llista ordenada i reproduïble de les cases de /home/operador/dades/cases.txt que sigui idèntica al teu portàtil i al servidor, encara que tinguin configuracions regionals diferents. Guarda el resultat a /home/operador/dades/cases-ordenades.txt i justifica la decisió.

Solucions

Solució 1.

operador@srv-tramontana:~$ DESPLEGAMENT=3.3.0
operador@srv-tramontana:~$ echo "$DESPLEGAMENT"
3.3.0
operador@srv-tramontana:~$ printenv DESPLEGAMENT; echo "codi: $?"
codi: 1
operador@srv-tramontana:~$ bash -c 'echo "el fill veu: [$DESPLEGAMENT]"'
el fill veu: []
operador@srv-tramontana:~$ export DESPLEGAMENT
operador@srv-tramontana:~$ bash -c 'echo "el fill veu: [$DESPLEGAMENT]"'
el fill veu: [3.3.0]

La variable existia des del principi al shell actual —per això echo la mostrava—, però no era al vector d'entorn que es copia als fills. printenv consulta exactament aquest vector, així que no la va trobar i va retornar 1. export no crea la variable: la marca per a exportació, i a partir d'aquí tots els fills nous la reben. Els que ja estiguessin corrent, no: l'herència passa en el moment del fork.

Solució 2. Ha d'anar a ~/.profile, perquè és una variable d'entorn i perquè ~/.profile es llegeix als shells de login, que és el que obre SSH. Al final, i no al principi, perquè el sistema tingui prioritat.

operador@srv-tramontana:~$ cp ~/.profile ~/.profile.bak-$(date +%F)
operador@srv-tramontana:~$ printf '\n# scripts propis de Tramontana\nexport PATH="$PATH:$HOME/scripts"\n' >> ~/.profile
operador@srv-tramontana:~$ diff -u ~/.profile.bak-$(date +%F) ~/.profile
@@ -20,3 +20,6 @@
     fi
 fi
+
+# scripts propis de Tramontana
+export PATH="$PATH:$HOME/scripts"
operador@srv-tramontana:~$ source ~/.profile
operador@srv-tramontana:~$ echo "$PATH" | tr ':' '\n' | tail -2
/usr/games
/home/operador/scripts

source aplica el canvi a la sessió actual sense reconnectar. Si en comptes d'això haguéssim escrit PATH="$HOME/scripts:$PATH", un script nostre anomenat df o tar emmascararia el del sistema, i aquest tipus de sorpresa és difícil de diagnosticar. Posar-lo al final és l'opció conservadora. Fixa't també que les cometes simples del printf impedeixen que $PATH i $HOME s'expandeixin en escriure el fitxer: volem que l'expansió passi a cada login, no ara.

Solució 3.

operador@srv-tramontana:~$ LC_ALL=C sort /home/operador/dades/cases.txt \
    > /home/operador/dades/cases-ordenades.txt
operador@srv-tramontana:~$ cat /home/operador/dades/cases-ordenades.txt
cal-ferrer
can-ventos
el-moli
la-solana
mas-figueres

La justificació és la clau de l'exercici: sense LC_ALL=C, el resultat depèn de la configuració regional de cada màquina. Amb ca_ES.UTF-8 el guionet s'ignora en la comparació i mas-figueres podria quedar en una altra posició respecte d'un hipotètic masfigueres; amb C es compara byte a byte i el resultat és idèntic a qualsevol sistema. Quan la sortida s'ha de comparar, versionar o alimentar un altre procés, la reproduïbilitat pesa més que la correcció ortogràfica de l'ordre. I es posa davant de l'ordre, sense export, per no alterar la resta de la sessió.

Conclusió

Has passat d'habitar l'entorn del shell a dissenyar-lo.

  • L'entorn és un vector CLAU=valor que el nucli lliura a cada procés; el llegeixes a /proc/<pid>/environ.
  • Distingeixes variable de shell de variable d'entorn: export marca la diferència, i l'herència és unidireccional i en el moment de l'arrencada, per això un script no et pot canviar el cd tret que sigui amb source.
  • Entens com busca Bash un executable recorrent el PATH d'esquerra a dreta, com ampliar-lo sense destruir-lo, i per què . mai no va al PATH.
  • Coneixes l'efecte real de la configuració regional sobre sort i les dates, i fas servir LC_ALL=C quan necessites resultats reproduïbles.
  • Manegues amb precisió "$VAR" enfront de $VAR i '$VAR', les claus ${VAR}, els valors per defecte ${VAR:-...} i ${VAR:?...}, i la substitució d'ordres $(...).
  • Tens desembolicat l'embolic dels fitxers d'arrencada: entorn a ~/.profile, àlies i PS1 a ~/.bashrc, i res d'això en un shell no interactiu.
  • Has posat l'indicador de producció en vermell per una raó operativa i has configurat un historial persistent amb histappend, sabent que les contrasenyes escrites a la línia acaben en un fitxer de text pla.

La lliçó següent fa el pas natural. Ja saps descriure un fitxer amb la seva ruta; ara aprendràs a descriure conjunts de fitxers i patrons de text. A Ús de Comodins i Expressions Regulars veuràs la distinció que gairebé ningú explica bé —que el globbing el fa el shell abans d'executar l'ordre, mentre que les expressions regulars les interpreta el programa que les rep—, i aquesta distinció, que ara entendràs perquè ja saps com processa Bash una línia abans de llançar-la, és la que evita que grep *.log faci una cosa completament diferent del que esperaves.

Curs de Linux: De Principiant a Administrador de Sistemes

Mòdul 1: Introducció a Linux

Mòdul 2: Comandes Bàsiques de Linux

Mòdul 3: Habilitats Avançades en la Línia de Comandes

Mòdul 4: Scripting en Shell

Mòdul 5: Administració del Sistema

Mòdul 6: Xarxes i Seguretat

Mòdul 7: Temes Avançats

Mòdul 8: Projectes Pràctics

© Copyright 2026. Tots els drets reservats