A la lliçó 02-02 ja vas fer servir | de manera intuïtiva per encadenar cut, sort i uniq. Ara entendràs què passa per sota i dominaràs l'altra meitat del mecanisme: la redirecció. Aquesta és la lliçó que converteix una col·lecció d'ordres soltes en un llenguatge de composició. I és també la que necessites perquè veloz-ops deixi d'imprimir a la pantalla i comenci a generar fitxers d'informe i registres d'execució, que és el que s'espera d'una eina d'operacions seriosa.
Contingut
- Els tres fluxos estàndard
- Redirigir la sortida:
>i>> - El risc de truncament i
noclobber - Redirigir l'entrada:
< - Redirigir els errors:
2>,2>&1i&> /dev/null: el forat negre del sistematee: veure i desar alhora- Canonades i el concepte de filtre
- Cadenes de filtres que responen preguntes reals
- El codi de sortida d'una canonada
- Els tres fluxos estàndard
Tot procés a Linux neix amb tres canals de comunicació oberts, identificats per un número anomenat descriptor de fitxer:
| Nom | Descriptor | Direcció | Destí per defecte |
|---|---|---|---|
| stdin (entrada estàndard) | 0 |
Entra | El teclat |
| stdout (sortida estàndard) | 1 |
Surt | La pantalla |
| stderr (sortida d'error) | 2 |
Surt | La pantalla |
flowchart LR
T[Teclat o fitxer] -->|stdin 0| C[ordre]
C -->|stdout 1| S[Pantalla, fitxer o ordre següent]
C -->|stderr 2| E[Pantalla o fitxer d'errors]
La pregunta òbvia és per què hi ha dues sortides si totes dues acaben a la pantalla. La resposta és la clau de tot el disseny: per poder separar-les quan cal. Una ordre escriu els seus resultats a stdout i les seves queixes a stderr, de manera que pots desar els resultats en un fitxer mentre els errors continuen apareixent al teu terminal, o just al revés. Comprova-ho:
Si executes ls /srv/veloz/dades /srv/veloz/inexistent, hi veuràs barrejats el llistat del primer directori i el missatge El fitxer o directori no existeix del segon. Semblen un sol bloc, però són fluxos diferents: així que en redirigeixis un, se separaran.
- Redirigir la sortida:
> i >>
> i >>L'operador > envia stdout a un fitxer en lloc de a la pantalla:
No veuràs res per pantalla: la sortida és al fitxer. > crea el fitxer si no existeix i el buida completament si existeix. Aquest buidatge és total i instantani, i passa abans que l'ordre comenci a executar-se.
L'operador >> afegeix al final sense esborrar el que hi havia:
date >> ~/veloz-ops/logs/execucions.log
echo "informe diari completat" >> ~/veloz-ops/logs/execucions.logRegla pràctica: > per a resultats que es regeneren, >> per a registres que s'acumulen. Un informe diari que es refà cada matí fa servir >; el registre d'execucions del toolkit fa servir >>.
- El risc de truncament i
noclobber
noclobberEl perill de > és que no avisa. Escriure per error > enviaments.csv en lloc de >> enviaments.csv destrueix el fitxer de dades a l'acte, sense confirmació i sense recuperació possible. És l'equivalent silenciós del rm de la lliçó 02-01.
Bash ofereix una xarxa de seguretat, l'opció noclobber:
Amb noclobber actiu, > es nega a sobreescriure un fitxer que ja existeix. Quan sí que ho vulguis fer, s'utilitza l'operador >|, que força el truncament:
echo "prova" >| ~/veloz-ops/logs/ciutats.txt # sobreescriu tot i noclobber
set +o noclobber # desactivar l'opcióFixa't en l'asimetria de set: -o activa una opció i +o la desactiva, al revés del que suggereix la intuïció. Activar noclobber al teu ~/.bashrc és una bona idea per a sessions interactives; en scripts es prefereix no dependre'n i escriure les redireccions amb cura.
- Redirigir l'entrada:
<
<L'operador < fa que una ordre llegeixi d'un fitxer en lloc del teclat:
Compara-ho amb wc -l /srv/veloz/dades/enviaments.csv, que imprimeix també el nom del fitxer. La diferència és conceptual: en el primer cas wc no sap que hi ha un fitxer, només rep un flux de dades per stdin; en el segon, obre el fitxer ell mateix. Aquesta distinció importarà quan escriguis bucles que llegeixen fitxers línia a línia al Mòdul 4.
Hi ha ordres que només accepten entrada estàndard, com tr, que no admet un fitxer com a argument: tr 'a-z' 'A-Z' < enviaments.csv és l'única manera d'alimentar-lo des d'un fitxer.
- Redirigir els errors:
2>, 2>&1 i &>
2>, 2>&1 i &>Com que stderr és el descriptor 2, es redirigeix anteposant aquest número a l'operador:
Ara sortida.txt conté només el llistat correcte i errors.txt només el missatge d'error. 2>> afegeix en lloc de truncar, exactament igual que >>.
Per combinar tots dos fluxos al mateix destí es fa servir 2>&1, que es llegeix «envia el descriptor 2 allà on apunta ara mateix el descriptor 1»:
L'ordre importa, i és el parany clàssic. Bash processa les redireccions d'esquerra a dreta:
| Escriptura | Què passa |
|---|---|
cmd > fitx 2>&1 |
1 va al fitxer; després 2 s'apunta on és 1 → tots dos al fitxer ✔ |
cmd 2>&1 > fitx |
2 s'apunta on és 1, que encara és la pantalla; després 1 va al fitxer → stdout al fitxer, stderr a la pantalla ✘ |
La segona forma és un error real i freqüent: sembla que ho desa tot i en realitat els errors es perden al terminal. Recorda que 2>&1 copia el destí actual d'1, no crea un vincle permanent.
Bash ofereix a més una forma abreujada pròpia, més llegible i sense risc d'ordre:
./informe-diari.sh &> ~/veloz-ops/logs/informe.log # tot al fitxer (trunca)
./informe-diari.sh &>> ~/veloz-ops/logs/informe.log # tot al fitxer (afegeix)&> no és POSIX: funciona a Bash però no a sh pur. Per a scripts portables es fa servir > fitx 2>&1 (portabilitat, 08-07).
/dev/null: el forat negre del sistema
/dev/null: el forat negre del sistema/dev/null és un fitxer especial que descarta tot el que s'hi escriu i retorna buit en llegir-lo. Serveix per silenciar sortides que no interessen:
grep -q ERROR /var/log/veloz/app.log 2>/dev/null # silenciar només els errors
ordre >/dev/null 2>&1 # silenciar absolutament totEl segon patró és un dels més freqüents en scripting. Es fa servir quan només t'importa el codi de sortida de l'ordre, no la seva sortida, típicament en una comprovació:
Aquí no volem veure les estadístiques de ping, només saber si ha respost. Ara bé, silenciar errors és perillós: 2>/dev/null en un script de producció pot amagar exactament la fallada que necessitaves veure. Fes-ho servir només quan l'error sigui esperat i estigui previst.
tee: veure i desar alhora
tee: veure i desar alhoratee (per la «T» de lampisteria) duplica el flux: l'escriu en un fitxer i el deixa continuar per stdout.
En una sola línia has desat els errors complets en un fitxer i has vist el recompte a la pantalla. Amb -a (append) afegeix en lloc de truncar, i combinat amb sudo resol un problema clàssic:
Això funciona on sudo echo ... >> /etc/... falla, i entendre'n el motiu és important: la redirecció la fa el teu shell, no sudo, així que és el teu usuari sense privilegis qui intenta obrir el fitxer del sistema. Amb tee, el procés privilegiat és el que escriu.
- Canonades i el concepte de filtre
Una canonada | connecta el stdout de l'ordre de l'esquerra amb el stdin de la de la dreta. Els dos processos s'executen alhora, no un darrere l'altre: les dades flueixen a mesura que es produeixen, sense fitxers intermedis i sense carregar res sencer a memòria.
Un filtre és un programa que llegeix de stdin, transforma i escriu a stdout. grep, cut, sort, uniq, tr, wc, head, tail i column són tots filtres, i per això encaixen entre ells en qualsevol ordre.
Dos advertiments:
- La canonada només transporta stdout. Els errors de la primera ordre van a la pantalla, no a la segona. Si els hi vols passar també,
cmd 2>&1 | filtre(Bash té la drecera|&). - Evita l'«ús inútil de
cat». L'exemple anterior s'escriu millor comgrep -c ERROR /var/log/veloz/app.log: un procés en lloc de tres.catal capdavant d'una canonada només es justifica quan concatenes diversos fitxers.
- Cadenes de filtres que responen preguntes reals
Aquí és on tot el mòdul convergeix. Les 10 IP que més peticions han fet avui a veloz-api:
cut -d' ' -f1 /var/log/veloz/acces.log | sort | uniq -c | sort -rn | head -10 \
| tee ~/veloz-ops/logs/top-ips.txtPas a pas: cut -d' ' -f1 extreu el primer camp separat per espais, que en el format combinat és la IP → sort agrupa les iguals → uniq -c compta cada grup → sort -rn ordena per número descendent → head -10 es queda amb el podi → tee el desa a més al toolkit.
Ciutats amb més incidències, desant el resultat com a informe:
grep ',incidencia,' /srv/veloz/dades/enviaments.csv \
| cut -d, -f3 | sort | uniq -c | sort -rn \
| column -t > ~/veloz-ops/logs/incidencies-per-ciutat.txtI per comptar i registrar alhora les peticions que van retornar error 500: grep ' 500 ' /var/log/veloz/acces.log | tee ~/veloz-ops/logs/errors-500.log | wc -l.
Fixa't en el patró general que es repeteix: filtrar → extreure → agrupar → comptar → ordenar → presentar → desar. Gairebé qualsevol pregunta sobre dades tabulars o registres es respon amb alguna variant d'aquesta seqüència, i aquest és exactament l'esquelet de l'analitzador de registres que construiràs al projecte 09-02.
- El codi de sortida d'una canonada
A la lliçó 01-04 vas veure $?, el codi de sortida de l'última ordre. En una canonada, $? és el de l'última ordre de la cadena, no el de tota ella:
grep no ha trobat res i ha retornat 1, però wc -l s'ha executat correctament i ha retornat 0, així que $? val 0. La canonada sembla haver tingut èxit quan en realitat el filtre ha fallat. És una font inesgotable de bugs silenciosos en scripts.
Bash ofereix dues solucions: l'array PIPESTATUS, que desa el codi de cada ordre de la canonada, i l'opció set -o pipefail, que fa que la canonada retorni el codi de la primera ordre que ha fallat. Totes dues pertanyen a la gestió d'errors i les estudiaràs a fons a 05-03; de moment, queda't amb l'advertiment.
Un últim apunt: quan necessitis passar la sortida d'una ordre com a arguments d'una altra (no com a entrada estàndard), la canonada no serveix i cal xargs, que veuràs a 05-01. I les formes avançades d'E/S —here-documents i descriptors propis— arriben a 05-05.
Errors Habituals i Consells
- Escriure
> fitxervolent>>. Destrueix el contingut a l'instant. Consideraset -o noclobberal teu~/.bashrc. - Posar
2>&1abans de>. Els errors es queden a la pantalla. La forma correcta éscmd > fitx 2>&1, o directamentcmd &> fitx. - Creure que la canonada transporta els errors. Només porta stdout; fes servir
2>&1 |si els necessites. - Fer servir
sudo cmd > /etc/fitxer. La redirecció la fa el teu shell sense privilegis i falla. Fes servirsudo tee. - Abusar de
2>/dev/null. Amaga el diagnòstic que anaves a necessitar. Silencia només errors esperats. - Confiar en
$?després d'una canonada. Només reflecteix l'última ordre (vegeu 05-03). - Llegir i escriure el mateix fitxer en una ordre.
sort fitxer > fitxerdeixa el fitxer buit, perquè>el trunca abans quesortel llegeixi. Escriu en un temporal i després reanomena ambmv.
Exercicis
Exercici 1 — Separar resultats i errors. Executa un ls sobre /srv/veloz/dades i sobre un directori inexistent, de manera que el llistat correcte quedi a ~/veloz-ops/logs/llistat.txt i el missatge d'error a ~/veloz-ops/logs/llistat.err. Després escriu la variant que ho desi tot junt en un sol fitxer, de les dues maneres possibles.
Exercici 2 — Informe de repartidors amb incidències. Sobre enviaments.csv, genera un fitxer ~/veloz-ops/logs/incidencies-repartidor.txt amb el nombre d'incidències de cada repartidor, ordenat de més a menys i presentat en taula alineada. Has de veure el resultat a la pantalla al mateix temps que es desa.
Exercici 3 — Depurar una ordre que falla en silenci. Un company té aquesta línia en un script i es queixa que «mai no detecta res»:
grep TIMEOUT /var/log/veloz/app.log | wc -l > /dev/null 2>&1
if [ $? -eq 0 ]; then echo "hi ha timeouts"; fiExplica les dues fallades i reescriu-la correctament.
Solucions
Solució a l'Exercici 1
# Separats
ls /srv/veloz/dades /srv/veloz/inexistent \
> ~/veloz-ops/logs/llistat.txt 2> ~/veloz-ops/logs/llistat.err
# Tot junt, forma POSIX (portable)
ls /srv/veloz/dades /srv/veloz/inexistent > ~/veloz-ops/logs/tot.log 2>&1
# Tot junt, forma abreujada de Bash
ls /srv/veloz/dades /srv/veloz/inexistent &> ~/veloz-ops/logs/tot.logLes dues últimes són equivalents a Bash. La primera és preferible en scripts que es puguin executar amb sh; la segona és més llegible i elimina el risc d'invertir l'ordre. El que no has d'escriure és ls ... 2>&1 > ~/veloz-ops/logs/tot.log, que deixaria els errors a la pantalla.
Solució a l'Exercici 2
grep ',incidencia,' /srv/veloz/dades/enviaments.csv \
| cut -d, -f4 | sort | uniq -c | sort -rn | column -t \
| tee ~/veloz-ops/logs/incidencies-repartidor.txtgrep ',incidencia,' filtra pel camp d'estat amb les comes al davant i al darrere, cosa que evita falsos positius si aquesta paraula aparegués en un altre camp; cut -d, -f4 extreu el repartidor; sort | uniq -c agrupa i compta; sort -rn ordena descendentment; column -t alinea; i tee al final —en lloc de >— és el que permet veure el resultat i desar-lo simultàniament.
Solució a l'Exercici 3
Les dues fallades:
$?recull el codi dewc, no el degrep.wc -lsempre acaba amb èxit, així que la condició es compleix sempre, hi hagi timeouts o no. L'script diria «hi ha timeouts» fins i tot amb el registre buit.- La redirecció
> /dev/null 2>&1no aporta res d'útil aquí i de passada descarta el número quewchavia calculat, amb la qual cosa la informació es perd del tot.
Versió correcta, fent servir el codi de sortida de grep (0 si ha trobat alguna cosa, 1 si no):
grep -q (quiet) no imprimeix res i retorna només el codi de sortida, així que substitueix alhora el wc i la redirecció a /dev/null. Si a més necessites el recompte:
La sintaxi $(...) és substitució d'ordres i les condicionals amb if es formalitzen al Mòdul 3; aquí el que importa és el diagnòstic: avalua el codi de sortida de l'ordre que de debò decideix, mai el de l'últim graó d'una canonada.
Conclusió
Has entès el mecanisme que fa de Bash un llenguatge de composició. Saps que tot procés té tres fluxos —stdin, stdout i stderr—, i per què separar-los és útil; redirigeixes sortides amb > i >> coneixent el risc del truncament i la protecció de noclobber; redirigeixes l'entrada amb <; controles els errors amb 2>, 2>&1 i &>, sabent que l'ordre importa; silencies soroll amb /dev/null amb la deguda prudència; dupliques fluxos amb tee, fins i tot per escriure en fitxers del sistema amb sudo; i encadenes filtres amb | per respondre preguntes que cap ordre no resol tota sola. També saps que $? després d'una canonada només reflecteix l'últim graó, un detall que resoldràs definitivament a 05-03.
Amb això, les ordres del Mòdul 2 deixen de ser peces soltes: són components de línies de muntatge que ja produeixen informes reals a ~/veloz-ops/logs.
A la lliçó 02-05 tanquem el cercle de la manipulació de fitxers amb els comodins. Descobriràs que *.csv no l'interpreta ls ni rm, sinó el mateix shell abans d'executar-los, i que aquesta diferència explica des de l'error rm * més famós de la història fins a per què un bucle falla quan un patró no casa amb res.
Curs de Programació en Bash
Mòdul 1: Introducció a Bash
- Què és Bash?
- Configurar el teu Entorn
- Navegació Bàsica per la Línia d'Ordres
- Entendre el Shell
- Trobar Ajuda: man, help i --help
Mòdul 2: Ordres Bàsiques de Bash
- Operacions amb Fitxers i Directoris
- Ordres de Processament de Text
- Permisos i Propietat dels Fitxers
- Redirecció i Canonades
- Comodins i Expansió de Rutes
- Historial i Dreceres de Teclat
Mòdul 3: Fonaments de Scripting
- Crear i Executar un Script
- Variables i Constants
- Operadors Bàsics
- Sentències Condicionals
- Arguments i Entrada de l'Usuari
- Cometes, Expansió i Substitució
Mòdul 4: Scripting Intermedi
- Bucles en Bash
- Funcions en Bash
- Arrays i Arrays Associatius
- Manipulació de Cadenes
- La Sentència case i els Menús Interactius
- Aritmètica i Càlculs Numèrics
Mòdul 5: Tècniques Avançades de Scripting
- Operacions Avançades amb Fitxers
- Gestió de Processos
- Gestió d'Errors i Depuració
- Expressions Regulars
- Entrada/Sortida Avançada: Descriptors i Here-Documents
- Scripts Modulars i Llibreries Reutilitzables
Mòdul 6: Treballar amb Eines Externes
Mòdul 7: Automatització i Programació
- Tasques Cron
- Automatitzar Tasques
- Scripts de Còpia i Restauració
- Monitoratge i Registre
- Serveis i Temporitzadors amb systemd
- Automatització Remota amb SSH
Mòdul 8: Bones Pràctiques i Optimització
- Escriure Codi Llegible
- Optimitzar Scripts en Bash
- Consideracions de Seguretat
- Control de Versions amb Git
- Anàlisi Estàtica amb ShellCheck i shfmt
- Proves Automatitzades amb Bats
- Portabilitat: POSIX sh enfront de Bashismes
